Admin Demo Showcase GitHub
Developer Documentation

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.

CDN-ready CSS variables Dark mode Accessibility-aware Low JavaScript footprint
Production startup checklist: preload theme before CSS, use 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.

CDN
<!-- 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" />
npm / local build
npm install @iamtherealtim/lume-css

/* app.css */
@import "@iamtherealtim/lume-css/lume.css";
For production, prefer pinned CDN versions and review CHANGELOG before upgrading.

Starter Templates

Choose a setup path: full bundle for fastest integration, or partial module imports when you want tighter payload control.

Full bundle (fastest)
<!-- 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>
Partial modules (selective)
/* 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";
Interactive starter (modal toggle)
<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">&times;</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>
Interactive starter (dropdown toggle)
<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>
Utilities are namespaced as 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.

Incremental rollout checklist
  • Start with one route or feature area.
  • Use existing classes + add lume-* utilities gradually.
  • Adopt token overrides in app-level :root once layout is stable.
Stylesheet order
<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.

Drop-in startup snippet
<!-- 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>
Required CSS guard in your app stylesheet: 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>.

Theme override example
: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;
}
Tip: persist user theme preference with 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.

Core color tokens
--lume-text
--lume-accent
--lume-surface
--lume-success
: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;
}
Type, spacing, radius tokens
: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.

Typography scale
Display Heading
Heading One
Heading Two

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>
Prose and code

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.

Grid columns
Column 1
Column 2
Column 3
<div class="lume-grid lume-grid-3">
  <div>Column 1</div>
  <div>Column 2</div>
  <div>Column 3</div>
</div>
Flex + stack helpers
Left item Right item
Stack item A
Stack item B
Stack item C
<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

Buttons

Buttons include solid, outline, ghost, danger, size, pill, block and icon-ready variants. Gradient buttons live in the optional extras.css bundle so the core stays calm.

Button variants
<button class="lume-btn lume-btn-primary">Primary</button>
<button class="lume-btn lume-btn-outline">Outline</button>
<button class="lume-btn lume-btn-ghost">Ghost</button>

<button class="lume-btn lume-btn-primary lume-btn-sm">Small</button>
<button class="lume-btn lume-btn-primary lume-btn-pill">Pill</button>
<button class="lume-btn lume-btn-dark lume-btn-block">Full width</button>
Buttons with icons
<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-with-icon">
  <i data-lucide="sparkles" class="lume-icon"></i>
  Upgrade
</button>

<button class="lume-btn lume-btn-dark lume-with-icon">
  Share
  <i data-lucide="arrow-right" class="lume-icon"></i>
</button>

<button class="lume-btn lume-btn-primary lume-btn-pill lume-with-icon">
  <i data-lucide="check" class="lume-icon"></i>
  Confirm
</button>

<button class="lume-btn lume-btn-dark lume-btn-icon" aria-label="Favorite">
  <i data-lucide="heart" class="lume-icon"></i>
</button>
Gradient buttons (optional extras.css)
<!-- Not in lume.css. Load the optional extras bundle first: -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@iamtherealtim/lume-css/dist/extras.css" />

<button class="lume-btn lume-btn-gradient-aurora">Aurora Shift</button>
<button class="lume-btn lume-btn-gradient-sunset">Sunset Glow</button>
<button class="lume-btn lume-btn-gradient-ocean">Ocean Lift</button>
<button class="lume-btn lume-btn-gradient-violet">Violet Pulse</button>
<button class="lume-btn lume-btn-gradient-forest">Forest Rise</button>
<button class="lume-btn lume-btn-gradient-aurora lume-btn-pill">Aurora Pill</button>

<!-- Optional effect controls -->
<button class="lume-btn lume-btn-gradient-sunset lume-btn-no-glow">No Glow</button>
<button class="lume-btn lume-btn-gradient-ocean lume-btn-no-lift">No Lift</button>
<button class="lume-btn lume-btn-gradient-sunset lume-btn-no-filter">No Filter</button>
<button class="lume-btn lume-btn-gradient-aurora lume-btn-no-shift">No Shift</button>
<button class="lume-btn lume-btn-gradient-violet lume-btn-fx-off">FX Off</button>
Loading button pattern
<button class="lume-btn lume-btn-primary lume-btn-loading" aria-busy="true">
  Saving changes
</button>

<button class="lume-btn lume-btn-outline lume-btn-loading" disabled>
  Processing
</button>

Forms

Form controls include labeled inputs, selects, textarea, search fields, check/radio groups, errors, and input groups.

Common form patterns
As it appears on legal documents.
Please enter a valid email address.
https://
Drop files here or click to browse
PNG, JPG, PDF up to 10MB
<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.

Card variants

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>
Card with header/footer
Project status Healthy

Daily backups succeeded. No incidents in the last 24 hours.

Interactive card links
<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.

Status indicators
Primary Active Pending Error Internal
Design Open Source Roadmap
<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.

Alert variants
<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.

Data table
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>

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.

Directional tooltips
<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.

Underline tabs

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.

Details accordion
How do I install Lume?
Use CDN or npm and import one stylesheet into your app.
How do I customize colors?
Override CSS variables like --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>

Progress & Spinner

Use progress bars for known completion states and spinners for indeterminate loading states.

Loading indicators
<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.

Avatar patterns
SM
AC
MW
PN
AC
MW
JD
KR
TM
+2
<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.

Toast variants
Saved
Your changes have been saved.
Upload failed
File exceeds size limit.
<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.

Page controls
<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.

Skeleton card
<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.

Callout variants
Default callout
Use callouts for persistent notes that should remain visible.
Info
This endpoint is rate limited to 100 requests per minute.
Warning
Migrating now may restart active sessions.
<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.

Settings list
Account
Profile Preferences Notifications
<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.

KPI cards
Revenue
$84.2k
12.5% MoM
Churn
2.4%
0.3% MoM
Uptime
99.9%
No change
<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.

No data placeholder
📂
No files yet
Upload your first file to begin collaborating with your team.
<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.

Horizontal + vertical stepper
Account
Profile
Billing
Review
Order placed
Shipped
Delivery failed
<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.

Right drawer
<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.

Shortcut tokens

Open command palette with ⌘ K.

CtrlShiftPEscEnterTab
<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
Utility composition example
Utility-first card shell

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)
Dashboardlayout-dashboard
Analyticsbar-chart-3
Usersusers
Ordersshopping-cart
Billingcredit-card
Audit Logshistory
Support Ticketslife-buoy
Integrationsplug-zap
Settingssettings
Library Docsbook-open
Showcasesparkles
Menu togglemenu
Search fieldsearch
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
Buttons, pills, badges, tags
<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>
Tabs, dropdowns, list items, breadcrumb
<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.

Loading the runtime
<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

AttributeOnEffect
data-lume-toggle + data-lume-targetAny buttonOpens or closes the target modal, drawer or dropdown. Use data-lume-toggle="dropdown" on menu triggers.
data-lume-open / data-lume-closeAny buttonOpens 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 toastRemoves the toast. .lume-toast-close does the same without the attribute.
data-lume-dismiss="alert"Button in an alert or calloutRemoves the alert or callout.
data-lume-removeButton in a tag, badge or chipRemoves the nearest tag, badge or chip, or the nearest ancestor matching the attribute's selector.
data-lume-autodismiss.lume-toastMilliseconds before removal (default 4500), or "false" to keep it. Hovering pauses the remaining time.
data-lume-overlay.lume-drawerSelector for the drawer's overlay when it is neither the drawer's parent nor its sibling.
data-lume-panel.lume-tabId of the tab's panel when panels are not in matching order. aria-controls works too.
Selectors in these attributes are written by you, so the runtime never trusts them: an invalid selector logs a console warning and is ignored instead of throwing.

API reference

MemberArgumentsReturnsNotes
Lume.open(target, trigger?)Selector or element; optional trigger elementvoidFocus returns to trigger on close, or to whatever was focused before opening.
Lume.close(target)Selector or elementvoidRestores focus for dialogs.
Lume.toggle(target, trigger?)Selector or elementvoidSame as open or close.
Lume.isOpen(target)Selector or elementbooleanfalse if the target doesn't resolve.
Lume.initTabs(root?)Element, selector or documentvoidWires tabs inserted after page load. Tabs present at load are wired automatically.
Lume.initToasts(root?)Element, selector or documentvoidRarely needed: toasts added to the page are picked up automatically.
Lume.dismissToast(toast)Selector or elementvoidRemoves the toast and cancels its timer.
Lume.version—stringPackage version the runtime was built from.
API usage
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.

Componentlume.js providesYou provide
Modal and drawerFocus 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.
DropdownImplements 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.
Tabstablist, 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.
ToastAuto-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.
TooltipNothing; 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.
Accessible dropdown menu
<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>
Announced toasts
<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">&times;</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
Recommended QA: navigate docs with keyboard only, run Lighthouse accessibility audit, and test with reduced motion enabled at the OS level.