Dockable
Guides

Theming with Fragiola palettes

Colour from palette roles, shape from CSS custom properties, both scoped to any element.

Because the package paints nothing, a "theme" is just your CSS. This site's examples show one way to do it that scales: colour comes from Fragiola UI palettes, shape from a handful of CSS custom properties, and both are scoped to an element so several themes can live on one page.

Intermediate exampleScoped palettesGive each tabset its own colour scheme. A dropdown in the tabset header picks a palette (blue, orange, green, purple, surface or raised), stored in the tabset's config through an action, so it is saved with the layout. A palette is just a CSS class that sets a few colour variables (background, soft fill, line, text, accent and focus ring); the tabs and panels inside the tabset read them.Open the live example

Colour: palette roles

A Fragiola palette is a class (palette-surface, palette-raised, palette-blue, …) that sets six roles as custom properties: --palette-base, --palette-soft, --palette-line, --palette-contrast, --palette-accent and --palette-ring. Utilities read the roles, not colours:

<Dockable.Root className="palette-surface bg-palette-base text-palette-contrast" … />
<Dockable.TabSet className="palette-raised bg-palette-base border border-palette-line" … />
<Dockable.Tab className="text-palette-accent/85 data-selected:bg-palette-soft data-selected:text-palette-contrast" … />
<Dockable.DropIndicator className="palette-blue z-10 border-2 border-palette-base bg-palette-base/20" />

A palette class on an element re-scopes the roles for its subtree, so the same utilities paint a blue drop indicator inside a neutral tabset. Changing a theme means changing what the palette classes resolve to, never the markup.

Two rules keep palettes from leaking: define all six roles for every palette a theme uses (an omitted role is inherited from the page), and use secondary text as text-palette-accent/85.

Shape: tokens

Colour is not the only thing a theme changes. The examples' themes also set shape tokens that the kit's class names read:

[data-example-theme="ide"] {
    --dk-radius: 0px;
    --dk-tab-radius: 0px;
    --dk-tab-height: 2.1rem;
    --dk-gap: 0px;
    --dk-splitter-size: 1px;
    --dk-splitter-grab: 6px;
    --dk-font: var(--font-sans);
}
<Dockable.TabSet className="rounded-(--dk-radius) border-(length:--dk-border)" … />
<Dockable.Tab className="h-(--dk-tab-height) rounded-t-(--dk-tab-radius)" … />

Splitter thickness is a token too: the engine measures the splitter element, so a theme can change it freely and the split maths follows.

Scoping a theme to an element

The palettes normally key off :root[data-theme]. To theme one region (a docs stage, a panel, a preview), scope the selectors to an attribute on any element:

:root [data-example-theme="terminal"] .palette-surface,
:root [data-example-theme="terminal"].palette-surface {
    --palette-base: oklch(0.12 0 0);
    --palette-soft: oklch(0.18 0.02 145);
    --palette-line: oklch(0.35 0.08 145);
    --palette-contrast: oklch(0.88 0.2 145);
    --palette-accent: oklch(0.78 0.17 145);
    --palette-ring: var(--palette-accent);
}

Every example on this site runs in five such themes (Light, Dark, IDE, Paper, Terminal) switched without a reload: the markup never changes, only the attribute on the stage.

Themes and popouts

A popout window is a new document. Dockable copies your stylesheets into it (<link>, <style> including later edits, CSSOM rules and adopted stylesheets) and the lang and dir of the page. If your theme keys off :root[data-theme] or a class on <html> or <body>, turn on popoutMirrorRoot and the windows follow the page, theme switches included:

<Dockable.Root model={model} popoutMirrorRoot>

A theme set on another element (a wrapper) is not on the document root: copy it in Dockable.Popout's onOpen. See Popouts.

Without Fragiola

Nothing here is required. The same pattern works with any design tokens: define custom properties per theme, read them from your classes, and select state with data-*. See Styling with plain CSS.

On this page