<!-- latest (fastest updates) --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@iamtherealtim/lume-css/dist/lume.min.css" /> <!-- pinned (recommended for production stability) --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@iamtherealtim/lume-css@2.1.0/dist/lume.min.css" />
Lume CSS Documentation
Lume is a minimal CSS library for thoughtful interfaces. This documentation is organized by foundation, component, and utility layers, with copy-ready HTML snippets for each pattern.
data-ui-init="pending" with
html[data-ui-init="pending"] body { visibility: hidden; }, defer icon script, and reveal after
window.load + bounded fonts wait.
See No Flicker Setup.
Installation
Use Lume from the local file, your npm package build, or a CDN. Start with one stylesheet import, then apply No Flicker Setup for stable first paint.
npm install @iamtherealtim/lume-css /* app.css */ @import "@iamtherealtim/lume-css/lume.css";
Starter Templates
Choose a setup path: full bundle for fastest integration, or partial module imports when you want tighter payload control.
<!-- HTML app shell --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@iamtherealtim/lume-css/dist/lume.min.css" /> <script defer src="https://cdn.jsdelivr.net/npm/@iamtherealtim/lume-css/dist/lume.js"></script>
/* app.css */ /* reset.css restyles bare elements (body, a, code...). Leave it out when adding Lume to an app that already has base styles. */ @import "@iamtherealtim/lume-css/reset.css"; @import "@iamtherealtim/lume-css/core.css"; /* required: tokens */ @import "@iamtherealtim/lume-css/components.css"; @import "@iamtherealtim/lume-css/utilities.css"; /* optional */ @import "@iamtherealtim/lume-css/a11y.css"; @import "@iamtherealtim/lume-css/responsive.css";
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@iamtherealtim/lume-css/dist/lume.min.css" />
<script defer src="https://cdn.jsdelivr.net/npm/@iamtherealtim/lume-css/dist/lume.js"></script>
<button
class="lume-btn lume-btn-primary"
data-lume-toggle="modal"
data-lume-target="#starter-modal">
Open modal
</button>
<div class="lume-modal-overlay" id="starter-modal">
<div class="lume-modal" role="dialog" aria-modal="true" aria-labelledby="starter-modal-title">
<div class="lume-modal-header">
<h3 class="lume-h4" id="starter-modal-title">Starter modal</h3>
<button class="lume-modal-close" data-lume-close data-lume-target="#starter-modal" aria-label="Close">×</button>
</div>
<div class="lume-modal-body">
<p class="lume-body">This modal opens and closes using Lume's data attributes.</p>
</div>
<div class="lume-modal-footer">
<button class="lume-btn lume-btn-outline" data-lume-close data-lume-target="#starter-modal">Close</button>
</div>
</div>
</div>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@iamtherealtim/lume-css/dist/lume.min.css" />
<script defer src="https://cdn.jsdelivr.net/npm/@iamtherealtim/lume-css/dist/lume.js"></script>
<div class="lume-dropdown" id="starter-dropdown">
<button
class="lume-btn lume-btn-outline"
data-lume-toggle="dropdown"
data-lume-target="#starter-dropdown">
Open menu
</button>
<div class="lume-dropdown-menu" role="menu">
<button class="lume-dropdown-item" role="menuitem">Profile</button>
<button class="lume-dropdown-item" role="menuitem">Billing</button>
<button class="lume-dropdown-item lume-dropdown-item-danger" role="menuitem">Sign out</button>
</div>
</div>
lume-* to avoid collisions with existing app styles.Migration & Integration
Adding Lume to an existing app usually takes three safe steps: import once, scope updates gradually, and swap custom utilities incrementally.
- Start with one route or feature area.
- Use existing classes + add
lume-*utilities gradually. - Adopt token overrides in app-level
:rootonce layout is stable.
<link rel="stylesheet" href="/legacy.css" /> <link rel="stylesheet" href="/lume.css" /> <link rel="stylesheet" href="/app-overrides.css" />
No Flicker Setup
Use this startup pattern to prevent first-paint theme flashes and layout shifts while fonts and icons initialize. It sets theme early, keeps the page hidden during hydration, and reveals it once the UI is ready.
<!-- 1) Prepaint init: place this before your CSS links -->
<script>
document.documentElement.setAttribute('data-ui-init', 'pending');
try {
const savedTheme = localStorage.getItem('lume-theme');
if (savedTheme) document.documentElement.setAttribute('data-theme', savedTheme);
} catch (_) {}
</script>
<!-- 2) Lume CSS -->
<link rel="stylesheet" href="lume.css" />
<!-- 3) Optional icons: defer so layout CSS is available first -->
<script defer src="https://unpkg.com/lucide@1.49.0/dist/umd/lucide.min.js"></script>
<!-- 4) Ready gate: run near end of body -->
<script>
(function () {
function initIcons() {
if (window.lucide && typeof window.lucide.createIcons === 'function') {
window.lucide.createIcons();
}
}
function revealUi() {
document.documentElement.removeAttribute('data-ui-init');
}
function waitForFontsOrTimeout(timeoutMs) {
if (!document.fonts || !document.fonts.ready) {
return Promise.resolve();
}
return Promise.race([
document.fonts.ready,
new Promise((resolve) => setTimeout(resolve, timeoutMs))
]);
}
window.addEventListener('load', function () {
waitForFontsOrTimeout(1200).then(function () {
initIcons();
revealUi();
});
});
})();
</script>
html[data-ui-init="pending"] body { visibility: hidden; }Theming & Dark Mode
Override CSS custom properties to match your brand. Toggle dark mode by setting data-theme="dark" on <html>.
:root {
--lume-accent: #7C3AED;
--lume-accent-light: #EDE9FE;
--lume-accent-dark: #5B21B6;
}
/* manual dark mode */
html[data-theme="dark"] {
--lume-bg: #141412;
--lume-text: #F0EFE9;
}
localStorage and apply it before CSS loads. See No Flicker Setup for the full startup pattern.Browser Support
Lume targets modern evergreen browsers. Some visual enhancements (for example color-mix()) may gracefully degrade in older environments.
| Browser | Supported | Notes |
|---|---|---|
| Chrome / Edge (current) | Yes | Full feature set |
| Firefox (current) | Yes | Full feature set |
| Safari 16.4+ | Yes | Recommended baseline for modern CSS parity |
| Legacy browsers | Partial | Core layout works; modern visual effects may degrade |
Design Tokens
Lume exposes colors, spacing, typography, radius, and shadows through CSS custom properties. Build custom themes by overriding these tokens in your app stylesheet.
:root {
--lume-bg: #FAFAF8;
--lume-surface: #F4F3EF;
--lume-text: #1C1C1A;
--lume-accent: #5B5BD6;
--lume-accent-light: #EEEEF9;
--lume-accent-dark: #3E3E9E;
--lume-danger: #C0392B;
--lume-warning: #B7791F;
--lume-info: #1A5C8A;
--lume-success: #30A46C;
}
:root {
--lume-font-sans: 'Instrument Sans', system-ui, sans-serif;
--lume-font-serif: 'Lora', Georgia, serif;
--lume-text-sm: 0.875rem;
--lume-text-base: 1rem;
--lume-text-lg: 1.125rem;
--lume-space-2: 0.5rem;
--lume-space-4: 1rem;
--lume-space-6: 1.5rem;
--lume-radius: 6px;
--lume-radius-lg: 12px;
--lume-shadow-sm: 0 1px 4px rgba(0,0,0,0.07);
}
| Token Group | Examples | Used In |
|---|---|---|
| Color | --lume-accent, --lume-border |
All components |
| Typography | --lume-font-sans, --lume-text-lg |
Headings, body, controls |
| Spacing | --lume-space-4, --lume-space-8 |
Padding, gaps, stacks |
| Radius / Shadow | --lume-radius-md, --lume-shadow |
Cards, modals, dropdowns |
Typography
Use semantic tags where possible and add Lume classes when you want the design-system styling applied. Lume includes display, heading, body, lead, small text, code, and blockquote patterns.
Lead paragraph text for intros and section summaries.
Body text for long-form content and interface copy.
Small text for metadata and helper copy.
Good design is as little design as possible.
<div class="lume-display">Display Heading</div> <div class="lume-h1">Heading One</div> <div class="lume-h2">Heading Two</div> <p class="lume-lead">Lead paragraph text...</p> <p class="lume-body">Body text...</p> <p class="lume-small">Small text...</p> <blockquote class="lume-blockquote"> Good design is as little design as possible. </blockquote>
Writing with lume-prose
Use .lume-prose for article-like content where headings, paragraphs, lists, and blockquotes need consistent spacing.
- Good defaults for body content
- Readable line-height and max width
- Works well in docs and blogs
<article class="lume-prose">
<h2>Article title</h2>
<p>Body paragraph...</p>
<ul>
<li>List item</li>
<li>List item</li>
</ul>
</article>
| Class | Purpose | Typical Usage |
|---|---|---|
.lume-display |
Largest serif heading | Hero headers |
.lume-h1 ... .lume-h6 |
Heading scale | Section and card titles |
.lume-lead |
Emphasized paragraph | Intro copy |
.lume-prose |
Long-form content wrapper | Docs / blog article |
Layout
Layout classes cover container sizing, grid columns, and flex helpers. Combine them with spacing utilities for fast page scaffolding.
<div class="lume-grid lume-grid-3"> <div>Column 1</div> <div>Column 2</div> <div>Column 3</div> </div>
<div class="lume-flex lume-justify-between lume-items-center"> <span>Left item</span> <span>Right item</span> </div> <div class="lume-stack-2"> <div>Stack item A</div> <div>Stack item B</div> </div>
| Class | Description | Notes |
|---|---|---|
.lume-container |
Responsive page container | Use -sm/-md/-lg/-xl variants |
.lume-grid-2/3/4 |
Fixed equal grid columns | Collapse at small breakpoints |
.lume-grid-5/6 |
Extended fixed equal grid columns | Useful for denser dashboards and data tiles |
.lume-grid-auto-fill/auto-fit |
Auto-responsive grid tracks | Adapts column count based on available width |
.lume-gap-1..8 |
Gap scale for grid and flex containers | One scale for both layout modes |
.lume-grid-collapse-sm/md/lg |
Explicit breakpoint collapse modifiers | Force single-column stack at selected breakpoints |
.lume-flex + helpers |
Flex row/column alignment | Pair with .lume-gap-* |
.lume-stack-* |
Vertical spacing between children | Includes .lume-stack-4 |
Forms
Form controls include labeled inputs, selects, textarea, search fields, check/radio groups, errors, and input groups.
<div class="lume-form-group">
<label class="lume-label lume-label-required">Full name</label>
<input class="lume-input" type="text" placeholder="Jane Smith" />
<span class="lume-hint">As it appears on legal documents.</span>
</div>
<div class="lume-select-wrapper">
<select class="lume-select">
<option>United States</option>
<option>Canada</option>
</select>
</div>
<div class="lume-input-group">
<span class="lume-input-addon">https://</span>
<input class="lume-input" type="text" placeholder="yourdomain.com" />
</div>
<input class="lume-input lume-input-error" type="email" value="not-an-email" />
<span class="lume-error-message">Please enter a valid email address.</span>
<label class="lume-check-group">
<input type="checkbox" checked />
<span class="lume-check-label">Enable notifications</span>
</label>
<label class="lume-check-group">
<input type="radio" name="plan" checked />
<span class="lume-check-label">Starter plan</span>
</label>
<label class="lume-check-group">
<input type="radio" name="plan" />
<span class="lume-check-label">Pro plan</span>
</label>
<textarea class="lume-textarea" rows="3" placeholder="Tell us about yourself..."></textarea>
<input class="lume-range" type="range" min="0" max="100" value="60" />
<label class="lume-switch">
<input type="checkbox" checked />
<span class="lume-switch-track" aria-hidden="true"></span>
<span class="lume-switch-label">Enable two-factor authentication</span>
</label>
<div class="lume-file">
<input class="lume-file-input" type="file" />
<div class="lume-file-dropzone">
<div class="lume-file-title">Drop files here or click to browse</div>
<div class="lume-file-meta">PNG, JPG, PDF up to 10MB</div>
</div>
</div>
</div>
Cards
Cards support default, lifted, and filled surfaces, plus optional header/footer regions.
Default card
Bordered surface for general content.
Lifted card
Primary content block with shadow.
Filled card
Muted surface for secondary content.
<div class="lume-card"> <div class="lume-card-body">...</div> </div> <div class="lume-card lume-card-lifted">...</div> <div class="lume-card lume-card-filled">...</div>
Daily backups succeeded. No incidents in the last 24 hours.
<div class="lume-card">
<div class="lume-card-header">
<span class="lume-h4">Project status</span>
<span class="lume-badge lume-badge-success">Healthy</span>
</div>
<div class="lume-card-body">
<p class="lume-card-text">Daily backups succeeded.</p>
</div>
<div class="lume-card-footer">
<button class="lume-btn lume-btn-outline lume-btn-sm">View logs</button>
<button class="lume-btn lume-btn-primary lume-btn-sm">Open dashboard</button>
</div>
</div>
<a class="lume-card lume-card-link lume-card-interactive" href="/reports/revenue">
<div class="lume-card-body">
<h3 class="lume-card-title">Revenue report</h3>
<p class="lume-card-text">Open the weekly performance summary.</p>
</div>
</a>
Badges & Tags
Use badges for status labels and tags for lightweight categorical chips.
<span class="lume-badge lume-badge-success lume-badge-dot lume-badge-pill">Active</span> <span class="lume-badge lume-badge-danger">Error</span> <span class="lume-tag">Design</span> <span class="lume-tag"> Roadmap <button class="lume-tag-close" aria-label="Remove">×</button> </span>
Alerts
Alerts communicate inline feedback. Choose severity variant by context and include short action text.
<div class="lume-alert lume-alert-info" role="alert">
<div class="lume-alert-content">
<div class="lume-alert-title">Heads up</div>
<div class="lume-alert-body">This is informational context.</div>
</div>
</div>
<div class="lume-alert lume-alert-success" role="alert">
<div class="lume-alert-content">
<div class="lume-alert-title">Saved</div>
<div class="lume-alert-body">Your changes were saved successfully.</div>
</div>
</div>
<div class="lume-alert lume-alert-warning" role="alert">
<div class="lume-alert-content">
<div class="lume-alert-title">Usage limit</div>
<div class="lume-alert-body">You are at 90% of your quota.</div>
</div>
</div>
<div class="lume-alert lume-alert-danger" role="alert">
<div class="lume-alert-content">
<div class="lume-alert-title">Request failed</div>
<div class="lume-alert-body">Please retry in a few seconds.</div>
</div>
</div>
Tables
Tables support hover rows, compact density, striped rows, and right-aligned action columns.
| Name | Status | Role | Actions |
|---|---|---|---|
| Alice Chen | Active | Admin | |
| Marcus Webb | Pending | Editor |
<div class="lume-table-wrapper">
<table class="lume-table lume-table-hover">
<thead>
<tr><th>Name</th><th>Status</th><th>Role</th><th class="lume-text-right">Actions</th></tr>
</thead>
<tbody>
<tr>
<td>Alice Chen</td>
<td><span class="lume-badge lume-badge-success lume-badge-dot lume-badge-pill">Active</span></td>
<td>Admin</td>
<td class="text-right"><button class="lume-btn lume-btn-ghost lume-btn-sm">Edit</button></td>
</tr>
</tbody>
</table>
</div>
Modal
Use modal overlays for focused decisions and short form flows. Always provide close controls and escape behavior.
<button class="lume-btn lume-btn-primary">Open modal</button>
<div class="lume-modal-overlay is-open">
<div class="lume-modal" role="dialog" aria-modal="true">
<div class="lume-modal-header">...</div>
<div class="lume-modal-body">...</div>
<div class="lume-modal-footer">...</div>
</div>
</div>
Tooltip
Tooltips are attribute-driven and work on any inline or block element. They are a visual enhancement: screen readers do not announce data-tooltip, so never make a tooltip the only place information lives, and give icon-only buttons an aria-label.
Note: CSS-only tooltips may clip inside overflow: hidden parents and complex scroll containers. For advanced positioning, use a JS-positioned popover/tooltip layer.
<button data-tooltip="Default tooltip">Hover</button> <button data-tooltip="Bottom" data-tooltip-pos="bottom">Hover</button> <button data-tooltip="Left" data-tooltip-pos="left">Hover</button> <button data-tooltip="Right" data-tooltip-pos="right">Hover</button>
Tabs
Tabs are class-toggled components. Activate the selected tab and matching panel by id.
Lume tabs are lightweight and framework-agnostic.
With lume.js loaded, tabs get roles, panel wiring and arrow-key navigation automatically. Without it, toggle .is-active on both tab and panel.
Prefer keyboard navigation with focus-visible styles enabled.
<div class="lume-tabs"> <button class="lume-tab is-active">Overview</button> <button class="lume-tab">API</button> </div> <div class="lume-tab-panels"> <div class="lume-tab-panel is-active">...</div> <div class="lume-tab-panel">...</div> </div>
Accordion
Accordions use native <details> and <summary>, so they work without JavaScript and are accessible by default.
Because it uses native <details>, open/close height animation is intentionally minimal. Use an enhanced JS variant if you need animated panels.
How do I install Lume?
How do I customize colors?
--lume-accent.<div class="lume-accordion">
<details class="lume-accordion-item" open>
<summary class="lume-accordion-trigger">Question</summary>
<div class="lume-accordion-body">Answer</div>
</details>
</div>
Dropdown
Dropdown menus are toggled by adding/removing .is-open on the parent .lume-dropdown.
<div class="lume-dropdown is-open">
<button class="lume-btn lume-btn-outline">Actions ▾</button>
<div class="lume-dropdown-menu">
<button class="lume-dropdown-item">Edit profile</button>
</div>
</div>
Progress & Spinner
Use progress bars for known completion states and spinners for indeterminate loading states.
<div class="lume-progress">
<div
class="lume-progress-bar"
role="progressbar"
aria-label="Upload progress"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="72"
style="width: 72%;">
</div>
</div>
<div class="lume-progress">
<div class="lume-progress-bar lume-progress-bar-striped" style="width: 45%;"></div>
</div>
<div class="lume-spinner lume-spinner-sm"></div>
<div class="lume-spinner"></div>
<div class="lume-spinner lume-spinner-lg"></div>
Avatar
Avatars support initials or image content, size modifiers, status dots, and overlapping groups.
<div class="lume-avatar">AC</div> <div class="lume-avatar lume-avatar-lg">MW</div> <div class="lume-avatar-wrapper"> <div class="lume-avatar lume-avatar-lg">AC</div> <div class="lume-avatar-status"></div> </div> <div class="lume-avatar-group"> <div class="lume-avatar">A</div> <div class="lume-avatar">B</div> </div>
Toast
Toasts are temporary notifications. Use semantic variants for success, info, warning, and error feedback.
<div class="lume-toast lume-toast-success">
<div class="lume-toast-content">
<div class="lume-toast-title">Saved</div>
<div class="lume-toast-body">Your changes have been saved.</div>
</div>
</div>
<div class="lume-toast lume-toast-danger">
<div class="lume-toast-content">
<div class="lume-toast-title">Upload failed</div>
<div class="lume-toast-body">File exceeds size limit.</div>
</div>
</div>
Pagination
Pagination items support active and disabled state classes. Pair with server-side or client-side paging logic.
<nav class="lume-pagination" aria-label="Pagination"> <a class="lume-page-item is-disabled" aria-disabled="true" tabindex="-1">← Prev</a> <a class="lume-page-item is-active" aria-current="page">1</a> <a class="lume-page-item">2</a> </nav>
Skeleton
Skeleton loaders preserve layout during async fetches and reduce perceived latency.
<div class="lume-skeleton-card" role="status" aria-label="Loading content">
<div class="lume-skeleton lume-skeleton-card-img"></div>
<div class="lume-skeleton-card-body">
<div class="lume-skeleton lume-skeleton-heading"></div>
<div class="lume-skeleton lume-skeleton-text"></div>
</div>
</div>
Callout
Callouts are persistent emphasis blocks for guidance, warnings, and important contextual information.
<div class="lume-callout"> <div class="lume-callout-title">Default callout</div> Use callouts for persistent notes. </div> <div class="lume-callout lume-callout-info"> <div class="lume-callout-title">Info</div> This endpoint is rate limited. </div> <div class="lume-callout lume-callout-warning"> <div class="lume-callout-title">Warning</div> Migrating now may restart active sessions. </div>
List Group
List groups are bordered vertical collections for settings menus, action lists, and simple data groups.
<div class="lume-list"> <div class="lume-list-header">Account</div> <a class="lume-list-item lume-list-item-action">Profile</a> <a class="lume-list-item lume-list-item-action lume-list-item-active">Preferences</a> <button class="lume-list-item lume-list-item-action lume-list-item-danger">Delete account</button> </div>
Stat / KPI
Stat cards present key metrics with trend context in dashboards and analytics screens.
<div class="lume-stat"> <div class="lume-stat-label">Revenue</div> <div class="lume-stat-value">$84.2k</div> <div class="lume-stat-trend lume-stat-trend-up">12.5% MoM</div> </div> <div class="lume-stat"> <div class="lume-stat-label">Churn</div> <div class="lume-stat-value">2.4%</div> <div class="lume-stat-trend lume-stat-trend-down">0.3% MoM</div> </div>
Empty State
Empty states guide users when no data exists yet. Include a clear action to unblock progress.
<div class="lume-empty"> <div class="lume-empty-icon">📂</div> <div class="lume-empty-title">No files yet</div> <div class="lume-empty-body">Upload your first file...</div> <div class="lume-empty-actions">...</div> </div>
Stepper
Steppers communicate progress in multi-step flows. Use completed, active, and optional error state classes.
<div class="lume-steps"> <div class="lume-step lume-step-completed">...</div> <div class="lume-step lume-step-active">...</div> <div class="lume-step lume-step-error">...</div> </div> <div class="lume-steps lume-steps-vertical"> <div class="lume-step lume-step-completed">...</div> <div class="lume-step lume-step-active">...</div> <div class="lume-step lume-step-error">...</div> </div>
Drawer
Drawers slide in from screen edges and work well for secondary workflows or contextual editing.
<div class="lume-drawer-overlay is-open"></div> <div class="lume-drawer lume-drawer-right is-open"> <div class="lume-drawer-header">...</div> <div class="lume-drawer-body">...</div> </div>
Keyboard / Kbd
Use semantic <kbd> for shortcuts in docs and app help UI.
Open command palette with ⌘ K.
<p>Open command palette with <kbd>⌘</kbd> <kbd>K</kbd>.</p>
Utilities
Use utilities for small one-off adjustments. Keep usage intentional to preserve readability.
| Category | Utilities | Example |
|---|---|---|
| Spacing | lume-mt-*, lume-mb-*, lume-p-*, lume-px-*, lume-py-* |
class="lume-mt-4 lume-p-4" |
| Display / Flex | lume-flex, lume-grid, lume-flex-1, lume-gap-* |
class="lume-flex lume-gap-4" |
| Responsive visibility | lume-sm-hidden, lume-sm-block, lume-md-hidden, lume-lg-hidden |
class="lume-sm-hidden lume-md-block" |
| Text behavior | lume-truncate, lume-text-wrap, lume-text-nowrap, lume-uppercase, lume-tracking-wide |
class="lume-truncate lume-uppercase" |
| Grid layout helpers | lume-gap-*, lume-grid-collapse-sm/md/lg |
class="lume-grid lume-grid-4 lume-gap-6 lume-grid-collapse-md" |
| Overflow / Position | lume-overflow-auto, lume-pos-relative, lume-pos-sticky |
class="lume-pos-sticky lume-inset-0" |
| Z-index | lume-z-dropdown, lume-z-modal, lume-z-toast |
Layer orchestration |
| Opacity / Cursor | lume-opacity-50, lume-cursor-not-allowed |
Disabled states |
| Transition / Aspect | lume-transition-fast, lume-aspect-video |
Smooth state + media boxes |
Composed from utility classes only.
<div class="lume-rounded-lg lume-overflow-hidden"> <div class="lume-aspect-video"></div> <div class="lume-p-4">...</div> </div> <button class="lume-btn lume-btn-outline w-full transition-fast"> Action </button>
Icon Set & Mapping
Lume includes icon helper classes so icons can be dropped into buttons, pills, badges, tags, tabs, dropdown items, list rows, and breadcrumb links with consistent alignment and sizing.
Lume is icon-library agnostic. Any inline <svg> inside a button, badge, tag, tab, dropdown item,
list item, breadcrumb link or check label is sized to the surrounding text, and .lume-icon sizes an icon anywhere else.
The docs and admin demo use Lucide as a recommended integration, not a dependency.
| UI Meaning | Mapped Icon (data-lucide) |
|---|---|
| Dashboard | layout-dashboard |
| Analytics | bar-chart-3 |
| Users | users |
| Orders | shopping-cart |
| Billing | credit-card |
| Audit Logs | history |
| Support Tickets | life-buoy |
| Integrations | plug-zap |
| Settings | settings |
| Library Docs | book-open |
| Showcase | sparkles |
| Menu toggle | menu |
| Search field | search |
| Class | Purpose |
|---|---|
.lume-icon |
Base icon size helper for inline icons |
.lume-icon-sm / .lume-icon-lg |
Optional icon size modifiers |
.lume-with-icon |
Align text + icon with consistent gap |
<button class="lume-btn lume-btn-primary lume-with-icon"> <i data-lucide="rocket" class="lume-icon"></i> Launch </button> <button class="lume-btn lume-btn-outline-primary lume-btn-pill lume-with-icon"> <i data-lucide="sparkles" class="lume-icon"></i> Pro Pill </button> <span class="lume-badge lume-badge-info lume-badge-pill lume-with-icon"> <i data-lucide="shield-check" class="lume-icon"></i> Verified </span> <span class="lume-tag lume-with-icon"> <i data-lucide="tag" class="lume-icon"></i> Release </span>
<button class="lume-tab is-active">
<i data-lucide="layout-dashboard" class="lume-icon"></i>
Overview
</button>
<button class="lume-dropdown-item">
<i data-lucide="user-cog" class="lume-icon"></i>
Account settings
</button>
<a href="#" class="lume-list-item lume-list-item-action">
<i data-lucide="folder-open" class="lume-icon"></i>
Projects
</a>
<span class="lume-breadcrumb-item">
<a href="#" class="lume-with-icon">
<i data-lucide="home" class="lume-icon lume-icon-sm"></i>
Home
</a>
</span>
<!-- Run once after DOM is ready -->
<script>window.lucide?.createIcons();</script>
JavaScript Runtime
Every Lume component renders without JavaScript. The optional lume.js runtime (6 kB gzipped, no dependencies)
adds behaviour through data attributes: open and close, focus management, keyboard navigation and auto-dismiss.
It is a classic script, not an ES module. Loading it defines the global Lume. Type declarations ship in the package.
<script defer src="https://cdn.jsdelivr.net/npm/@iamtherealtim/lume-css@2.1.0/dist/lume.js"></script> <!-- or, with a bundler --> <script type="module"> import "@iamtherealtim/lume-css/lume.js"; // side-effect import, sets window.Lume </script>
Data attributes
| Attribute | On | Effect |
|---|---|---|
data-lume-toggle + data-lume-target | Any button | Opens or closes the target modal, drawer or dropdown. Use data-lume-toggle="dropdown" on menu triggers. |
data-lume-open / data-lume-close | Any button | Opens or closes the target. Without a target, data-lume-close closes the dialog or menu it sits in. |
data-lume-dismiss="toast" | Button in a toast | Removes the toast. .lume-toast-close does the same without the attribute. |
data-lume-dismiss="alert" | Button in an alert or callout | Removes the alert or callout. |
data-lume-remove | Button in a tag, badge or chip | Removes the nearest tag, badge or chip, or the nearest ancestor matching the attribute's selector. |
data-lume-autodismiss | .lume-toast | Milliseconds before removal (default 4500), or "false" to keep it. Hovering pauses the remaining time. |
data-lume-overlay | .lume-drawer | Selector for the drawer's overlay when it is neither the drawer's parent nor its sibling. |
data-lume-panel | .lume-tab | Id of the tab's panel when panels are not in matching order. aria-controls works too. |
API reference
| Member | Arguments | Returns | Notes |
|---|---|---|---|
Lume.open(target, trigger?) | Selector or element; optional trigger element | void | Focus returns to trigger on close, or to whatever was focused before opening. |
Lume.close(target) | Selector or element | void | Restores focus for dialogs. |
Lume.toggle(target, trigger?) | Selector or element | void | Same as open or close. |
Lume.isOpen(target) | Selector or element | boolean | false if the target doesn't resolve. |
Lume.initTabs(root?) | Element, selector or document | void | Wires tabs inserted after page load. Tabs present at load are wired automatically. |
Lume.initToasts(root?) | Element, selector or document | void | Rarely needed: toasts added to the page are picked up automatically. |
Lume.dismissToast(toast) | Selector or element | void | Removes the toast and cancels its timer. |
Lume.version | — | string | Package version the runtime was built from. |
Lume.open('#invite-modal'); // selector or element
Lume.open(panel, triggerButton); // focus returns to triggerButton on close
Lume.close('#invite-modal');
Lume.toggle('#user-menu');
Lume.isOpen('#invite-modal'); // true / false
Lume.initTabs(container); // tabs inserted after page load
Lume.dismissToast(toastElement);
Lume.version; // "2.1.0"
Component contracts
What the runtime does for each interactive component, and what your markup must provide.
| Component | lume.js provides | You provide |
|---|---|---|
| Modal and drawer | Focus moves in on open and is trapped; Escape closes the top-most dialog; focus returns to the trigger; page scroll is locked without touching your body styles; role="dialog" and aria-modal="true" are added if missing; closed dialogs are hidden from assistive tech. | An accessible name: aria-labelledby pointing at the title, or aria-label. The runtime warns in the console when it's missing but never invents one. |
| Dropdown | Implements the ARIA menu pattern: aria-haspopup and aria-expanded on the trigger, focus to the first item, Arrow keys, Home and End, Escape returns focus, choosing an item closes the menu, clicking or tabbing away closes it. | role="menu" on the menu, role="menuitem" on each item, role="separator" on dividers. Put only actions in a menu: forms, link lists and arbitrary content need a different pattern. |
| Tabs | tablist, tab and tabpanel roles; aria-selected, roving tabindex, aria-controls and aria-labelledby when ids exist; Arrow keys, Home and End; hidden inactive panels. | Ids on tabs and panels so they can be linked, and a label for the tab list, e.g. aria-label on .lume-tabs. |
| Toast | Auto-dismiss with a pause on hover; dismiss buttons work. | A live region so screen readers announce toasts: aria-live="polite" on the container, or role="alert" for urgent ones. An aria-label on icon-only close buttons. |
| Tooltip | Nothing; it is pure CSS. | Treat data-tooltip as a visual hint. It is not announced by screen readers, so never put information only there. Give icon-only buttons an aria-label. |
<div class="lume-dropdown" id="user-menu">
<button class="lume-btn lume-btn-outline" id="user-menu-trigger"
data-lume-toggle="dropdown" data-lume-target="#user-menu">Account</button>
<div class="lume-dropdown-menu" role="menu" aria-labelledby="user-menu-trigger">
<button class="lume-dropdown-item" role="menuitem">Profile</button>
<div class="lume-dropdown-divider" role="separator"></div>
<button class="lume-dropdown-item" role="menuitem">Sign out</button>
</div>
</div>
<div class="lume-toast-container lume-toast-container-br"
role="region" aria-label="Notifications" aria-live="polite">
<!-- toasts appended here are announced and auto-dismiss -->
<div class="lume-toast lume-toast-success" data-lume-autodismiss="6000">
<div class="lume-toast-content">Saved</div>
<button class="lume-toast-close" aria-label="Dismiss">×</button>
</div>
</div>
Accessibility
Lume includes visible focus styles and reduced motion support. Use this checklist during implementation.
| Area | What to implement | Lume support |
|---|---|---|
| Keyboard navigation | All interactive controls reachable with Tab | :focus-visible styles included |
| Modal semantics | An accessible name via aria-labelledby or aria-label |
lume.js adds role="dialog" and aria-modal, traps and restores focus, locks scroll |
| Menus and tabs | role="menu" / menuitem on dropdowns; ids on tabs and panels |
lume.js handles ARIA state and keyboard navigation; see component contracts |
| Toasts | aria-live="polite" on the toast container |
Auto-dismiss pauses on hover |
| Tooltips | Never put essential information only in data-tooltip |
Visual hint only; shows on hover and keyboard focus |
| Alerts | Use role="alert" for critical live updates |
Alert variants available |
| Motion | Respect reduced motion preference | @media (prefers-reduced-motion: reduce) support included |
| Contrast | Verify custom theme contrast at WCAG AA | Default tokens pass AA on every surface in both themes; checked with axe in CI |