ColorScheme[] type ColorScheme[]
import {color_schemes} from '@fuzdev/fuz_css/theme.js'; 10 declarations
ColorScheme[] type ColorScheme[]
import {color_schemes} from '@fuzdev/fuz_css/theme.js'; ColorScheme type "dark" | "light" | "auto"
import type {ColorScheme} from '@fuzdev/fuz_css/theme.js'; (base: { name: string; variables: { name: string; light?: string | undefined; dark?: string | undefined; summary?: string | undefined; }[]; summary?: string | undefined; scheme?: "dark" | "light" | "dual" | undefined; scheme_mirror?: { ...; }[] | undefined; }, ...overlays: { ...; }[]): { ...; } import {compose_themes} from '@fuzdev/fuz_css/theme.js'; Composes a base theme with overlay fragments by flatten + last-wins: later
variables replace same-named earlier ones wholesale, a dark-only overlay
keeping the light slot beneath it (see overlay_style_variable). Any
knob-only theme is already a valid fragment - the contrast modifiers in
contrast_modifiers are the canonical overlays. This is the hand-flatten
precursor to a first-class extends, with the same merge semantics.
The base's scheme stance wins; when the base is single-scheme, each
overlay variable is re-slotted to the stanced scheme's value so a
dual-slot fragment can't leak the other scheme's appearance past the
stance. The base's scheme_mirror carries through minus any names the
overlays set - the mirror was computed against the base's own variables,
so entries for newly composed names would shadow nothing but still render
The composed name appends the overlay names so name-keyed pickers and
renderers treat the composition as its own theme, and the base's
summary carries through.
baseoverlaysTheme[]"fuz.theme.baked" import {FUZ_BAKED_THEME_LAYER} from '@fuzdev/fuz_css/theme.js'; The sublayer a build-time theme's own overlay renders into. A layer's
direct styles outrank its sublayers, so a runtime theme rendered into
fuz.theme wins over the baked one whatever their selectors' specificity,
while the baked overlay still sits above fuz.preferences.
"@layer fuz.base, fuz.preferences, fuz.theme, fuz.utilities;" import {FUZ_LAYER_ORDER_STATEMENT} from '@fuzdev/fuz_css/theme.js'; The fuz_css cascade layer order: defaults (variables + element styles) in
fuz.base, OS user-preference mappings (prefers-contrast,
prefers-reduced-motion) in fuz.preferences, theme overrides in
fuz.theme, generated utility classes in fuz.utilities. Layer order
beats specificity and source order, so the preference mappings win over
the defaults, theme overrides win over both regardless of head insertion
order, and consumers' unlayered styles beat everything.
Two deliberate exceptions, both !important declarations in a layer, which
outrank every later layer and unlayered styles: the prefers-reduced-motion
mapping (a theme's durations must not re-enable motion the user turned off)
and [hidden].
(existing: { name: string; light?: string | undefined; dark?: string | undefined; summary?: string | undefined; } | undefined, overlay: { name: string; light?: string | undefined; dark?: string | undefined; summary?: string | undefined; }): { ...; } import {overlay_style_variable} from '@fuzdev/fuz_css/theme.js'; Overlays one variable onto the same-named variable beneath it, the merge
every theme composition shares: the overlay replaces wholesale, except that
a dark-only overlay keeps the light slot beneath it. A dark slot only
shadows under .dark, so dropping the light slot would leave the variable
without its light-scheme value.
existingthe variable being overlaid, if any
StyleVariable | undefinedoverlaythe variable that wins
(v: { light?: string | undefined; dark?: string | undefined; } | undefined, stance: "dark" | "light"): string | undefined import {pick_stance_slot} from '@fuzdev/fuz_css/theme.js'; Picks the value a single-scheme stance renders from a dual-slot shape: the stanced scheme's slot, dark falling back to the light/base position. Shared by compose_themes and the theme editor so the re-slot semantics can't drift.
v{ light?: string | undefined; dark?: string | undefined; } | undefinedstance"dark" | "light"string | undefined
(theme: { name: string; variables: { name: string; light?: string | undefined; dark?: string | undefined; summary?: string | undefined; }[]; summary?: string | undefined; scheme?: "dark" | "light" | "dual" | undefined; scheme_mirror?: { ...; }[] | undefined; }, options?: RenderThemeStyleOptions): string import {render_theme_style} from '@fuzdev/fuz_css/theme.js'; Renders a theme's variables as CSS, wrapped in the fuz.theme cascade
layer by default.
Renders exactly what the theme carries - an empty variables array renders
nothing (inheriting the fuz.base defaults) unless a scheme stance needs
pinning. To render the full default set, pass it: `render_theme_style({name:
'base', variables: default_variables})`. To render a single-scheme theme
faithfully, resolve it through resolve_theme_stance first.
Total over any JSON value, because a theme restored from storage reaches
this without passing the schema: whatever isn't the type Theme declares
is dropped instead of thrown on or coerced into the stylesheet. A
variables or scheme_mirror that isn't an array renders as empty, an
entry that isn't an object is skipped, a name or slot that isn't a
contained string drops its declaration, and a summary that isn't one drops
its comment (see css_containment.ts).
themethe theme to render
options{}string the theme CSS, or an empty string when there's nothing to render
RenderThemeStyleOptions import type {RenderThemeStyleOptions} from '@fuzdev/fuz_css/theme.js'; comments?type boolean
id?Scopes the rendered variables to #id instead of :root. Dark slots
render for both placements of the scheme class - on the scope element
itself (#id.dark) and on the root (:root.dark #id), the ecosystem
convention - so a scoped theme's dark appearance follows the page's.
Only literal-valued variables take effect in a scope: the derived color
stops (--palette_a_50, --shade_50, ...) resolve their calc() on
:root, and a descendant inherits the computed color, so a curve knob
like --chroma_scale set at #id changes nothing. Knob-only themes
need :root.
Any string works: it's escaped into the selector, so it matches the
element whose id attribute is that string and can't end the rule or
the <style> element. An empty string renders to :root.
type string | null
layer?The cascade layer wrapping the rendered variables. Theme overrides
default to fuz.theme so they beat the fuz.base defaults by layer
order; pass null to render unlayered.
type string | null
(scheme: unknown): "dark" | "light" | null import {to_theme_stance} from '@fuzdev/fuz_css/theme.js'; Reads the single-scheme stance a theme's scheme names, or null for a
dual-scheme theme. Total over any value, so it reads unvalidated data too.
schemea theme's scheme
unknown"dark" | "light" | null