Guide
Theming
Five dimensions on the root element — values, defaults, auto-resolution, and how to override tokens yourself.
The whole look hangs on five attributes written to the root element (<html>): data-theme, data-accent, data-scene, data-density, data-contrast. The theme engine in @bysages/core writes them and persists; you can also set them by hand for static pages.
import { applyTheme } from "@bysages/core";
applyTheme({
mode: "dark", // "light" | "dark" | "system" (default "system")
accent: "zhusha", // "auto" | "ink" | "qinghua" | "celadon" | "zhusha" | "feicui" | "jilan" | "qingjin" (default "auto")
scene: "civic", // "auto" | civic | enterprise | studio | tech | cupertino | expressive | fluent | material | sketch | missive | dispatch | metric | new-york
density: "spacious", // "compact" | "default" | "comfortable" | "spacious" (default "default")
contrast: "high", // "auto" | "normal" | "high" (default "auto")
});
The call merges with the current theme, writes the attributes, and returns the full Theme. Persistence goes to localStorage key bs-theme (opt out with persist: false); initTheme() restores it and keeps mode: "system" tracking the OS.
#Mode — paper and lacquer
data-theme="light" is the paper; data-theme="dark" is warm lacquer-black — never #000 — with the same hue relationships lifted to brighter steps. "system" resolves through prefers-color-scheme at apply time.
#Accent — the mineral pigments
data-accent lays a pigment under primary action blocks and the focus halo. Semantic colors — success, warning, danger, info — are fixed and never move with the accent.
accent | Reads as | Note |
|---|---|---|
ink (or unset) | monochrome ink | the default solemn primary |
qinghua | cobalt blue | 青花 |
celadon | pale water green | 天水碧 |
zhusha | cinnabar red | seal-paste red, civic identity |
feicui | jade green | 翡翠, the handheld register's pigment |
jilan | azure blue | 霁蓝, the office register's pigment |
qingjin | lapis blue | 青金, the metric register's pigment |
auto | the scene's pairing | see the table below |
#Scene — thirteen temperaments
data-scene retunes shape, density, motion, and the direction of light across the whole interface — no component forks. Two families:
scene | Name | Serves | Paired accent | Paired contrast |
|---|---|---|---|---|
civic | 典章 | elder-facing and government desktops — square corners, 48px targets | zhusha | high |
enterprise | 信笺 | quiet commercial long sessions | qinghua | normal |
studio | 雅集 | design and editorial, literati whitespace | celadon | normal |
tech | 司南 | modern precision and throughput | ink | normal |
cupertino | 圆融 | soft, ethereal handheld register | qinghua | normal |
expressive | 飞白 | playful, motion-first | zhusha | normal |
fluent | 流水 | restrained productivity suite | qinghua | normal |
material | 格物 | elevation register, layered by shadow | celadon | normal |
sketch | 写意 | hand-drawn charm | zhusha | normal |
missive | 家书 | the familiar handheld register | feicui | normal |
dispatch | 公牍 | the office handheld register | jilan | normal |
metric | 格律 | the metric enterprise register | qingjin | normal |
new-york | 玄素 | the monochrome developer register | ink | normal |
Where each register comes from. The audience scenes — civic, enterprise, studio, tech — are our own desks. The style scenes are homages to one design language each, renamed to a common word so the API stays ours; the lineage is the point, so it is stated plainly:
scene | Borrows the temperament of |
|---|---|
cupertino 圆融 | the iOS design language — soft, airy, handheld |
expressive 飞白 | spring-motion-led mobile design |
fluent 流水 | Fluent, the measured productivity register |
material 格物 | Material, the elevation-led layered register |
missive 家书 | the familiar phone register of open mobile style specs (WeUI) |
dispatch 公牍 | WeUI for Work, the same register dressed for the office desk |
metric 格律 | TDesign, the metric enterprise design system |
| sketch 写意 | hand-drawn sketchbooks and comics |
new-york 玄素 | shadcn/ui, the monochrome developer register (its own style name) |
applyTheme({ scene }) applies the pairing automatically; an explicitly set accent or contrast always wins over the pairing — applyTheme({ scene: "civic", accent: "qinghua" }) keeps civic geometry and speaks in cobalt. The pairing lives in SCENE_DEFAULT_ACCENT / SCENE_DEFAULT_CONTRAST if you need it in code.
#Density and contrast
data-density scales whitespace only — gaps, paddings, control spacing — never type size or contrast. data-contrast="high" lifts secondary text and hairlines to primary strength and deepens the focus halo: the same design, louder.
#Overriding tokens
Themes select values; your CSS can replace them. Tokens live in @layer bs.tokens, so an unlayered override wins without specificity games:
:root {
--bs-radius-sm: 4px; /* square-cut the seals further */
--bs-color-primary: var(--bs-color-ink);
}
Scope an override by mounting the attributes on a subtree instead of <html> — the attributes cascade, so a data-scene="studio" panel can sit inside a civic page. getTheme() reads the current logical theme; initTheme() is the whole boot for restore-and-follow-OS behavior.