Base UI · Tailwind v4 · copy & paste
Components that take the colour you give them.
A copy-paste component library built on Base UI and Tailwind v4. Six colour roles, any number of palettes — scope one to a page, a card, or a single button.
Why it exists
Three problems, three decisions
Fragiola starts from shadcn/ui — it reuses its registry format and its CLI, so installing a component is the command you already know. Three things about that baseline stopped scaling, and each one shaped a decision here.
- 01
Colour you can scope
A single global token set makes one decision for the whole application: switching a theme redefines every surface at once, and there is no way to tint one region while the rest of the page stays neutral. Fragiola applies colour with a class, so a page, a card or one button can carry its own palette — and no component needs a colour variant to make it happen.
- 02
Styles shared, not pasted
Components that share a visual skeleton should share the CSS that draws it. When each one keeps a private copy, the copies drift: one sibling loses its close animation, another gains a padding nobody chose, and a fix has to be applied as many times as it was pasted. Fragiola gives each skeleton one home — a style family — and every component that looks like it consumes that.
- 03
One component per idea
The same wrapper written twice for two menus, several ways to write an input, a button re-wrapped for each context: every duplicate is another place the next change has to reach. Fragiola builds those families from one factory, so there is a single component per idea and a single place to change it.
The token system
Six roles, any number of palettes
A palette is six roles applied by class. Components read roles, never colours — so the same button is at home in any palette, and none of them needs a colour variant.
baseThe colour of this palette — a background, or a fill.
softA subtle background: an alert, a badge, a hover, a field's surface.
lineStroke.
contrastContent on base — the text on what you painted.
accentContent on a foreign background, or on soft.
ringThe interaction highlight: focus, active. The only role drawn outside the element.
The number of palettes is free. The number of roles is not.
Six roles, every palette declaring all six. When something does not fit, the answer is another palette — never a seventh role. That is what keeps the contract closed while the colours keep coming.
And a palette is applied by class, so it is scoped to whatever subtree carries it: a page, a card, or one button on an otherwise neutral floor.
// The floor is neutral.
<div className="palette-surface bg-palette-base">
{/* The palette goes on the element. */}
<Clickable.Button className="palette-danger">
Delete
</Clickable.Button>
</div>The demonstration
Six palettes, one surface
One neutral floor, six palettes, one component. Nothing below is a variant: each button carries a palette-* class and reads the same six roles.