Overlay
The Overlay Tailwind plugin is an unstyled JavaScript utility for creating modals, drawers, and more, enhancing user interaction and content layout.
Contents
- Overview
- Installation
- Basic usage
- Configuration Options
- JavaScript API
- Events
- Common Patterns
- License
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/overlay
CSS
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) 2024 Htmlstream.
Licensed under the MIT License.