Light and dark via data-theme on <html> — override
semantic aliases, not primitives.
Dark is the default. Set data-theme="dark" or
data-theme="light" on the document element. Semantic tokens under
html[data-theme="light"] remap purpose aliases; primitive scales stay
unchanged.
Use the theme toggle in the corner of this page to preview light and dark live —
the same data-theme attribute your app should set. Recolor the whole
system from Examples → Colors with
--ca-hue.
Semantic color is OKLCH with a shared brand hue. Default is macOS-adjacent
254. Set --ca-hue and optionally
--ca-chroma (a multiplier, default 1) on
:root or html after Caustica CSS loads.
--ca-chroma: 0 is Ink — grayscale brand, same light and dark surfaces.
Status tokens start on the shipped ok, warn, danger, and info hues. They keep their own
chroma so warnings still read on Ink. Retune with the examples meters or optional
--ca-hue-ok, --ca-hue-warn, --ca-hue-danger, or
--ca-hue-info.
The examples style guide paints these knobs live — wheel, system picker, screen
eyedropper (other windows or another browser tab in Chromium), or a screenshot with
movable pins on the pixels we sampled. Copy the CSS snippet from that panel into a
host file imported
after caustica-design/css. Same recipe on
Getting started → Brand palette.
/* Host file imported after caustica-design/css */
:root {
--ca-hue: 192;
--ca-chroma: 1;
}
Semantic glass reads three host knobs: --ca-blur-glass (frost, default
32px), --ca-glass-fill (surface alpha multiplier, default
1), and --ca-glass-edge (border / highlight / rim multiplier,
default 1). Set them on :root or html after
Caustica CSS loads. Fill and edge stay in family with light and dark recipes — they
scale the shipped alphas, they do not replace them.
The examples style guide paints these meters live. Copy the CSS snippet from that
panel into a host file imported after
caustica-design/css. Same recipe on
Getting started → Glass knobs.
/* Host file imported after caustica-design/css */
:root {
--ca-blur-glass: 24px;
--ca-glass-fill: 0.8;
--ca-glass-edge: 1.1;
}
<html lang="en" data-theme="dark">
<!-- or data-theme="light" -->
</html>
Static multipage sites ship with a default data-theme in HTML. Restoring
theme only after deferred JS runs paints dark first. Skeletons and spinners do not fix
that. Put a tiny blocking script in <head> that sets
data-theme synchronously. This docs site uses
data-ca-theme-boot: it restores theme only after a consent choice, reading
optional localStorage (Accept) or tab-only sessionStorage (Accept or Essential). See
Cookies.
useTheme, setTheme, getTheme,
toggleTheme, and toggleThemeFromEvent live on
caustica-design/core. They read and write
document.documentElement’s data-theme.
import { setTheme, useTheme, toggleThemeFromEvent } from 'caustica-design/core'
function ThemeToggle() {
const [theme] = useTheme()
return (
<button type="button" onClick={toggleThemeFromEvent}>
{theme === 'dark' ? 'Light' : 'Dark'}
</button>
)
}
// Imperative (no animation)
setTheme('light', { immediate: true })
// Circular reveal from a point
setTheme('dark', { origin: { x: 24, y: 24 } })
On desktop, toggling theme runs a circular reveal from the control via the View
Transitions API when available. The wipe uses a reused stylesheet with baked
clip-path coordinates on ::view-transition-new(root).
On phones and coarse-pointer devices (Chrome Android, Samsung Internet), the
circular wipe is skipped — those engines pin the reveal to the top-left, hide
fixed glass menus mid-transition, and stutter on full-page snapshots. Theme
swaps immediately with the icon nudge only. CSS in motion.css:
html[data-theme-to] marks the transition; JS injects
literal clip-path keyframes from the toggle
.is-theme-leaving /
.is-theme-entering — opacity only (no shell transform)
.is-theme-toggling on the toggle buttonprefers-reduced-motion: reduce and mobile / coarse pointer skip
the wipe
Try the theme toggle in the corner — circular reveal on desktop; instant swap on phones.
Theme tokens ship with both CSS entries. Pick the surface your host needs:
caustica-design/css/foundation — fonts, token layers, and body chrome (no
component classes)
caustica-design/css — foundation plus buttons, panels, pickers, and the rest
Importing only token files without foundation/base drops body font, canvas glow, and grain. For brand remaps, see Tokens → override.
Wrap the host UI in .ca-root. Prefer fewer stacked
backdrop-filter layers; dense screens can lower
--ca-blur-glass / --glass-blur, or drag the meters in the
examples glass studio.
import 'caustica-design/css/foundation'
// or full system:
import 'caustica-design/css'