Theming
Fragiola's colour system is built on **palettes**: sets of six roles applied
Fragiola's colour system is built on palettes: sets of six roles applied by class. Components read roles, never colours.
The six roles
| role | what it is |
|---|---|
base | the color of this palette — background or fill |
soft | subtle background — alert, badge, hover, field surface |
line | stroke |
contrast | content on base |
accent | content on a foreign background or on soft |
ring | interaction highlight — focus, active |
The palette class declares the six custom properties and paints nothing.
Components paint explicitly with bg-palette-base, text-palette-contrast,
and so on.
The number of palettes is free; the number of roles is not
If something does not fit, create another palette. Two background levels
on a surface are two palettes, not a seventh role. A token that is always
derived and never overridden is a derivation, not a role —
--color-palette-base-hover is computed from base in @theme with an
escape hatch, because no palette ever overrode it.
Every palette declares all six
Custom properties inherit. A palette that omits a role picks it up from
whatever ancestor declared it — silently, across unrelated scopes. "Optional
token" does not exist in an inheritance-based system. This is why ring,
the one people forget, is mandatory: an omitted ring inherits from an
ancestor, and that is how an invalid field inside a feature ended up with a
red border and a purple focus ring.
Scoping
A palette is scoped by class, so any subtree can carry its own.
palette-danger on a button tints that button; palette-raised on a card
changes the whole card's surface. Adding a colour is a new palette, not a new
token everywhere — destructive went from 132 occurrences to 10.
Never mix palettes
A palette that borrows neutrals from one place and colour from another exists
only to fit one case, and the next case will ask for another exception. The
tell: a role with no coherent source. If you need a neutral surface with a
coloured focus ring, create a surface-* palette — that is what they are for.
Secondary text
The most-used token in the shadcn baseline (muted-foreground, 44
occurrences) maps to a role Fragiola deliberately lacks. The settled value is
text-palette-accent/85 — it clears AA (4.5:1) on every neutral surface in
both themes. Apply it uniformly, including to placeholders. See AGENTS.md
for the measurement.
Chart series are not palettes
No role describes "the third series", and reusing danger as series 4 breaks
its meaning. Chart colours live outside the contract, in their own ordered
set; their ordering is what guarantees colourblind separation. Only the
derived ordinal ramp is themeable.