Theming
Built-in light and dark, custom themes that inherit a palette, and the controller that switches between them.
A theme is a palette of ten colour roles. AsheeUI ships two, light and dark, and an
application adds its own by declaring them in the configuration. Nothing about a theme is a
stylesheet: the palette is resolved from the configuration, written into CSS variables as
--ashee-*, and selected by one class on the document root.
The two built-in themes
defaultTheme states what a first-time reader sees. It takes a theme name, or "system",
which follows prefers-color-scheme.
export default {
defaultTheme: "system",
};
A reader's choice is remembered in localStorage under ashee-theme, and it wins over
defaultTheme on later visits. themeController and useTheme are the API for changing
it, see Switch a theme.
A custom theme
A custom theme is a palette that inherits one of the built-ins and states only what it
changes. extends names the palette it inherits. The name is your own string, it needs no
declaration, and the palette key, defaultTheme and themeController.setTheme all accept it:
import type { ExternalConfig } from "@asheeui/core";
export const config: ExternalConfig = {
defaultTheme: "system",
color: {
midnight: {
extends: "dark",
background: "#0b0b12",
primary: "#8b5cf6",
border: "#2d2a45",
},
},
};
The existence of a theme is checked at runtime rather than at compile time: a defaultTheme
naming a theme the configuration does not declare is rejected when the provider renders. So the
declaration and its use have to agree, and the compiler will not catch a typo for you.
Three things follow from that declaration:
- The seven roles it does not state are inherited, so a change to the built-in
darkpalette reaches it. - The inheritance is from the merged palette, not from the palette that shipped with
the library, an application that also overrides
darkgets its own values here. - It is a theme like any other: it appears in
availableThemes, is selectable, is remembered, and gets.theme-midnighton the document root.
A custom theme with no extends inherits the family of defaultTheme when that is light
or dark; with defaultTheme: "system" it inherits light, because a system default has
no single family to inherit.
Switch a theme
useTheme reads the controller and re-renders when it changes.
| Field | Meaning |
|---|---|
theme |
The selection: a theme name, or "system" |
resolvedTheme |
What the selection resolves to, system becomes light or dark |
availableThemes |
Every theme the application declared |
setTheme |
Choose a theme and persist the choice |
toggleTheme |
Advance to the next available theme, for a single-control switch |
What the controller puts on the page
The controller does two things, both before the first paint:
- It applies
theme-<name>to the document root, removing any othertheme-*class. - It writes a stylesheet of
--ashee-*variables, one block per theme.
The stylesheet is built from every provider's configuration rather than from the last one to mount, so a provider nested inside the application, a page demonstrating a different configuration, for instance, can add a theme but never remove one. If you have seen a custom theme resolve to the light palette, that is the failure this rule prevents.
Themes and color-scheme
The applied theme is also written to the root's color-scheme, so the browser's own
surfaces, scrollbars, form controls, the caret, follow the theme rather than the operating
system. It is written wherever the theme is applied: by the pre-paint script on first load, and
by the controller on every later switch, so a runtime change moves the browser's surfaces with it.
Server rendering
The provider renders a small script that applies the class and injects the variables while the server HTML is parsed, so a dark application does not flash light before hydration. A client-only render cannot run a script React creates, so the provider applies the same values itself in a layout effect, still before the first paint.
Only the outermost provider emits that script. A provider nested inside the application, a
page demonstrating its own configuration, still adds its themes to the controller and to the
stylesheet, but it is not the document's writer: the application's provider owns the pre-paint
class, the color-scheme and the document's default theme.