Theming & Runtime Controls
Lucent's theming is pure CSS cascade. Values live in React context (via LucentProvider), but propagation is a set of :root attributes and CSS custom properties — so changing a theme axis repaints every surface instantly with no React re-render.
The four runtime axes
| Axis | Values | Mechanism |
|---|---|---|
| Theme | dark (default), light | data-theme on :root |
| Accent | cyan (default), violet, teal | data-accent on :root |
| Density | airy (default), balanced, compact | data-density on :root |
| Glass | opacity 0.60–1.0, blur 0–24px | --lucent-glass-opacity / --lucent-glass-blur |
Plus a contrast axis (default / solid) for a fully opaque, high-contrast rendering — see Transparency & Solid Mode.
The prebuilt panel
<ThemePanel> exposes every axis with no wiring. Import its styles and drop it in:
import { LucentProvider, ThemePanel } from '@orionshub/lucent'
import '@orionshub/lucent/styles.css'
import '@orionshub/lucent/theme.css'
export function Settings() {
return (
<LucentProvider>
<ThemePanel />
</LucentProvider>
)
}Imperative setters
Build your own controls with the guarded setters. They are SSR-safe (no-op without document), clamp/validate their input, and write a single CSS var or attribute — never triggering a re-render:
import {
setTheme,
setAccent,
setDensity,
setGlassOpacity,
setGlassBlur,
setContrast,
} from '@orionshub/lucent/theme'
setTheme('light') // 'dark' | 'light'
setAccent('violet') // 'cyan' | 'violet' | 'teal'
setDensity('compact') // 'airy' | 'balanced' | 'compact'
setGlassOpacity(0.85) // clamped to 0.60–1.0
setGlassBlur(8) // clamped to 0–24 px
setContrast('solid') // 'default' | 'solid'Unknown enum values are silently rejected — a mitigation against
localStorageinjection when you persist and replay a stored theme.
Reading the current axes
Inside a LucentProvider, useLucent() returns the current axis values:
import { useLucent } from '@orionshub/lucent'
function Readout() {
const { theme, accent, density, glassOpacity, glassBlur, contrast } = useLucent()
return <pre>{JSON.stringify({ theme, accent, glassOpacity }, null, 2)}</pre>
}Token overrides
Tokens are two-tier: primitive (raw palette) → semantic (role-based). Override either tier with your own CSS custom properties scoped to :root or a subtree:
:root {
--lucent-accent: 265 90% 66%; /* your brand accent (HSL channels) */
}