Liberman GonzalezProduct designer · Design engineer
EN / ES

Design engineering · 01 · System

The system this site is built with

Tokens in Figma Variables with light and dark modes, exported to CSS and React components. This page is built from the same files the site runs on: when a token changes, the docs change with it.

Primitives
21
Semantic × 2 modes
15
Text styles
18
Components
7

ITurn on Inspect, top right: every component on this page tells you what it’s made of, and holding Alt measures the distance between two elements.

01

From Figma to the browser

Figma holds the values; the repo is the source of truth. Between them sit two scripts and no manual steps.

  1. 1

    Figma Variables

    Variables and text styles

    Four collections: primitives, semantic with Light and Dark modes, spacing and shape, and motion. Type lives in text styles.

  2. 2

    Export

    design/tokens/figma-export.json

    Variables exactly as they leave Figma, committed to the repo so every change shows up in the history.

  3. 3

    DTCG tokens

    scripts/figma-to-dtcg.mjs

    Turns each variable into the standard token format and keeps aliases intact: a semantic token still points to its primitive.

  4. 4

    CSS

    scripts/build-tokens.mjs

    Style Dictionary writes tokens.css: primitives as custom properties, semantic tokens with light-dark(), and text classes with fluid sizes.

  5. 5

    Components

    src/components

    They only read semantic tokens. A hard-coded color in a component is a bug, not a shortcut.

One token, end to end
In Figma
color/text/accentLight → blue/500 · Dark → blue/300
In DTCG
"$value": "{color.blue.500}"
In CSS
--color-text-accent: light-dark(var(--color-blue-500), var(--color-blue-300));
On screen
See the system →See the system →

02

Color

A short palette: paper and ink for light mode, night and snow for dark, and a single blue accent. Components never use it directly; they go through semantic tokens.

Primitives

paper

  • 0#fbfaf7
  • 50#f4f1ea
  • 100#e9e4da

ink

  • 300#b8b3a9
  • 500#66635c
  • 700#3b3a36
  • 900#151513
  • a10#1515131a
  • a20#15151333

night

  • 800#1f2226
  • 900#15171a
  • 950#0d0e10

snow

  • 100#f1efea
  • 300#b9b7b1
  • 500#8e8c87
  • a10#f1efea1a
  • a20#f1efea33

blue

  • 100#e4e8ff
  • 300#8197ff
  • 500#2a4deb
  • 900#1a2459

Semantic

Each semantic token has one value per mode. The site follows your operating system until you pick a mode with the Theme button. 15 of 15 semantic tokens point to a primitive in both modes. None has a value of its own.

TokenLightDark
bg-canvaspaper.50night.950
bg-surfacepaper.0night.900
bg-sunkenpaper.100night.800
bg-inverseink.900snow.100
bg-accentblue.500blue.300
bg-accent-subtleblue.100blue.900
bg-artifactpaper.0paper.0
text-primaryAaink.900Aasnow.100
text-secondaryAaink.700Aasnow.300
text-tertiaryAaink.500Aasnow.500
text-inverseAapaper.0Aanight.950
text-accentAablue.500Aablue.300
line-gridink.a10snow.a10
line-strongink.a20snow.a20
line-accentblue.500blue.300

Contrast

The text and background pairs the site actually uses, computed from each mode’s real values. The lowest is 4.7:1; the AA minimum for body text is 4.5:1.

Text on backgroundLightDark
text-primary / bg-canvasAa16.2:1 AAAAa16.8:1 AAA
text-primary / bg-surfaceAa17.5:1 AAAAa15.6:1 AAA
text-primary / bg-sunkenAa14.4:1 AAAAa13.9:1 AAA
text-secondary / bg-canvasAa10.1:1 AAAAa9.6:1 AAA
text-secondary / bg-surfaceAa10.9:1 AAAAa9.0:1 AAA
text-secondary / bg-sunkenAa9.0:1 AAAAa8.0:1 AAA
text-tertiary / bg-canvasAa5.3:1 AAAa5.7:1 AA
text-tertiary / bg-surfaceAa5.7:1 AAAa5.3:1 AA
text-tertiary / bg-sunkenAa4.7:1 AAAa4.8:1 AA
text-accent / bg-canvasAa5.5:1 AAAa7.2:1 AAA
text-accent / bg-surfaceAa6.0:1 AAAa6.7:1 AA
text-accent / bg-sunkenAa4.9:1 AAAa5.9:1 AA
text-inverse / bg-accentAa6.0:1 AAAa7.2:1 AAA
text-inverse / bg-inverseAa17.5:1 AAAAa16.8:1 AAA
text-accent / bg-accent-subtleAa5.1:1 AAAa5.4:1 AA

03

Typography

Satoshi for reading, Geist Mono for labels. Styles of 32 px and up scale with the screen: the size listed is the maximum.

  • t-display-xlSatoshi 500 · 112/0.95 · -0.03emHome hero

    One system over a thousand exceptions

  • t-display-lSatoshi 500 · 96/0.95 · -0.03emCase title and contact CTA

    One system over a thousand exceptions

  • t-display-mSatoshi 500 · 72/1 · -0.02emNext case

    One system over a thousand exceptions

  • t-heading-xlSatoshi 500 · 64/1 · -0.02emKey figures

    One system over a thousand exceptions

  • t-heading-lSatoshi 500 · 48/1.05 · -0.015emSection title or case in a list

    One system over a thousand exceptions

  • t-heading-mSatoshi 500 · 40/1.1 · -0.01emSection intro, guiding question

    One system over a thousand exceptions

  • t-heading-sSatoshi 500 · 32/1.15 · -0.01emPrinciples

    One system over a thousand exceptions

  • t-titleSatoshi 500 · 22/1.25 · -0.005emCard title

    Systems for products that keep evolving

  • t-leadSatoshi 400 · 20/1.45Hero subtitle

    Systems for products that keep evolving

  • t-body-lSatoshi 400 · 18/1.55Case body text

    Systems for products that keep evolving

  • t-body-mSatoshi 400 · 16/1.5General text

    Systems for products that keep evolving

  • t-body-m-strongSatoshi 500 · 16/1.5Buttons, links

    Systems for products that keep evolving

  • t-body-sSatoshi 400 · 14/1.45Navigation, metadata

    Systems for products that keep evolving

  • t-body-s-strongSatoshi 500 · 14/1.45Small emphasis

    Systems for products that keep evolving

  • t-body-xsSatoshi 400 · 13/1.4Chips

    Systems for products that keep evolving

  • t-body-xs-strongSatoshi 500 · 13/1.4Pills

    Systems for products that keep evolving

  • t-quoteSatoshi 400 · 28/1.25 · -0.005emQuotes and testimonials

    Systems for products that keep evolving

  • t-label-monoGeist Mono 400 · 12/1.2 · 0.06emUppercase labels

    Systems for products that keep evolving

04

Spacing and shape

A 4 px scale. Steps grow as space separates bigger things: 4 between an icon and its label, 96 between sections.

Space

  • space-14
  • space-28
  • space-312
  • space-416
  • space-520
  • space-624
  • space-832
  • space-1040
  • space-1248
  • space-1664
  • space-2080
  • space-2496
  • space-30120

Radius

  • radius-sm6
  • radius-md12
  • radius-lg16
  • radius-full999

Stroke

  • stroke-default1
  • stroke-accent2

05

Motion

Durations and curves are tokens too. With reduced motion on, every duration drops to 0 and the site shows up without animating.

You have reduced motion on: the bars jump to the end without animating.

Duration · with ease-standard

  • instant 100ms
  • fast 160ms
  • base 240ms
  • slow 480ms
  • deliberate 800ms

Curve · over duration-deliberate

  • standard cubic-bezier(0.2, 0, 0, 1)
  • out cubic-bezier(0.16, 1, 0.3, 1)
  • in-out cubic-bezier(0.65, 0, 0.35, 1)

06

Components

Every component has the same name in Figma and in code. Figma only holds the variants; hover, focus and disabled are handled in CSS.

  • Pill

    Figma properties: Variant=Outline|Accent, Size=M|S

    Compact labels and controls. The ones that act are buttons; the ones that inform are text.

    iOS and AndroidShippediOS and AndroidShipped
  • Button

    Figma properties: Variant=Primary|Secondary|Ghost

    Hover, focus and disabled are handled in CSS; Figma only holds the variants.

    Decision · 2026-10-05Variant and state kept apart: Figma only holds variants and CSS handles states, so combinations don’t multiply.

  • Case row

    Figma properties: Media=Right|Left

    Label, title and three pills: role, platform and project status.

  • Card

    Figma properties: State=Default|Hover

    Label, title, body and link. The whole card is clickable; focus shows on the border.

    02 · Exploration

    Glup, from Figma to React Native

    A hydration app with offline logging, Supabase and physics-driven bubbles drawn in Skia.

  • Section header

    Figma properties: Label, Meta, Show meta

    Numbered mono label with an optional detail on the right.

    06 — Components

    Section header

The header and the footer are system components too: they sit at the top and bottom of this page.

07

System rules

Few rules, no exceptions. Each one says how it’s kept.

  1. RuleTwo named modes, Light and Dark, each with its own palette.

    How it’s keptDark mode uses night and snow instead of inverting light. The semantic table shows both side by side.

  2. RuleEvery semantic token points to a primitive.

    How it’s keptThe site build stops if an alias points to a primitive that doesn’t exist.

  3. RuleNo component uses a loose color.

    How it’s keptComponents only read semantic tokens, which is why the same code works in both modes.

  4. RuleIn Figma, components only have variants.

    How it’s keptCSS handles hover, focus and disabled, so combinations don’t multiply in the file.

  5. RuleAll text passes AA in both modes.

    How it’s keptContrast on this page is computed on every build from each mode’s real values.