121
GitHub
Getting started

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.

121components
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="you@example.com" />
  <Button>Subscribe</Button>
</Card>
---
import { Button, Card, Input } from '@santi020k/lumen-astro'
---

<Card>
  <label for="email">Email</label>
  <Input id="email" type="email" placeholder="you@example.com" />
  <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 emits the shared sizing attributes for app-level adapters.
  • Use React hooks such as useDialog, usePopover, useDropdownMenu, useContextMenu, useTabs, useSelect, useFormValidation, useCalendar, useInputOTP, useDateRangePicker, useRichTextEditor, useSchedule, useResizable, useThemeBuilder, 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, resizable pane sizing, ThemeBuilder export, VirtualList range events, overlays, select, tabs, tooltip, and toast.
  • 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 "tailwindcss";
@import "@santi020k/lumen-astro/styles.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.

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.

Glass

Translucent by design

Shared tokens control blur, saturation, borders, highlights, and shadow depth across light and dark themes.

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

Complete

@santi020k/lumen-astro

Full component catalog, shared CSS, and the UIPrimitives runtime.

React

Adapter

@santi020k/lumen-react

React components for the full primitive catalog using the shared class and data contract.

Elements

Adapter

@santi020k/lumen-elements

Standards-based custom elements for the full primitive catalog using the shared class and data contract.

Core

Shared

@santi020k/lumen-core

Design tokens, component metadata, and small adapter utilities.

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.

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.