Skip to content

Foundations

Nine pages, each held to one bar: Every rule carries why it exists, and every number cites the token, not a hex. Every number beside a rule is the token build’s.

The pictures that show how something works — a library of 3D objects, each picture drawn live for the web and still for a slide or a PDF. A section of its own, in four pages.

A token name reads left to right, from the general to the particular. There are three tiers.

tier 1 — the raw ramps. A name is a ramp and a number (color/blue/400), never a fancy word: nobody has to agree on what azure means.

tier 2 — the job, not the colour. text/default, surface/card, border/focus. Every value here is a reference to tier 1, and npm test fails if one is a literal.

tier 2, by mode — the same seventeen roles in four modes (blue, red, yellow, multicolor). Set data-accent on any element and its subtree re-resolves; that is how one library serves products with different accents.

tier 2, by canvas — the type roles (display / headline / title / body / label, each large / medium / small) in four canvases: web-desktop, web-tablet, web-mobile and slide. A web page’s two section steps live here too — layout/page-section-gap and layout/page-section-gap-tight — one custom property each that the canvas re-sets, so a frame’s one mode sets its type and its spacing together.

tier 2, by ground — a grey surface is one step darker than the ground it sits on: neutral/100 on the white page, neutral/200 on the product ground surface/card, where neutral/100 could not be told from the ground. Two roles in two modes (page, card); surface/sunken, the neutral KPI tile and the box of a segmented Tabs read them — and the Tabs’ box is the one exception, white on the product ground. Set data-ground="card" on the element that paints surface/card as the ground of a screen and everything grey on it follows; the parts of @pepperui/css that paint a ground themselves — a tinted Card, a Table, a Dialog, and every white container inside them — set it for what they hold.

Values that are not Figma variables because Figma has no type for them (a blur radius, a container width, an opacity). They live in tokens.css and are documented with the reason: Figma has no variable type for these. They become effect styles or stay CSS-only.

That is 386 custom properties in tokens.css, and every one of them is rendered in the workshop — the generator fails if a property has no record.

tokens/*.json is the only place a value is typed. npm run build writes:

Output For
tokens.css any browser — the --pui-* custom properties
_tokens.scss Sass consumers — MAFO’s _variables.scss theme
tailwind.js a Tailwind v3 preset
tailwind.css a Tailwind v4 @theme block — 167 keys
shadcn.css the shadcn variable map — 32 names + 7 of our own
figma.json the Figma variable import (Figma is authored from here)
pepper_tokens.py Python — the deck’s PPTX exporter reads it

DESIGN.md is the eighth, and packages/charts/dist/theme.json the ninth — the chart theme, on Data visualisation.

Pepper UI v1.5.0

The Mobupps design system. 386 tokens, 88 components, 65 pages in the workshop — every page of this site generated from the repository, so nothing here is typed twice.