Theming #
The library is themed entirely through CSS custom properties. There are no
inline-style props for colours or dimensions. You override a token by setting it
on .stage-root (or any container inside it).
.my-editor {
--stage-color-bg: #1b1d22;
--stage-color-accent: #6cc;
--stage-border-radius: 4px;
}
Defaults are declared with :where(.stage-root) (specificity 0), so your
overrides win without !important. The library never writes !important
either.
// Per-instance via style:
<div class="stage-root" style={{ '--stage-color-accent': '#ff4488' }}>
Conventions #
- Every class is
stage-*. Every dimension and colour is a--stage-*variable. - Tokens derive from a few roots, parent-by-parent. The whole grey ground keys
off
--stage-color-bg(panel lifts off bg, header darkens off panel, border lifts off panel, the input well darkens off panel…). So retinting one root flows to its children, while pinning a single token stops the chain there. - Surface colours are opaque — no alpha (the one exception is
--stage-color-modal-backdrop, a scrim). Don't reintroduce alpha into the ramp. - The accent family (
--stage-color-accent+-readonly+-on-accent+-text-on-accent+-icon-on-accent) is a separate "primary" axis; it does not derive from bg. - Content on an accent fill has its own colours:
--stage-color-text-on-accent(the "on"/active button label, the highlighted dropdown option, the active search row) and--stage-color-icon-on-accent(icons in those spots). The text defaults to white, which suits the default saturated accent — but a theme with a bright accent + light text must override--stage-color-text-on-accent(and, if needed,-icon-on-accent) to a dark, legible colour, or on-accent content washes out. (--stage-color-on-accentis separate: the bright fill for the decorative switch thumb / checkbox tick.)
The most useful tokens #
Colour #
| Token | Role |
|---|---|
--stage-color-bg |
Root background; the whole grey ramp derives from it. |
--stage-color-panel-bg |
Panel surface. |
--stage-color-popover-bg |
Dropdowns / popovers. |
--stage-color-section-header-bg |
Section header strip. |
--stage-color-input-bg |
Input well. |
--stage-color-input-readonly-bg |
Readonly input fill. |
--stage-color-control-hover-bg |
The single hover fill for every interactive control (buttons, steppers, triggers, panel/tab chrome). |
--stage-color-border / --stage-color-border-strong |
Borders. |
--stage-color-border-hover |
Border lift on hover (bordered buttons, select triggers). |
--stage-color-text |
Primary text. -text-label and -text-muted equal it by default, so they don't dim until you override them. |
--stage-color-text-disabled |
Text on a disabled button. Solid, mixed from text and the panel background. |
--stage-color-accent |
Primary/accent (checked toggles, active states). |
--stage-color-accent-hover |
Brightened accent for hovering an accent-filled control a click would change — a checked switch/checkbox, an active multi-toggle (all turn off on click). Lifted toward white off --stage-color-accent; the neutral --stage-color-control-hover-bg covers the off/unfilled state. |
--stage-color-text-on-accent |
Text on an accent fill (active button, highlighted option, active search row). Pure white by default (full contrast on the saturated-blue accent); override to a dark colour for a pale accent. |
--stage-color-icon |
Base icon colour. Defaults to currentColor (matches surrounding text). |
--stage-color-icon-on-accent |
Icon on an accent fill. Defaults to --stage-color-text-on-accent. |
--stage-color-text-hover |
Label/glyph of a hovered button — brightens to the on/active colour so a hover reads as the same "lit" state. Defaults to (and tracks) --stage-color-text-on-accent; override to decouple. |
--stage-color-on-accent |
Bright fill for the decorative switch thumb / checkbox tick. |
--stage-color-accent-readonly |
Accent under readonly. |
--stage-color-focus |
The focus indicator colour (default white, deliberately accent-independent so it stays legible on an accent-filled control). Every bordered control — value controls, buttons, the colour field, and joined input+action groups — recolours its border to it on focus, the top of a cascade-layer border-state hierarchy, so it overrides the hover/checked/validation/resting border while focused and restores on blur. Joined groups coordinate: a focused segment also whites the seam it shares with its neighbour, so the recolour is real (no box-shadow ring). |
--stage-color-info / -success / -warn / -error |
Status levels (toast border, validation). |
--stage-color-modal-backdrop |
Modal scrim (the one translucent token). |
Corner radius (one knob) #
| Token | Default | Covers |
|---|---|---|
--stage-border-radius |
4px |
Everything — inputs, select trigger, buttons, dropdowns, the modal — so the whole app rounds uniformly. |
--stage-border-radius-lg |
= base |
Alias of the base (kept because a lot of CSS references it). |
--stage-border-radius-modal |
= base |
Alias of the base. |
Set --stage-border-radius: 0 for a fully-square look. To bring back a rounder
tier for the bigger surfaces, override -lg / -modal (e.g.
--stage-border-radius-lg: calc(var(--stage-border-radius) * 2)).
Motion (one derived knob) #
| Token | Default | Notes |
|---|---|---|
--stage-motion-duration |
0.18s |
Base duration. |
--stage-motion-ease |
ease |
Base easing. |
--stage-panel-motion / --stage-section-motion / --stage-toast-motion |
derived | Per-interaction <duration> <easing> pairs. |
Retune everything by overriding the base, or one interaction by pinning its own
token. Set any duration to 0s to disable that motion. Animations are on by
default; @media (prefers-reduced-motion: reduce) zeroes the base
automatically.
Size tiers #
The library owns the type scale: font size and control height are coupled and
ship as three hand-tested discrete tiers (a 1px border doesn't scale with em, so
it's tiers, not a formula).
| Tier | font / height | Selector |
|---|---|---|
small |
11px / 16px | .stage-size-small |
normal (default) |
13px / 20px | unclassed |
large (touch) |
16px / 24px | .stage-size-large |
Set a tier with the size prop (EditorPanels, Panel, Section, Label)
or a .stage-size-* class on any element. It cascades: set it on the stage and
everything inside scales; re-set it on a section to override just that subtree.
<EditorPanels size="small" ... /> // dense whole stage
<Section title="Details" size="small"> // dense section in a normal stage
Font family is 100% yours (the library
font: inherits it). Font size is a library knob because layout is downstream of it — an off-tier font-size forfeits the row-height guarantee..stage-rootand the tier classes set it, so custom markup in a section reads at the tier's size rather than the page's. Setfont-sizeon your own class to change that markup. To return a subtree under asmall/largeancestor to normal, use the explicit.stage-size-normalclass (the default is unclassed).
Many dimensions derive from the tier: --stage-expanded-width (panel width),
--stage-bar-height, --stage-icon-size, checkbox/switch/slider sizes, etc. — so
a large stage gets wider panels and taller bars for free.
On touch devices (@media (pointer: coarse)), text inputs bump their
font-size to --stage-font-size-touch (16px) so focusing one doesn't trigger iOS
Safari's zoom (it zooms any focused input under 16px). This is font-only — the row
height, label, and other controls stay dense — and on by default; opt a field out
with touchSizing={false}. The small tier is left as-is (its row is too short to
seat 16px) and accepts the zoom.
Component dimension tokens #
For fine-tuning individual widgets (most consumers never touch these):
--stage-field-value-width, --stage-section-body-pad, --stage-textarea-{min,max}-height,
--stage-select-listbox-max-height, --stage-search-max-{width,height},
--stage-slider-track-height, --stage-colorpicker-{width,area-height,…},
--stage-tab-max-width, and the per-level tooltip skins
--stage-tooltip-{info,success,warn,error}-{bg,text,border}.
Z-index tokens #
--stage-z-popover (1000) < --stage-z-modal (1050) < --stage-z-toast (1060) <
--stage-z-tooltip (1100). Inside a stage: --stage-z-stage-world (1) <
-stage-hud (20) < -stage-pointer (30); floating panels use
--stage-z-float (40).
Where tokens live #
All defaults are in the library's theme/tokens.css (imported by styles.css).
Browse that file for the complete, authoritative list and the derivation formulas.
Which classes are stable #
You can override anything, since the CSS is yours to fight with. These classes are the ones that won't change without a mention in the changelog:
| Class | What it is |
|---|---|
.stage-root |
The token scope |
.stage-size-small, -normal, -large |
The size tiers |
.stage-icon, .stage-icon-<name> |
Icons |
.stage-input |
The text input surface |
.stage-button |
The shared button base |
.stage-field-control |
A field row |
.stage-compact, .stage-expanded, .stage-bar |
A row's layout |
Every other .stage-* class is internal. Selecting one still works, but it's an escape
hatch that a release can break. Prefer a token or a class prop: Label, Section,
SectionDef, FieldBar and stageModal.open all take one.