@preline/overlay
v4.2.0
Published
Preline UI is an open-source set of prebuilt UI components based on the utility-first Tailwind CSS framework.
Readme
Overlay
The Overlay Tailwind plugin is an unstyled JavaScript utility for creating modals, drawers, and more, enhancing user interaction and content layout.
Contents
Overview
The Overlay component provides a flexible foundation for creating modals, drawers, sidebars, and other overlay-based UI elements. It handles backdrop management, focus trapping, keyboard navigation, and z-index stacking automatically.
Key Features:
- Modal and drawer support
- Automatic backdrop management
- Focus trapping and keyboard navigation (Tab, Esc)
- Z-index stacking for multiple overlays
- Responsive breakpoint support
- Auto-close on screen size changes
- Programmatic control via JavaScript API
- Event system for lifecycle hooks
- Accessibility attributes (ARIA) built-in
Installation
To get started, install Overlay plugin via npm, else you can skip this step if you are already using Preline UI as a package.
npm i @preline/overlayCSS
Use @source to register the plugin's JavaScript path for Tailwind CSS scanning, then @import the plugin's CSS files into your Tailwind CSS file.
@import "tailwindcss";
/* @preline/overlay */
@source "../node_modules/@preline/overlay/*.js";
@import "./node_modules/@preline/overlay/variants.css";
@import "./node_modules/@preline/overlay/theme.css";JavaScript
Include the JavaScript that powers the interactive elements near the end of your </body> tag:
<script src="./node_modules/@preline/overlay/index.js"></script>Manual Initialization
Use the non-auto entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.
<script type="module">
import HSOverlay from "@preline/overlay/non-auto.mjs";
new HSOverlay(document.querySelector("#overlay"));
</script>Via Bundler
When using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.
@preline/overlay is the auto-init entry: it scans the DOM and initializes matching elements automatically.
import "@preline/overlay";@preline/overlay/non-auto is the manual entry: use it when you want explicit control over when initialization happens, either via autoInit() or by creating a specific instance yourself.
import HSOverlay from "@preline/overlay/non-auto";
HSOverlay.autoInit();
// Or initialize a specific element manually
const el = document.querySelector("#overlay");
if (el) new HSOverlay(el);TypeScript
This package ships with TypeScript type definitions. No additional @types/ package is needed.
Basic usage
The following example demonstrates the minimal HTML structure required for an overlay component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The overlay appears when the button is clicked.
<button type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="hs-unstyled-modal" data-hs-overlay="#hs-unstyled-modal">
Open modal
</button>
<div id="hs-unstyled-modal" class="hs-overlay hidden size-full fixed top-0 start-0 z-60 overflow-x-hidden overflow-y-auto pointer-events-none" role="dialog" tabindex="-1" aria-labelledby="hs-unstyled-modal-label">
<div class="hs-overlay-open:opacity-100 hs-overlay-open:duration-500 opacity-0 transition-all sm:max-w-lg sm:w-full m-3 sm:mx-auto">
<div class="pointer-events-auto">
<h3 id="hs-unstyled-modal-label">Title</h3>
Modal content
</div>
</div>
</div>Structure Requirements:
data-hs-overlay: Required on the trigger button, must be a valid CSS selector pointing to the overlay elemenths-overlay: Required class on the overlay content containerhiddenclass on the overlay initially- Proper ARIA attributes (
aria-haspopup,aria-expanded,aria-controls,role="dialog",aria-labelledby)
Initial State:
- Set
aria-expanded="false"on the trigger button initially - Add
hiddenclass to the overlay initially - Overlay visibility is controlled by the plugin
Configuration Options
Data Attributes
Data attributes are added directly to HTML elements to configure overlay behavior.
| Attribute | Target Element | Type | Default | Description |
| --- | --- | --- | --- | --- |
| data-hs-overlay-options | Trigger button | object (JSON) | - | Defines the modal configuration. Should be added to the button (trigger). |
| :hiddenClass | Inside data-hs-overlay-options | string | "hidden" | Defines which classes will be added/removed when modal toggles. |
| :emulateScrollbarSpace | Inside data-hs-overlay-options | boolean | false | If true, then adds right padding to the body equal to the size of the scrollbar. Useful to prevent layout shift when overlay opens. |
| :isClosePrev | Inside data-hs-overlay-options | boolean | true | Determines whether the previous open modal will be closed when the next one is opened. |
| :backdropClasses | Inside data-hs-overlay-options | string | "hs-overlay-backdrop transition duration fixed inset-0 bg-gray-900/50 dark:bg-neutral-900/80" | Defines default backdrop classes. |
| :backdropExtraClasses | Inside data-hs-overlay-options | string | - | Allows to add additional classes besides those specified in backdropClasses. |
| :backdropParent | Inside data-hs-overlay-options | string | - | Allows to specify the selector of the element where the backdrop will be generated. |
| :moveOverlayToBody | Inside data-hs-overlay-options | number | null | null | Moves the overlay to the body tag if the screen width is less than the value specified. |
| :isToggleClassesImmediately | Inside data-hs-overlay-options | boolean | false | When set to true, toggles hidden, open, and opened classes immediately instead of waiting for the open delay, resize debounce, or close transition. |
| data-hs-overlay-backdrop-container | Overlay content | string (CSS selector) | null | Backdrop element selector. Should be added to the overlay (content). |
| data-hs-overlay-keyboard | Overlay content | boolean | true | When set to false, the modal will not close when pressing the ESC button on the keyboard. Should be added to the overlay (content). |
CSS Classes (Modifiers)
CSS class modifiers use Tailwind-style syntax with -- prefix to control overlay behavior.
| Class Modifier | Target Element | Values | Default | Description |
| --- | --- | --- | --- | --- |
| [--overlay-backdrop:*] | Overlay content | "static" | null | null | When backdrop is set to "static", the modal will not close when clicking outside it. Should be added to the overlay (content). |
| [--auto-hide:*] | Overlay content | number | 0 | Milliseconds for auto-closing a modal. When set to 0, the modal will not close automatically. Should be added to the overlay (content). |
| [--auto-close:*] | Overlay content | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number | null | Screen resolution at which the overlay will close automatically. Should be added to the overlay (content). |
| [--auto-close-equality-type:*] | Overlay content | 'less-than' | null | null | This option determines the condition under which an overlay should automatically close based on the window's width. When set to less-than, the overlay will close if the window's width is less than or equal to the value defined in [--auto-close:*]. Otherwise the overlay will close if the window's width is greater than or equal to the [--auto-close:*] threshold. Should be added to the overlay (content). |
| [--opened:*] | Overlay content | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number | null | Screen resolution at which the plugin will apply functionality for the initially opened overlay. Should be added to the overlay (content). |
| [--body-scroll:*] | Overlay content | boolean | true | When set to false, the body scroll will be hidden as soon as the modal opens. Should be added to the overlay (content). |
| [--tab-accessibility-limited:*] | Overlay content | "true" | "false" | "true" | Restricts focus to elements within an overlay (or modal). Should be added to the overlay (content). |
| [--has-autofocus:*] | Overlay content | "true" | "false" | "true" | Disables autofocus on the first focusable element when opening an overlay. Should be added to the overlay (content). |
| [--has-dynamic-z-index:*] | Overlay content | "true" | "false" | "false" | When set to "true", each newly opened element in the group will receive an incremented z-index inline, ensuring it appears above previously opened elements with the same base z-index. Should be added to the overlay (content). |
| [--is-layout-affect:*] | Overlay content | "true" | "false" | "false" | Informs the plugin that the instance affects the website layout. Should be added to the overlay (content). |
| [--toggle-classes-immediately:*] | Overlay content | "true" | "false" | "false" | When set to "true", toggles hidden, open, and opened classes immediately instead of waiting for the open delay, resize debounce, or close transition. |
Example:
<div class="hs-overlay --overlay-backdrop:static --auto-hide:5000">
<!-- Overlay content -->
</div>Required CSS Classes
These classes define the structure and must be present for the overlay to function.
| Class | Required On | Purpose |
| --- | --- | --- |
| hs-overlay | Overlay content container | Identifies the overlay content element |
Optional CSS Classes
| Class | Required On | Purpose |
| --- | --- | --- |
| hs-overlay-animation-target | Element inside overlay | Defines an element inside the popup, after the end of whose animation the popup will be completely hidden. Has no parameters. |
| autofocus | Input element inside overlay | Focus the first input in a modal with the autofocus attribute on opening. Must be added to an input element inside a modal window. |
Tailwind Modifiers
| Name | Description |
| --- | --- |
| hs-overlay-open:* | Defines classes that will be applied to any elements inside the hs-overlay when the overlay is open. |
| hs-overlay-backdrop-open:* | Defines classes that will be applied to backdrop when the overlay is open. |
| hs-overlay-layout-open:* | Defines classes that will be applied to any elements inside the body tag when the overlay is open. |
JavaScript API
The HSOverlay object is available in the global window object after the plugin is loaded.
Instance Methods
These methods are called on an overlay instance.
| Method | Parameters | Return Type | Description |
| --- | --- | --- | --- |
| open(cb) | cb: Function \| null (optional) | Promise<void> | Forces the overlay to open programmatically. Optionally accepts a callback function that will be called after opening. Returns a Promise. |
| close(forceClose, cb) | forceClose: boolean (optional)cb: Function \| null (optional) | Promise<unknown> | Forces the overlay to close programmatically. forceClose bypasses animations and closes immediately. Optionally accepts a callback function. Returns a Promise. |
| minify(isMinified, cb) | isMinified: booleancb: Function \| null (optional) | void | Minimizes or restores the overlay. Useful for drawer/sidebar components. |
| updateToggles() | None | void | Updates the toggle buttons. Useful after dynamically adding or removing toggle buttons. |
| destroy() | None | void | Destroys the overlay instance, removes all generated markup, classes, and event listeners. Use when removing overlay from DOM. |
Static Methods
These methods are called directly on the HSOverlay class.
| Method | Parameters | Return Type | Description |
| --- | --- | --- | --- |
| HSOverlay.getInstance(target, isInstance) | target: HTMLElement \| string (CSS selector)isInstance: boolean (optional) | HTMLElement \| { id: string \| number, element: HSOverlay } \| null | Returns the overlay instance or element associated with the target. If isInstance is true, returns collection item object { id, element } where element is the HSOverlay instance. If isInstance is false or omitted, returns the DOM element (HTMLElement). Returns null if overlay instance is not found. |
| HSOverlay.open(target) | target: HSOverlay \| HTMLElement \| string (CSS selector) | void | Opens the overlay identified by target. Accepts an overlay instance, DOM element, or CSS selector string. Static alternative to instance open() method. |
| HSOverlay.close(target) | target: HSOverlay \| HTMLElement \| string (CSS selector) | void | Closes the overlay identified by target. Accepts an overlay instance, DOM element, or CSS selector string. Static alternative to instance close() method. |
Usage Examples
Example 1: Using instance methods (public API)
// Create a new overlay instance
const modal = new HSOverlay(document.querySelector('#hs-modal'));
const openBtn = document.querySelector('#hs-open-btn');
// Open overlay programmatically
openBtn.addEventListener('click', () => {
modal.open();
});Example 2: Using static methods
const openBtn = document.querySelector('#hs-open-btn');
// Open overlay using static method (no instance needed)
openBtn.addEventListener('click', () => {
HSOverlay.open('#hs-modal');
});Example 3: Getting instance and using methods (recommended pattern)
// Get the overlay instance
const instance = HSOverlay.getInstance('#hs-modal', true);
if (instance) {
const { element } = instance; // element is the HSOverlay instance
const openBtn = document.querySelector('#hs-open-btn');
const closeBtn = document.querySelector('#hs-close-btn');
// Use instance methods
openBtn.addEventListener('click', () => {
element.open();
});
closeBtn.addEventListener('click', () => {
element.close();
});
// Clean up when removing from DOM
function removeOverlay() {
element.destroy();
}
}Example 4: Destroying overlay instance
const instance = HSOverlay.getInstance('#hs-modal', true);
if (instance) {
const { element } = instance;
const removeBtn = document.querySelector('#hs-remove-btn');
removeBtn.addEventListener('click', () => {
// Clean up before removing from DOM
element.destroy();
// Now safe to remove the element
document.querySelector('#hs-modal').remove();
});
}Events
Overlay instances emit events that can be listened to for lifecycle hooks and custom behavior.
| Event Name | When Fired | Callback Parameter | Description |
| --- | --- | --- | --- |
| on:open | After overlay is opened | HSOverlay (overlay instance) | Fires after the overlay has been displayed. Use for post-open actions like loading content or focusing elements. |
| on:close | After overlay is closed | HSOverlay (overlay instance) | Fires after the overlay has been hidden. Use for cleanup or state updates. |
Event Usage Example
// Get overlay instance
const instance = HSOverlay.getInstance('#hs-modal', true);
if (instance) {
const { element } = instance;
// Listen to open event
element.on('open', (overlayInstance) => {
console.log('Overlay opened:', overlayInstance);
// Perform actions after overlay opens
// e.g., load content, focus input, track analytics
});
// Listen to close event
element.on('close', (overlayInstance) => {
console.log('Overlay closed:', overlayInstance);
// Perform cleanup or state updates
});
}Common Patterns
Pattern 1: Static Backdrop
Prevent closing when clicking outside the overlay.
<div class="hs-overlay --overlay-backdrop:static">
<!-- Overlay content -->
</div>Pattern 2: Auto-close on Screen Size
Close overlay automatically at specific breakpoints.
<div class="hs-overlay --auto-close:md --auto-close-equality-type:less-than">
<!-- Overlay content -->
</div>Pattern 3: Programmatic Control
Control overlay from external buttons.
<button id="hs-open-modal">Open Modal</button>
<button id="hs-close-modal">Close Modal</button>
<div id="hs-modal-first" class="hs-overlay hidden">
<!-- Overlay content -->
</div>
<script>
const instance = HSOverlay.getInstance('#hs-modal-first', true);
if (instance) {
const { element } = instance;
document.querySelector('#hs-open-modal').addEventListener('click', () => {
element.open();
});
document.querySelector('#hs-close-modal').addEventListener('click', () => {
element.close();
});
}
</script>License
Copyright (c) 2026 Preline Labs.
Licensed under the MIT License.
