Build your Skin
A Skin is a named set of --vl-* tokens. It changes how Velora looks and never how it moves or is built: no component selectors, no JavaScript. This page is the shortest path to a working Skin.
Status: Experimental. The template, the validator and this guide exist in the repository and are tested. They are not published to npm yet.
1. Copy the template
Start from packages/css/templates/skin.template.css. Rename my-skinand change the values. The eighteen declarations below are the whole contract: every one is required, because a Skin states its identity instead of inheriting it.
html[data-editorial-theme="my-skin"] {
--vl-bg-main: #ffffff;
--vl-text-primary: #14171f;
--vl-color-primary: #2f3fd4;
--vl-color-accent: #2f3fd4;
--vl-font-family: system-ui, sans-serif;
--vl-font-family-display: system-ui, sans-serif;
--vl-skin-content-max: 72rem;
--vl-skin-density: 1;
--vl-radius-md: 8px;
--vl-skin-border-width: 1px;
--vl-skin-media-ratio: 16 / 10;
--vl-motion-distance: 1rem;
--vl-duration-normal: 240ms;
--vl-motion-signature: fade-up;
--vl-corner-style: round;
--vl-cut-angle: 18.4deg;
--vl-cut-pitch: 9px;
color-scheme: light;
}2. Select it
<html data-editorial-theme="my-skin">Light and dark come from color-scheme and, if you want both, fromlight-dark() in the colour tokens. The velora Skin is a complete example of one palette written once.
3. Optional: an accent family
A Skin may declare --vl-color-accent-primary and, optionally,--vl-color-accent-alt-1 and --vl-color-accent-alt-2, then read the choice a page makes. Alternates must come as a pair starting with alt-1.
<html data-skin-accent="alt-1">
<input type="radio" name="vl-skin-accent" value="alt-1">Skins that do not declare an accent family keep their primary accent. Nothing changes for them.
4. Validate
pnpm validate:skins
node tools/skin/validate-skins.mjs --css my-skin.css --skin my-skinThe validator opens a real browser and checks two things:
- Completeness. Every token family is declared by the Skin itself: colour, typography, spacing, shape, media, motion, structure and colour scheme. A failure names the family and the tokens.
- Contrast. The primary accent, and each alternate if present, reaches WCAG AA (4.5:1) on the Skin's own background, in every theme member and under both system colour schemes.
Constraints
- A Skin is unlayered CSS, so it wins over the layered core. Keep it to custom properties.
- Motion intent (
--vl-motion-signature) names a core effect; it does not define one. - Readable motion still follows
prefers-reduced-motion; a Skin cannot override it.