Lockness UI Component
DOCUMENTATION
Modal
Modal dialog component built on the native HTML <dialog> element. Supports
both native dialog and Unpoly layer modes. Includes header, body, footer
sections with close button support.
Installation
deno run -A jsr:@lockness/ui add modal
Usage
import {
Modal,
ModalTrigger,
ModalContent,
ModalHeader,
ModalTitle,
ModalDescription,
ModalBody,
ModalFooter,
ModalClose,
ModalCloseIcon
} from '@lockness/ui/components'
<ModalTrigger targetId="my-modal">Open Modal</ModalTrigger>
<Modal id="my-modal">
<ModalContent>
<ModalHeader>
<ModalTitle>Modal Title</ModalTitle>
<ModalCloseIcon />
</ModalHeader>
<ModalBody>
<p>Modal content goes here.</p>
</ModalBody>
<ModalFooter>
<ModalClose>Cancel</ModalClose>
<Button>Confirm</Button>
</ModalFooter>
</ModalContent>
</Modal>
Components
Modal
The main dialog container using the native <dialog> element.
<Modal id='unique-modal-id'>
{/* Modal content */}
</Modal>
ModalTrigger
Button or link that opens the modal. Supports both native dialog and Unpoly modes.
// Native dialog trigger
<ModalTrigger targetId="my-modal">Open Modal</ModalTrigger>
// Unpoly layer trigger (loads content from URL)
<ModalTrigger href="/modal-content">Load Modal</ModalTrigger>
// With variant
<ModalTrigger targetId="my-modal" variant="outline">Open</ModalTrigger>
ModalContent
Wrapper for modal sections (header, body, footer).
<ModalContent>
{/* Modal sections */}
</ModalContent>
ModalHeader
Top section containing title and close button.
<ModalHeader>
<ModalTitle>Title</ModalTitle>
<ModalCloseIcon />
</ModalHeader>
ModalTitle
Main heading text for the modal.
<ModalTitle>Confirm Action</ModalTitle>
ModalDescription
Subtitle or additional context text.
<ModalDescription>This action cannot be undone.</ModalDescription>
ModalBody
Scrollable main content area.
<ModalBody>
<p>Your modal content here...</p>
</ModalBody>
ModalFooter
Bottom section for action buttons.
<ModalFooter>
<ModalClose>Cancel</ModalClose>
<Button>Confirm</Button>
</ModalFooter>
ModalClose
Button that closes the modal.
<ModalClose>Cancel</ModalClose>
<ModalClose size="md">Done</ModalClose>
ModalCloseIcon
X button for the header area.
<ModalCloseIcon />
Props
ModalProps
| Prop | Type | Default | Description |
|---|---|---|---|
| id | string | required | Unique identifier for the modal dialog |
| children | unknown | - | Modal content |
| class | string | - | Additional CSS class names |
ModalTriggerProps
| Prop | Type | Default | Description |
|---|---|---|---|
| targetId | string | - | ID of the target modal (for native dialog) |
| href | string | - | URL to load in Unpoly layer (for Unpoly mode) |
| variant | 'primary' | 'secondary' | 'outline' | 'ghost' | 'primary' | Visual style variant |
| children | unknown | - | Button content |
| class | string | - | Additional CSS class names |
ModalCloseProps
| Prop | Type | Default | Description |
|---|---|---|---|
| size | 'sm' | 'md' | 'lg' | 'sm' | Button size |
| children | unknown | 'Close' | Button content |
| class | string | - | Additional CSS class names |
Complete Example
<ModalTrigger targetId="delete-modal" variant="destructive">
Delete Item
</ModalTrigger>
<Modal id="delete-modal">
<ModalContent>
<ModalHeader>
<div>
<ModalTitle>Delete Item</ModalTitle>
<ModalDescription>
This action cannot be undone.
</ModalDescription>
</div>
<ModalCloseIcon />
</ModalHeader>
<ModalBody>
<p>
Are you sure you want to delete this item?
All associated data will be permanently removed.
</p>
</ModalBody>
<ModalFooter>
<ModalClose>Cancel</ModalClose>
<Button variant="destructive">Delete</Button>
</ModalFooter>
</ModalContent>
</Modal>
Unpoly Integration
For dynamic content loading, use the href prop on ModalTrigger:
<ModalTrigger href='/api/user/edit' variant='outline'>
Edit Profile
</ModalTrigger>
This creates an Unpoly layer with:
up-layer="new"- Opens as new layerup-size="medium"- Medium-sized modalup-dismissable="button"- Close via button only
Features
- Native Dialog: Uses the HTML
<dialog>element for proper accessibility - Backdrop Click: Closes when clicking outside the modal
- Keyboard Support: Escape key closes the modal
- Focus Management: Proper focus trapping within modal
- Animations: Fade-in and zoom animations on open
- Scrollable Content: Body section scrolls for long content
- Max Height: Limited to 90vh with overflow handling
CSS Variables
@theme {
--modal-header-padding-x: 1.5rem;
--modal-header-padding-y: 1rem;
--modal-body-padding-x: 1.5rem;
--modal-body-padding-y: 1rem;
--modal-footer-padding-x: 1.5rem;
--modal-footer-padding-y: 1rem;
--radius: 0.5rem;
}
LIVE EXAMPLES
FEATURES
Pure CSS
Uses native HTML <dialog> element with zero custom JavaScript
Animated
Smooth fade-in and zoom-in animations using Tailwind CSS
Accessible
ESC key closes modal, click outside to dismiss, focus trapping
Unpoly Support
Can also use Unpoly layers for server-rendered modals
BASIC USAGE
import {
Modal,
ModalTrigger,
ModalContent,
ModalHeader,
ModalTitle,
ModalCloseIcon,
ModalBody,
ModalFooter,
ModalClose,
} from '@lockness/ui/components'
// Trigger button
<ModalTrigger targetId="my-modal">Open Modal</ModalTrigger>
// Modal dialog
<Modal id="my-modal">
<ModalContent>
<ModalHeader>
<ModalTitle>Modal Title</ModalTitle>
<ModalCloseIcon />
</ModalHeader>
<ModalBody>
<p>Your modal content goes here.</p>
</ModalBody>
<ModalFooter>
<ModalClose>Close</ModalClose>
</ModalFooter>
</ModalContent>
</Modal>TRIGGER VARIANTS
<ModalTrigger targetId="my-modal" variant="primary">Primary</ModalTrigger>
<ModalTrigger targetId="my-modal" variant="secondary">Secondary</ModalTrigger>
<ModalTrigger targetId="my-modal" variant="outline">Outline</ModalTrigger>
<ModalTrigger targetId="my-modal" variant="ghost">Ghost</ModalTrigger>UNPOLY LAYER MODE
// Opens a server-rendered page in an Unpoly layer
<ModalTrigger
href="/some-page"
variant="primary"
>
Open Unpoly Layer
</ModalTrigger>