Theme architecture
Krysalicss ships three themes (default, dark,
high-contrast) — each one targeting a different surface
context. This page explains why each theme exists and which
trigger activates it. For the lookup-shaped contrast table see the
Themes reference; to author your own,
start at Author a custom theme.
default
The light, OS-default theme. Emits at :root with no media-query gate,
so it's the fallback whenever no other theme has opted in. White surface
(#ffffff) and near-black ink (#27282b) over a deep-accent palette
(sage, moss, amber, coral) with a single light tint (mint). Every accent
hits WCAG 2.2 AAA body text (≥ 7:1) against its white foreground
and WCAG 1.4.11 (≥ 3:1) against the white page so each component's
edge stays legible. The link token reuses sage (#1a6347, 7.2:1)
so brand identity, primary surface and link colour share the same
single source.
dark
The dark counterpart, hue-aligned with default so brand-feel carries
across themes. Two activation triggers are wired: the
prefers-color-scheme: dark media query (auto-switching when the OS is
in dark mode) and the explicit .theme-dark class (manual override
regardless of OS preference). Either path resolves to the same
declaration block.
high-contrast
Opt-in only: not auto-switched via prefers-contrast: more, because
forcing this aggressive palette on every user with a system-level
high-contrast preference is a worse default than leaving the choice
explicit. Every (bg, fg) pair clears WCAG 2.2 AAA (7:1+) and border
radius is zeroed so component edges stay legible at low vision.
Why three themes — and why opt-in only for the third
The bundled set is deliberately small. Each entry earns its place by targeting a context the other two can't cover:
defaultis the unconditional fallback. Removing it would mean every consumer has to opt into something before getting a working page — a worse first-run experience than shipping a neutral light baseline.darkrides on the most common runtime cue (prefers-color-scheme) and adds an explicit class override for users who want to overrule their OS. Auto-switching keeps the framework usable on every modern device without a JS theme switcher.high-contrastexists for low-vision and high-glare contexts. It is not auto-switched: forcing AAA-contrast colours on every user withprefers-contrast: moreswings too hard for many of them. The framework leaves that choice to consumers and to in-page toggles.
Where the gallery grows
The shipped themes are a starting kit, not a closed set. Community
themes ride on the same theme() mixin and the same required-keys
contract. The path from a custom palette to a published npm package is
covered end-to-end in
Author and publish a theme:
palette validation against WCAG, the theme() call shape, packaging,
and pnpm publish.
Further reading
- Themes reference — every shipped
theme's
(bg, fg)pairs and per-pair contrast ratio, plus forced-colors interaction. - Theme contract reference —
the required keys, the
theme()mixin signature, and the error semantics for incomplete maps. - Variable layers — the declarative / semantic / runtime split that lets themes re-tune everything without touching component source.
- Author a custom theme — the in-app, single-consumer recipe.
- Author and publish a theme — the full publish flow for community themes.