Skip to main content
196

Search Lumen

Find components, APIs, guides, and recipes.

GitHub
Web docs
Web documentation

Build once. Use Lumen everywhere.

Semantic markup, one token-driven stylesheet, and a small progressive-enhancement runtime. Pick a framework below; the markup and class contract stay the same everywhere.

196components
3framework targets
1shared stylesheet

Glass system

Built from Lumen primitives
Live
AstroReference implementation@santi020k/lumen-astro
ReactAdapter primitives@santi020k/lumen-react
ElementsCustom elements@santi020k/lumen-elements
1

Install a target package

Add Astro, React, or Elements; every target shares the same stylesheet package.

2

Load styles once

Import the standalone stylesheet from a global CSS file, root layout, or app entry.

3

Build on one contract

Markup, tokens, glass surfaces, and runtime behavior stay identical across targets.

Start here

Install and use

pnpm

bash
pnpm add @santi020k/lumen-astro
pnpm add @santi020k/lumen-astro

npm

bash
npm install @santi020k/lumen-astro
npm install @santi020k/lumen-astro

yarn

bash
yarn add @santi020k/lumen-astro
yarn add @santi020k/lumen-astro

With the stylesheet loaded globally, import components where you use them. Mount UIPrimitives once in your root layout only if you use interactive primitives; do not add it beside every component.

astro
---
import { Button, Card, Input } from '@santi020k/lumen-astro'
---

<Card>
  <label for="email">Email</label>
  <Input id="email" type="email" placeholder="[email protected]" />
  <Button>Subscribe</Button>
</Card>
---
import { Button, Card, Input } from '@santi020k/lumen-astro'
---

<Card>
  <label for="email">Email</label>
  <Input id="email" type="email" placeholder="[email protected]" />
  <Button>Subscribe</Button>
</Card>

Framework contracts

Framework-specific guidance

Lumen keeps one visual contract, but each target has its own integration surface for behavior, local wrappers, and registration.

Astro

@santi020k/lumen-astro
  • Reference implementation for markup, props, standalone CSS, and progressive enhancement.
  • Mount UIPrimitives once in the root layout for interactive primitives and CustomEvents.
  • Use lumen add Component or lumen add recipe-name --target astro when you want local .astro starter files.

React

@santi020k/lumen-react
  • React components mirror the same ui-* classes, data attributes, and prop names where React naming allows it.
  • DataTable renders the same structured row contract and VirtualList provides built-in fixed-height windowing.
  • Use React hooks such as useDialog, usePopover, useDropdownMenu, useContextMenu, useTabs, useSelect, useFormValidation, useCalendar, useInputOTP, useDateRangePicker, useRichTextEditor, useSchedule, useKanban, useResizable, useThemeBuilder, useThemeToggle, useToast, and useTooltip for behavior-heavy primitives.
  • Use lumen add Component --target react or lumen add recipe-name --target react when you want local .tsx starter files.

Elements

@santi020k/lumen-elements
  • Custom elements expose the shared class and data contract through lumen-* tags that work in plain HTML or any framework.
  • Call defineLumenElements once before using the tags; behavior-backed elements include DataTable selection/sort, form validation, calendar grids, OTP segmentation, date range syncing, rich text commands, context menu triggers, schedule drag/drop and file drag/drop, tours, anchor scroll spy, transfer, mentions, cascader and tree selection, resizable pane sizing, ThemeBuilder export, VirtualList range events, overlays, tabs, tooltip, and toast.
  • For Badge, Button, and Card-only surfaces, use their @santi020k/lumen-elements/components/* entrypoints to avoid bundling the complete catalog.
  • Use lumen add Component --target elements or lumen add recipe-name --target elements when you want local Elements starter files.

Shared stylesheet

Add styles once

Lumen ships standalone CSS. Load it once from a global CSS file, root layout, or app entry; Tailwind configuration is optional.

Astro + Tailwind

Use the stylesheet exported by your framework package in your main CSS entry.

css
@import "@santi020k/lumen-astro/layers.css";
@import "tailwindcss";
@import "@santi020k/lumen-astro/styles.css";
@import "@santi020k/lumen-astro/layers.css";
@import "tailwindcss";
@import "@santi020k/lumen-astro/styles.css";

Astro layout

Use this in an Astro root layout when you do not already have a shared CSS entry.

astro
---
import '@santi020k/lumen-astro/styles.css'
---

<html lang="en">
  <body>
    <slot />
  </body>
</html>
---
import '@santi020k/lumen-astro/styles.css'
---

<html lang="en">
  <body>
    <slot />
  </body>
</html>

App entry

Use this in a bundled app entry, such as main.tsx, main.ts, or client.ts.

ts
import '@santi020k/lumen-react/styles.css'

import { createRoot } from 'react-dom/client'

import { App } from './App'
import '@santi020k/lumen-react/styles.css'

import { createRoot } from 'react-dom/client'

import { App } from './App'

Token contract

Themes

Color tokens live on the root data-theme attribute. Light and dark ship built in, and any brand can be expressed through the same token contract.

Light · same components, different tokens
Workspace settingsActive
Dark · same components, different tokens
Workspace settingsActive

Illustrative previews · buttons do not save data. Try your own palette in the theme playground.

Default themes

Omit data-theme or set data-theme="light" for the default light theme; set data-theme="dark" for the built-in dark theme.

html
<html data-theme="light">
  <body>...</body>
</html>

<html data-theme="dark">
  <body>...</body>
</html>
<html data-theme="light">
  <body>...</body>
</html>

<html data-theme="dark">
  <body>...</body>
</html>

Runtime switch

Switch themes by updating document.documentElement.dataset.theme; components read the CSS variables from the root element.

ts
const nextTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
  ? 'dark'
  : 'light'

document.documentElement.dataset.theme = nextTheme
const nextTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
  ? 'dark'
  : 'light'

document.documentElement.dataset.theme = nextTheme

Custom theme

These docs run santi020k-light and santi020k-dark, a custom theme family built on the same token contract. Copy the shape and adapt the values to your brand.

css
@import "@santi020k/lumen-astro/styles.css";

:root[data-theme="santi020k-light"] {
  color-scheme: light;
  --canvas: 268 20% 98%;
  --surface: 268 20% 100%;
  --ink: 268 10% 20%;
  --brand: 264 92% 47%;
  --accent: 264 95% 57%;
  /* ...override the remaining tokens */
}

:root[data-theme="santi020k-dark"] {
  color-scheme: dark;
  --canvas: 260 43% 8%;
  --surface: 260 30% 12%;
  --ink: 260 10% 88%;
  --brand: 264 90% 58%;
  --accent: 264 90% 68%;
  /* ...override the remaining tokens */
}
@import "@santi020k/lumen-astro/styles.css";

:root[data-theme="santi020k-light"] {
  color-scheme: light;
  --canvas: 268 20% 98%;
  --surface: 268 20% 100%;
  --ink: 268 10% 20%;
  --brand: 264 92% 47%;
  --accent: 264 95% 57%;
  /* ...override the remaining tokens */
}

:root[data-theme="santi020k-dark"] {
  color-scheme: dark;
  --canvas: 260 43% 8%;
  --surface: 260 30% 12%;
  --ink: 260 10% 88%;
  --brand: 264 90% 58%;
  --accent: 264 90% 68%;
  /* ...override the remaining tokens */
}

Surface system

Glassmorphism surfaces

Glass is a surface treatment, not a theme: shared tokens control blur, saturation, borders, and highlights, with fallbacks for browsers without backdrop-filter support.

Workspace

Launch window

The same content on three surface treatments.

Solid surface<Card>
Workspace

Launch window

The same content on three surface treatments.

Subtle glass<Card glass="subtle">
Workspace

Launch window

The same content on three surface treatments.

Strong glass<Card glass="strong">

Astro card

Use the glass prop for standalone content surfaces.

astro
<Card glass>
  <h2>Launch window</h2>
  <p>Shared glass tokens.</p>
</Card>
<Card glass>
  <h2>Launch window</h2>
  <p>Shared glass tokens.</p>
</Card>

Overlay surface

Use the same glass prop for overlays without changing their semantics.

astro
<Popover glass>
  <Button data-ui-trigger>
    Invite
  </Button>
  <div>
    <Input type="email" />
  </div>
</Popover>
<Popover glass>
  <Button data-ui-trigger>
    Invite
  </Button>
  <div>
    <Input type="email" />
  </div>
</Popover>

Elements

Elements support glass with a boolean attribute.

html
<lumen-card glass>
  <h2>Production</h2>
  <p>Same class contract.</p>
</lumen-card>
<lumen-card glass>
  <h2>Production</h2>
  <p>Same class contract.</p>
</lumen-card>

Package map

Packages

Astro

Reference

@santi020k/lumen-astro

Reference implementation of the full primitive catalog, shared CSS, and progressive-enhancement runtime.

React

Full catalog

@santi020k/lumen-react

React implementation of the full primitive catalog using the shared class, token, and data contract.

Elements

Full catalog

@santi020k/lumen-elements

Standards-based implementation of the full primitive catalog using the shared class, token, and data contract.

Core

Shared foundation

@santi020k/lumen-core

Design tokens, component metadata, and utilities shared by every framework implementation.

Behavior layer

Progressive-enhancement runtime

Mount UIPrimitives once in your Astro root layout to wire every interactive primitive. Static components never need it, and component snippets assume the root layout already owns that runtime include.

Switch content

Try the tabs

Focus a tab and use the arrow keys to switch views.

Your workspace is ready.

No new activity today.

Tabs API and examples
Open an overlay

Try the dialog

Open help, press Escape to dismiss it, then check where focus returns.

Workspace help

Keep your team settings in one place.

Dialog API and examples
Native HTML behavior

Try the disclosure

Click the question or focus it and press Enter. This also works without JavaScript.

Does this need JavaScript?

Native details can open and close without the runtime.

Accordion API and examples

Copy a demo · Astro snippets assume styles and UIPrimitives are already mounted in your root layout.

astro
---
import { Tabs, TabsList, TabsPanel, TabsTrigger } from '@santi020k/lumen-astro'
---

<Tabs initialValue="overview">
  <TabsList aria-label="Workspace view">
    <TabsTrigger value="overview">Overview</TabsTrigger>
    <TabsTrigger value="activity">Activity</TabsTrigger>
  </TabsList>
  <TabsPanel value="overview">
    <p>Your workspace is ready.</p>
  </TabsPanel>
  <TabsPanel value="activity">
    <p>No new activity today.</p>
  </TabsPanel>
</Tabs>
More runtime behaviors

Dialogs and sheets

Trigger, dismiss, escape-key, and focus behavior for modal surfaces.

Menus and popovers

Lightweight disclosure patterns for contextual actions and floating content.

Tabs and command lists

Progressively enhanced keyboard navigation and client-side filtering.

Toasts and carousel controls

Document-level behavior for transient feedback and slide navigation.

Next steps

Every component page includes a live preview plus code for all three targets.

Web docsFull catalog