@skirbi/pinta
@skirbi/pinta is Skirbi’s CSS theme and token engine. It supplies color
families, derived backgrounds, typography, and recipes for Semtic components.
It needs no JavaScript runtime.
Pinta removed its general-purpose utility attributes in v0.0.12. Spacing, borders, shadows, and responsive layout belong in your CSS. Use classes or attributes as selectors, whichever makes the intent clearer.
Load and activate
Load the stylesheet exported as @skirbi/pinta/pinta.css. For a plain HTML
site, serve that file and link to it:
<link rel="stylesheet" href="/css/pinta.css">
Enable the theme on the page or a subtree:
<html skirbi-theme="mytheme">
<section skirbi-theme="mytheme">...</section>
An empty skirbi-theme uses Pinta’s defaults. A name is a CSS selector hook;
it does not load a separate theme. Define your named theme in your stylesheet.
Semtic elements keep their semtic- names.
Cascade layers
@layer skirbi-base, skirbi-theme, skirbi-transform, skirbi-components,
skirbi-app;
| Layer | Purpose |
|---|---|
skirbi-base |
Structural defaults, typography, focus styling |
skirbi-theme |
Default tokens and theme-author overrides |
skirbi-transform |
Derived colors and final tokens |
skirbi-components |
Component recipes |
skirbi-app |
Application overrides |
Load your theme CSS after Pinta. Put token overrides in skirbi-theme and
application rules in skirbi-app. Later layers win for normal declarations;
within a layer, specificity and source order still matter. Unlayered normal
CSS takes precedence over layered normal CSS.
Color families
Each of primary, secondary, tertiary, and quaternary has main,
soft, strong, and contrast tokens. Start with the main colors:
@layer skirbi-theme {
[skirbi-theme="mytheme"] {
--skirbi-primary-main: #127872;
--skirbi-secondary-main: #fcb919;
--skirbi-tertiary-main: #fad68d;
--skirbi-quaternary-main: #243642;
--skirbi-secondary-contrast: var(--skirbi-on-light);
}
}
Pinta mixes each main color with white and black to derive soft and
strong. Contrast colors use foreground helpers; they are not calculated
contrast checks. Choose readable foregrounds for your palette.
The older --skirbi-*-light and --skirbi-*-dark tokens remain deprecated
aliases for soft and strong.
Status colors are separate: --skirbi-info-base, --skirbi-success-base,
--skirbi-warn-base, and --skirbi-error-base.
Page and surface colors
Source tokens feed Pinta’s background derivations:
@layer skirbi-theme {
[skirbi-theme="mytheme"] {
--skirbi-page-bg-source: var(--skirbi-primary-main);
--skirbi-surface-bg-source: var(--skirbi-tertiary-main);
--skirbi-wash-perc: 8%;
}
}
The page defaults to a wash. Choose another transform on the theme root:
<html skirbi-theme="mytheme" skirbi-bg-transform="reverse-wash">
Supported values are none, wash, and reverse-wash. The attribute selects
the transform percentage; setting only the --skirbi-bg-transform custom
property does not switch it.
For an exact background, set --skirbi-page-bg or --skirbi-surface-bg.
Application recipes should consume --skirbi-page-bg-final,
--skirbi-surface-bg-final, and --skirbi-surface-border-final so those
overrides are respected.
Light and dark mode
Put the mode on the same element as theme activation:
<html skirbi-theme="mytheme" skirbi-theme-mode="dark">
Light is the default. Pinta switches modes through the theme attribute. Toggle
it between light and dark to update the themed subtree:
document.documentElement.setAttribute('skirbi-theme-mode', 'dark');
Dark mode changes the environment used for mixing, foregrounds, and surface sources. Theme-specific overrides use the same selector:
@layer skirbi-theme {
[skirbi-theme="mytheme"][skirbi-theme-mode="dark"] {
--skirbi-page-fg: #f8fafc;
--skirbi-surface-bg-source: #243642;
--skirbi-surface-fg: #f8fafc;
}
}
Typography and controls
@layer skirbi-theme {
[skirbi-theme="mytheme"] {
--skirbi-font-sans: system-ui, sans-serif;
--skirbi-font-scale: 1.125;
--skirbi-line-height: 1.7;
--skirbi-h1-scale: 2.5;
--skirbi-prose-gap: 1.5rem;
--skirbi-field-height: 3rem;
--skirbi-button-radius: 0.5rem;
}
}
--skirbi-field-height sizes text inputs and selects. Checkboxes and radios
use a proportion of it, unless --skirbi-choice-size is set. Textareas keep
their own height. Focus styling uses --skirbi-focus-ring-size and
--skirbi-focus-ring-color.
Component recipes and application CSS
Buttons express their role through attributes:
<button type="submit" lead>Save</button>
<button type="button" support>Cancel</button>
<button type="button" antagonist>Delete</button>
Other button roles are extra and herald. Each has
--skirbi-button-<role>-* tokens. These are component recipes, not a generic
tone/variant system for arbitrary elements.
For your own styling, use ordinary CSS:
<semtic-panel class="login-card">
...
</semtic-panel>
@layer skirbi-app {
[skirbi-theme] .login-card {
max-inline-size: 28rem;
padding: 1.5rem;
border: 1px solid var(--skirbi-surface-border-final);
border-radius: 0.75rem;
background: var(--skirbi-surface-bg-final);
color: var(--skirbi-surface-fg);
}
}
Pinta also supplies defaults for semtic-grid, semtic-flex, and
semtic-stack, plus named grid recipes such as diptych and triptych.
See Semtic layout elements.
See the theme specification for the
contract. The detailed recipe reference lives in the DocumentCSS blocks in
Pinta’s lib/themes/pinta.css.