tokens
Every colour, text style and spacing step is stored once, as a design token, in one file of the repository, written in the format of the Design Tokens Community Group. One build turns that file into three forms, so CSS, JavaScript and Tailwind apps all read the same values. Change a value in that one place, and every form follows.
the three forms
| form | for |
|---|---|
| CSS custom properties | Everything. The components read their colours, typefaces and spacing from them |
| Typed objects | JavaScript that needs a value, such as a chart or a drawing on a canvas |
| A Tailwind preset | Apps built with Tailwind that want the same colours, fonts and spacing |
The specs below give the import for each. What each value is for is on colour, typography and layout.
css custom properties
The tokens' stylesheet declares every token as a CSS custom property, for the whole page:
- The colours: the canvas and its steps, the four greys of text, the brand blue and cyan, the signal colours, a control's edge, the focus outline, and each state's colour, tint and border.
- The two typefaces, Inter and JetBrains Mono, each with its fallbacks.
- The eight steps of space, from 4 to 64 pixels.
- A dark colour scheme, which makes the browser's own controls, scrollbars and form fields dark too.
The same stylesheet also sets the page's base styles, so the page sits on the canvas, in the main text colour and in Inter, and it defines a class for each of the six type roles. Sizes and weights aren't custom properties of their own: use a role's class, or the components.
typed tokens
For JavaScript that needs a value, the same tokens come as typed objects, ready to read:
import { tokens, colors, typography, fontFamily, space } from '@nalet/design-system/tokens';
colors['state-warning']; // '#F2B233'
typography.h2.fontSize; // '22px'
fontFamily.mono; // "'JetBrains Mono', ui-monospace, monospace"
space['s-4']; // '16px'
The package's main entry exports the same objects, with one difference: there, the space helper turns a number of pixels into its token, for the components' space settings. The specs list the names.
tailwind preset
The preset only extends Tailwind's theme; the library itself doesn't depend on Tailwind. It gives Tailwind every colour token by its name, the two typefaces, the spacing scale and square corners.
Its spacing keys follow the scale, not Tailwind's own steps, so a key can mean a different size than you are used to: the key 5 means 24 pixels here, not Tailwind's usual 20. Tailwind's other spacing keys are still there, but they are off the scale, so leave them alone. The preset adds no font sizes.
theming
The system is dark only. An app can give a custom property another value, but the contrast rules only cover the values that ship, and the components are built for them. Light and high-contrast themes are planned.
changing a token
Tokens change in the repository, never inside an app:
- Edit the tokens file.
- Run the token build. It writes the stylesheet, the typed tokens and the Tailwind preset again, and warns about any pair of colours that falls below its contrast minimum.
- Run the tests. The contrast test fails on any pair below the rules, and so does the build on every push.
The generated files are never edited by hand; see contributing.
specs
the three forms
| form | import |
|---|---|
| CSS custom properties | @nalet/design-system/tokens.css (also inside styles.css); the components read var(--fg-2), var(--state-error), var(--s-4) |
| Typed objects | import { tokens } from '@nalet/design-system/tokens' |
| Tailwind preset | presets: [require('@nalet/design-system/tailwind')] |
tokens.css
Declared on :root:
- Colours:
--bg…--surface-selected,--fg…--fg-dim,--cloud-blue,--cloud-cyan,--signal-green,--signal-amber,--signal-red,--border-control,--focus-ring, and each state's--state-*,--state-*-bgand--state-*-border. - Families:
--ff-ui(Inter) and--ff-mono(JetBrains Mono), with their fallbacks. - Space:
--s-1(4px) to--s-8(64px). color-scheme: dark.
The page base: body on --bg, in --fg and Inter. The type role classes: .type-display, .type-h1, .type-h2, .type-body, .type-ui and .type-code.
typed tokens
- From
@nalet/design-system/tokens:tokens,colors,typography,fontFamilyandspace. - From
@nalet/design-system: the same objects, except thatspaceis the helper behind the components' space props:space(16)returns'var(--s-4)'. - The types
ColorToken,TypographyRoleandSpaceTokenname the keys.
tailwind preset
| tailwind | maps to |
|---|---|
colors |
Every colour token by its name: bg-surface, text-fg-muted, border-state-error-border |
fontFamily |
font-mono and font-sans, the two stacks |
spacing |
The keys 1–8 and s-1–s-8 to the scale, so p-5 is 24px here, not Tailwind's 20px |
borderRadius |
DEFAULT and none are 0: square frames |
Tailwind's other spacing keys (0.5, 10, 12 …) are off the scale.
changing a token
- Edit
tokens/tokens.json(the DTCG format). - Run
npm run build-tokens. It regeneratessrc/styles/tokens.css,src/tokens.tsandtailwind-preset.cjs, and warns about any colour pair under its contrast minimum. - Run
npm test:tokens/contrast.test.tsfails on a pair below the policy, and so does CI.