@skirbi/pinta
@skirbi/pinta defines how things look.
It is the token and primitive theme layer for semtic. Everything is CSS — no runtime, no JS required.
Opt-in
Pinta does nothing unless [semtic-theme] is present. Apply it to the whole
page or scope it to a subtree:
<html semtic-theme>...</html>
<div semtic-theme="mytheme">...</div>
Named themes are supported. The attribute value selects which theme overrides apply.
Cascade layers
Pinta fixes layer order so overrides are always predictable:
@layer pinta-base, pinta-theme, pinta-components;
pinta-base— engine, tokens, primitives, interactionspinta-theme— palette, pointers, defaultspinta-components— light defaults for semtic elements
Write your overrides in pinta-theme or pinta-components. You always come
last, so you always win.
Theme mode
<html semtic-theme semtic-theme-mode="dark">
Toggle via JS:
document.documentElement.setAttribute('semtic-theme-mode', 'dark');
Tokens
Brand palette
Override the four base color slots:
@layer pinta-theme {
[semtic-theme] {
--semtic-primary-base: #1AB1AB;
--semtic-secondary-base: #FCB919;
--semtic-tertiary-base: #FAD68D;
--semtic-quaternary-base: #127872;
}
}
Pinta derives light and dark variants automatically.
Pointers
Pointers decide which color drives which surface:
@layer pinta-theme {
[semtic-theme] {
--semtic-page-tone-base: var(--semtic-tertiary-base);
--semtic-surface-tone-base: var(--semtic-quaternary-base);
}
}
Status colors
Used for info, success, warn, error states. Override carefully and test for colorblind resilience.
--semtic-info-base: #2f6fed;
--semtic-success-base: #0f766e; /* teal, not neon green */
--semtic-warn-base: #d97706; /* orange, not pale yellow */
--semtic-error-base: #b91c1c;
Typography
Driven by knobs:
@layer pinta-theme {
[semtic-theme] {
--semtic-font-scale: 1.125;
--semtic-line-height: 1.7;
--semtic-h1-scale: 2.5;
--semtic-h2-scale: 1.875;
--semtic-prose-gap: 1.5rem;
}
}
Optional font profile presets:
<div semtic-theme semtic-font-profile="editorial">
Primitives
These attributes work on any element inside a themed subtree.
| Attribute | Values | Effect |
|---|---|---|
semtic-pad |
0–6 | Padding |
semtic-gap |
0–6 | Gap (for layout containers) |
semtic-border |
0–5 | Border width |
semtic-radius |
0–6 | Border radius |
semtic-shadow |
1–5 | Box shadow |
semtic-shadow-border |
1–5 | Shadow-based border |
semtic-bg |
— | Surface background |
semtic-hover |
— | Hover interaction |
semtic-selected |
— | Selected state |
semtic-press |
— | Press interaction |
semtic-disabled |
— | Disabled state |
Example card:
<article semtic-bg semtic-pad="3" semtic-radius="4" semtic-shadow="2" semtic-hover semtic-press>
...
</article>
Tone and variant
Apply color context to any element:
<button semtic-theme-tone="primary" semtic-theme-variant="solid">Save</button>
<div semtic-theme-tone="warn" semtic-theme-variant="outline">Warning</div>
Tones: primary, secondary, tertiary, quaternary, info, success,
warn, error.
Variants: solid, outline, gradient.
Dark mode overrides
@layer pinta-theme {
[semtic-theme="mytheme"][semtic-theme-mode="dark"] {
--semtic-page-tone-base: var(--semtic-primary-base);
--semtic-page-fg: var(--semtic-tertiary-base);
--semtic-page-opacity: 95%;
}
}
Interaction knobs
@layer pinta-theme {
[semtic-theme] {
--semtic-hover-shadow: 0 8px 16px rgb(0 0 0 / 0.08);
--semtic-press-shadow: 0 2px 4px rgb(0 0 0 / 0.12);
--semtic-transition-duration: 200ms;
--semtic-hover-ring-size: 0px;
}
}
Overriding components
@layer pinta-components {
semtic-panel {
/* your overrides */
}
}