Alpha: Ships on npm at 7.0.0-alpha.07.0.0-alpha.0

The Theme contract

The 21 core roles every Theme sets, the derived roles that default through var(), the invariant roles, the plate remap, and what compileTheme refuses.

startupbuilder.dev defines the Theme: "A Theme is the design tokens of a Brand, its color, type, spacing, radius, and motion, that every Component reads when it renders." mdxui adds five policies and compiles the tokens to CSS custom properties.

Three kinds of role

A role names a use, not a look: ink-lede is the color a lede is set in, whatever that color is. Every Section type reads roles and never a literal color, typeface or size.

KindA ThemeExample
CoreSets every one, in light or invariantpaper, ink, accent
DerivedMay override any; each defaults to a core role through var()ink-lede defaults to the secondary ink
InvariantDeclares once and never redefines for darkcertificate's plate family

From @mdxui/themes@7.0.0-alpha.0: 21 core roles, which every Theme sets, and 325 derived roles, each with a default.

paper, panel, ink, ink-soft, rule, rule-soft, accent, accent-ink, pass, fail, warn, plate, plate-ink, plate-rule, sans, mono, text, lh, shell-max, shell-inset, shell-d-section

Core roles

Each square is one role under one Theme, as resolveTokens returns it. Hover for the value; click to copy the custom property. Dark shows the palette a Theme emits, so the three light-only Themes keep their light values.

21 core roles · 14 colors
Role certificate vaultline quaypoint callboard directory
paper
panel
ink
ink-soft
rule
rule-soft
accent
accent-ink
pass
fail
warn
plate
plate-ink
plate-rule
Rolecertificatevaultlinequaypointcallboarddirectory
sansIBM Plex SansPublic SansInterInstrument SansInter
monoIBM Plex MonoIBM Plex MonoJetBrains MonoDM MonoJetBrains Mono
text15px16px16px16px14px
lh1.61.51.51.551.5
shell-max1320px1180px1200px1200px1200px
shell-insetclamp(1rem,3.5vw,2.5rem)clamp(20px,4vw,32px)clamp(20px,4vw,36px)clamp(20px,4vw,36px)clamp(16px,4vw,32px)
shell-d-sectionclamp(2.25rem,5vw,3.5rem)clamp(70px,9vw,104px)clamp(72px,9vw,104px)clamp(72px,9vw,104px)24px

Derived roles

A Theme that sets only the core roles renders: every derived role falls back through var() to a core role. A richer Theme overrides the families its design needs. The bars count how many roles of each family a Theme sets itself.

FamilyRolescertificatevaultlinequaypointcallboarddirectory
All 49 families32529254122126126
fg-*fg-gap: 1.9rem27122000
tier-*tier-gap: 1.9rem27027444
prose-*prose-ink: var(--ink-soft)27027000
footer-*footer-brand-size: var(--text)272268811
nav-*nav-height: 58px21121444
plate-*plate-panel: var(--plate)19417171717
btn-*btn-face: var(--mono)18110161616
app-*app-nav-active-bg: color-mix(in srgb, var(--accent) 10%, transparent)1600000
hero-*hero-h1-gap: 1.1rem14113000
term-*term-bg: var(--plate-well)1210121212
shell-*shell-d-compact: var(--shell-d-section)1039555
sechead-*sechead-max: none909888
The other 37 families
FamilyRolescertificatevaultlinequaypointcallboarddirectory
ink-*ink-lede: var(--ink-soft)707444
h2-*h2-size: clamp(1.4rem,3.4vw,2.15rem)707771
article-*article-rule: var(--rule-soft)606002
accent-*accent-hover: var(--accent)504444
radius-*radius-control: 0505555
code-*code-ink: var(--plate-ink)404444
brand-*brand-weight: 700401233
post-*post-title-size: clamp(23px,3.2vw,30px)404000
share-*share-label-ink: var(--ink-muted)404000
shadow-*shadow-card: none404333
h1-*h1-size: clamp(2.2rem,7vw,3.4rem)303330
title-*title-size: var(--h1-size)300003
eyebrow-*eyebrow-size: var(--mono-xs)313333
listing-*listing-title-size: clamp(34px,6vw,52px)303000
field-*field-a: color-mix(in oklch, var(--accent), white 18%)330033
panel-*panel-inset: var(--panel)202111
glass-*glass: var(--paper)202222
cta-*cta: var(--accent)202222
display-*display: var(--sans)202221
text-*text-sm: calc(var(--text) * .875)220000
mono-*mono-sm: var(--text-sm)210000
link-*link: currentColor202222
btitle-*btitle-size: var(--h2-size)200002
compare-*compare-no: var(--ink-faint)201000
fsplit-*fsplit-copy: 400px200000
dur-*dur-fast: 120ms220000
tint-*tint: var(--paper)101111
track-*track: var(--rule-soft)111111
rule-*rule-faint: var(--rule-soft)101111
ghost-*ghost-border: var(--rule)101111
figures-*figures: normal110000
heading-*heading-wrap: balance101000
meta-*meta-accent: var(--eyebrow-color)101000
hatch-*hatch: color-mix(in srgb, var(--ink) 16%, transparent)110000
scrim-*scrim: rgba(0, 0, 0, 0.58)110000
ease-*ease-out: cubic-bezier(0.16, 1, 0.3, 1)110000
z-*z-nav: 50111000

@mdxui/themes exports lists every derived role with its default.

Invariant roles

An invariant role is declared once and never redefined for dark. compileTheme refuses a Theme that names one in dark. certificate keeps its plate and its scrim invariant, so a code window is the same dark plate in both schemes.

certificate24 roles
plateplate-inkplate-ink-softplate-ruleplate-accentplate-passscrimand 17 type, size and motion roles: ease-out, dur-fast, dur-base, mono, sans, text, lh, text-sm, text-xs, mono-xs, fg-label-size, eyebrow-size, btn-size, hero-fine-size, nav-link-size, footer-brand-size, footer-note-size
vaultline0 roles
none
quaypoint16 roles
term-bgterm-row-bgterm-inkterm-ink-softterm-ink-mutedterm-rule-softterm-rule-faintterm-accentterm-passterm-control-inkterm-command-inkterm-hovercode-inkcode-commentcode-stringcode-keyword
callboard16 roles
term-bgterm-row-bgterm-inkterm-ink-softterm-ink-mutedterm-rule-softterm-rule-faintterm-accentterm-passterm-control-inkterm-command-inkterm-hovercode-inkcode-commentcode-stringcode-keyword
directory16 roles
term-bgterm-row-bgterm-inkterm-ink-softterm-ink-mutedterm-rule-softterm-rule-faintterm-accentterm-passterm-control-inkterm-command-inkterm-hovercode-inkcode-commentcode-stringcode-keyword

On a plate

A plate section re-tokens its subtree: a Section type on a plate reads --ink and gets the plate's ink. PLATE_REMAP names each pair, so one Section type renders on paper and on a plate with no second set of styles.

On paper
ink
ink-soft
ink-lede
ink-body
ink-note
ink-muted
ink-quiet
ink-faint
ink-ghost
rule
rule-soft
rule-faint
panel
panel-inset
ghost-border
accent
accent-muted
pass
On a plate
ink → plate-ink
ink-soft → plate-ink-soft
ink-lede → plate-ink-lede
ink-body → plate-ink-body
ink-note → plate-ink-faint
ink-muted → plate-ink-muted
ink-quiet → plate-ink-faint
ink-faint → plate-ink-faint
ink-ghost → plate-ink-ghost
rule → plate-rule
rule-soft → plate-rule-soft
rule-faint → plate-rule-faint
panel → plate
panel-inset → plate-panel
ghost-border → plate-ghost-border
accent → plate-accent
accent-muted → plate-accent-soft
pass → plate-pass

18 remaps in PLATE_REMAP. A Section type on a plate reads --ink and gets the plate’s ink; the compiler declares every chained role again on the plate.

The Theme object

interface Theme {
  name: string;
  light: Record<string, string>; // the full light palette, every core role included
  dark?: Record<string, string>; // only what changes for dark
  invariant?: Record<string, string>; // declared once, never redefined for dark
  dividers: DividerPolicy;
  markers: boolean;
  featured: 'rule' | 'plate';
  icons: 'inline' | 'image';
  scheme?: 'auto' | 'light';
}

Compiling

compileTheme(theme) checks a Theme against the contract and returns its custom properties for :root. It refuses a Theme missing a core role and names every missing one. For a Theme that sets only paper:

theme "harbor" is missing core roles: panel, ink, ink-soft, rule, rule-soft, accent, accent-ink, pass, fail, warn, plate, plate-ink, plate-rule, sans, mono, text, lh, shell-max, shell-inset, shell-d-section

resolveTokens(theme) returns the light palette with every var() chain followed to its value, for wherever a role is needed as a literal. The playground runs both on every edit.

Prior definitions

PriorDefinitionDisposition
startupbuilder.dev ThemeThe design tokens of a Brand, its color, type, spacing, radius, and motionAdopted word for word. The 5 policies are the mdxui addition. Its css property, extra CSS after the tokens, has no counterpart: no page carries its own CSS.
@mdxui/themes contractRole and value pairs compiled to CSS custom propertiesKept as the rendering view of the same Noun
Legacy mdxui theme proplight, dark or system on a Site wrapperSuperseded by the scheme policy and the dark palette

On this page