Theming and color scheme
Every visual value in Oblyx (color, spacing, radius, shadow, typography, motion) is a CSS custom property, prefixed --ox-. Components read these tokens; they never hardcode a color or a size. Change a token on :root and every component that reads it changes with it, with no component-level configuration.
Two attributes, two independent axes
Oblyx puts two attributes on the document root, and they control different things. Neither is required, both have a working default with zero attributes present.
data-color-scheme:"light"or"dark". Selects which half of the color and shadow tokens apply. Absent means "follow the OS setting."data-oblyx-theme: the active theme's name, only needed when a theme stylesheet is loaded. Absent means the default look, not a theme called"default". See "Themes" below.
<html data-color-scheme="dark" data-oblyx-theme="neutral"></html>How light and dark are selected
data-color-scheme is resolved in three tiers, each one able to override the last:
- Default. With no attribute set and no OS preference detected, every color and shadow token resolves to its light value. This is the plain
:rootbaseline intokens.css. - OS preference. A
prefers-color-scheme: darkmedia query redefines the same tokens for dark, but only while the page hasn't explicitly opted into light. The media query is guarded with:root:not([data-color-scheme="light"])so it can never win over an explicit choice. - Explicit attribute.
:root[data-color-scheme="dark"]always wins, over both the default and the OS preference, because it's the most specific selector and the last one in the cascade.
Net effect: explicit attribute beats OS preference beats light default. A page needs no JavaScript beyond setting or clearing data-color-scheme to move between all three states. This docs site's own light/dark switch, in the header, does exactly that: it toggles data-color-scheme on <html> and remembers the choice in localStorage. This page has no live components on it, but every one on the component pages reads the same token layer described below, so try the toggle there to see it in effect.
Themes
A theme is a second stylesheet that redeclares a set of --ox-* tokens, scoped to [data-oblyx-theme="name"] with @scope. Applying one is a link tag plus an attribute, nothing else:
<link rel="stylesheet" href=".../oblyx.css" />
<link rel="stylesheet" href=".../themes/neutral.css" />
<html data-oblyx-theme="neutral"></html>Load the theme's stylesheet after oblyx.css, set data-oblyx-theme to the theme's name on <html>, and every component on the page picks up the theme's tokens with no component-level change. There's no build step and no theme registry to configure: the attribute and the stylesheet are the whole mechanism.
The default look, the one every other page on this site uses, is not a theme named default. It's what a page gets with no theme stylesheet loaded and no data-oblyx-theme attribute set at all: tokens.css and theme.css already are that baseline. There is no default.css to link and no data-oblyx-theme="default" value that does anything.
Which themes exist
Astryx, the design system Oblyx ports, ships seven named themes. Oblyx ships two so far, neutral and gothic, shown below. The remaining five are not built yet. There's no committed date for when, or whether, the rest arrive: the point of shipping two now is to prove the mechanism works for both a theme that supports light and dark and one that doesn't (see "Gothic is dark-only" below), not to promise a full set on a schedule.
Themes are token-only
Each Astryx theme also carries a set of hand-tuned per-component overrides beyond its tokens: a filled badge that locks its background to a fixed tone instead of inverting with the rest of the categorical palette, a card variant that re-scopes its own text color, and similar refinements that a plain token swap doesn't reproduce. Oblyx's neutral.css and gothic.css port the token layer only: color, typography, radius, motion, and shadow. Every component still reads var(--ox-color-*) and friends, so it recolors correctly under either theme, but it won't match Astryx's own neutral or gothic pixel-for-pixel. If you compare the two side by side and something looks close but not identical, this is why, not a bug.
Gothic is dark-only
gothic.css writes every color as a single literal value, never light-dark(). Setting data-color-scheme="light" alongside data-oblyx-theme="gothic" changes nothing: gothic has no light half to switch to. This matches Astryx's own gothic theme, which is dark-only upstream too. If a page using gothic looks unaffected by the light/dark toggle, this is why.
Cost
Each shipped theme file is about 1.4 KB gzipped, a second, independent request from oblyx.css. A page that never links a theme's stylesheet pays nothing for its existence, the same "opt in or pay nothing" rule as everything else in this library.
Seeing the difference
Screenshots go stale; these three are real pages, each loading oblyx.js and oblyx.css (plus, for the themed two, the matching theme stylesheet), exactly as described above. Each has its own light/dark toggle, so the theme and the color scheme can be checked independently.
neutralgothicOverriding a token
Because every value is a plain custom property, overriding one is plain CSS: redeclare it at a scope that beats Oblyx's own declaration. The whole token catalog is visible in node_modules/oblyx/dist/oblyx.css (or packages/oblyx/src/styles/tokens.css in this repo), there's no separate reference document to look up a name from.
:root {
--ox-color-accent: #7952ff;
}Loaded after oblyx.css, this repaints every accent-colored surface across the whole page: button primaries, focus rings, checked states, the works, because they all read --ox-color-accent rather than a hardcoded blue. No build step, no component prop, no theme file required for a one-token tweak.