ЁЯПл The SchoolтА║ЁЯУМ UIтА║ЁЯО╜ рдзрдбрд╛ 08 тАФ Design systems рдЖрдгрд┐ tokens: рд╢рд╛рд│реЗрдЪрд╛ рдЧрдгрд╡реЗрд╢
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯО╜ рдзрдбрд╛ 08 тАФ Design systems рдЖрдгрд┐ tokens: рд╢рд╛рд│реЗрдЪрд╛ рдЧрдгрд╡реЗрд╢

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 08 ┬╖ рдорд╛рдЧреАрд▓: lesson-07-frameworks ┬╖ рдкреБрдвреАрд▓: lesson-09-performance


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рдзрдбреЗ 01тАУ07, рдЖрдгрд┐ рддреНрдпрд╛рд╕реЛрдмрдд рдЕрдиреЗрдХ рд▓реЛрдХ рдмрджрд▓ рдХрд░рдд рдЕрд╕рддрд╛рдирд╛рд╣реА рдлрд▓рдХ рдПрдХрд╕рд╛рд░рдЦрд╛ рдХрд╕рд╛ рд░рд╛рд╣рддреЛ: design tokens (рдирд╛рд╡ рджрд┐рд▓реЗрд▓реЗ рдирд┐рд░реНрдгрдп), рдЕрдВрддрд░ рдЖрдгрд┐ рдЕрдХреНрд╖рд░рд╛рдВрд╕рд╛рдареА scales, рдЖрдгрд┐ token swap рдореНрд╣рдгреВрди dark mode.

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рдкрд╛рдЪ рд▓реЛрдХрд╛рдВрдирд╛ рдПрдХ рдлрд▓рдХ рджреНрдпрд╛ рдЖрдгрд┐ рдХреЛрдгрддреЗрд╣реА рдирд┐рдпрдо рджреЗрдК рдирдХрд╛, рддрд░ рддреБрдореНрд╣рд╛рд▓рд╛ рдкрд╛рдЪ рдирд┐рд│реЗ рд░рдВрдЧ, рд░рд╛рдЦрд╛рдбреАрдЪреНрдпрд╛ рдЪрд╛рд░ рдЫрдЯрд╛, 7px, 13px рдЖрдгрд┐ 18px рдЪреЗ рдЕрдВрддрд░, рдЖрдгрд┐ рдмрдЯрдгрд╛рдВрдЪреНрдпрд╛ рддреАрди рд╢реИрд▓реА рдорд┐рд│рддрд╛рдд. рдХрд╛рд╣реАрдЪ рдЪреВрдХ рдирд╛рд╣реА; рдлрдХреНрдд рдлрд▓рдХ рдкрд╛рдЪ рдлрд▓рдХрд╛рдВрд╕рд╛рд░рдЦрд╛ рджрд┐рд╕рддреЛ.

рдЧрдгрд╡реЗрд╢ рд╣реЗ рджреБрд░реБрд╕реНрдд рдХрд░рддреЛ ЁЯО╜. "рд╕рдЧрд│реЗ рдПрдХрдЪ рдХрдкрдбреЗ рдШрд╛рд▓рддрд╛рдд" рдЕрд╕реЗ рдирд╛рд╣реА, рддрд░ "рдкреНрд░рддреНрдпреЗрдХ рдирд┐рд░реНрдгрдп рдПрдХрджрд╛рдЪ рдШреЗрддрд▓рд╛ рдЬрд╛рддреЛ рдЖрдгрд┐ рддреНрдпрд╛рд▓рд╛ рдирд╛рд╡ рджрд┐рд▓реЗ рдЬрд╛рддреЗ":

:root {
  --bg: #f8fafc;  --ink: #0f172a;  --accent: #db2777;
  --space-1: 6px; --space-2: 12px; --space-3: 20px;
  --radius: 12px;
}

рдЖрддрд╛ stylesheet рдордзреНрдпреЗ рдХреБрдареЗрд╣реА #db2777 рд▓рд┐рд╣рд┐рд▓реЗрд▓реЗ рдирд╛рд╣реА тАФ рддрд┐рдереЗ var(--accent) рд▓рд┐рд╣рд┐рд▓реЗрд▓реЗ рдЖрд╣реЗ. Token рдПрдХрджрд╛ рдмрджрд▓рд╛ рдЖрдгрд┐ рд╕рдВрдкреВрд░реНрдг рдлрд▓рдХ рдмрджрд▓рддреЛ. рдпрд╛ рдирд╛рд╡ рджрд┐рд▓реЗрд▓реНрдпрд╛ рдирд┐рд░реНрдгрдпрд╛рдВрдирд╛ design tokens рдореНрд╣рдгрддрд╛рдд, рдЖрдгрд┐ рддреА рд╕рдЧрд│реНрдпрд╛рдд рдЫреЛрдЯреА рдЙрдкрдпреЛрдЧреА design system рдЖрд╣реЗ.

рд╕рдЧрд│реНрдпрд╛рдд рдЫрд╛рди рднрд╛рдЧ рдореНрд╣рдгрдЬреЗ dark mode ЁЯМЩ: рдкреНрд░рддреНрдпреЗрдХ component рд░рдВрдЧ рди рдорд╛рдЧрддрд╛ var(--bg) рдЖрдгрд┐ var(--ink) рдорд╛рдЧрддреЛ, рдореНрд╣рдгреВрди theme рдмрджрд▓рдгреЗ рдореНрд╣рдгрдЬреЗ рд╕рд╣рд╛ tokens рдкреБрдиреНрд╣рд╛ рдард░рд╡рдгреЗ. рджреБрд╕рд░реА stylesheet рдирд╛рд╣реА. Elements рдмрджрд▓рдгрд╛рд░реА JavaScript рдирд╛рд╣реА. рд╕рд╣рд╛ рдУрд│реА.

рдЖрдгрд┐ рдлрд▓рдХ рд╡рд╛рдЪрдХрд╛рдЪрд╛ рджреЛрдирджрд╛ рдЖрджрд░ рдХрд░рддреЛ: рддреЛ system setting (prefers-color-scheme) рдкрд╛рд│рддреЛ рдЖрдгрд┐ toggle рдиреЗ (<html> рд╡рд░рдЪрд╛ data-theme) рддреА рдмрджрд▓реВ рджреЗрддреЛ, рдЬреА рдирд┐рд╡рдб localStorage рдордзреНрдпреЗ рд▓рдХреНрд╖рд╛рдд рдареЗрд╡рд▓реА рдЬрд╛рддреЗ.

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart TB
    tok[":root tokens тАФ --bg --ink --accent --space-2 --radius --font"]
    comp["components only use var(--token) тАФ never a raw colour"]
    sys["@media (prefers-color-scheme: dark) тЖТ redefine 6 tokens"]
    user[':root[data-theme="dark"] тЖТ the toggle wins, saved in localStorage']
    tok --> comp
    tok --> sys --> comp
    tok --> user --> comp
    note["change --accent once тЖТ the whole board changes"]

тЭУ рдХрд╛рдп

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг рдПрдХрд╕рд╛рд░рдЦреЗрдкрдгрд╛ рд╣реЗ рдЕрд╕реЗ рд╡реИрд╢рд┐рд╖реНрдЯреНрдп рдЖрд╣реЗ рдЬреЗ users рдирд╛ "рд╣реЗ рдЪрд╛рдВрдЧрд▓реЗ рдмрдирд╡рд▓реЗ рдЖрд╣реЗ" рдЕрд╕реЗ рдЬрд╛рдгрд╡рддреЗ, рдЖрдгрд┐ token рдкрддреНрд░рдХ redesign рд▓рд╛ "300 рдард┐рдХрд╛рдгреА рдмрджрд▓ рдХрд░рд╛" рд╡рд░реВрди "рд╕рд╣рд╛ рдХрд┐рдорддреА рдмрджрд▓рд╛" рд╡рд░ рдЖрдгрддреЗ. Dark mode, brand рдмрджрд▓ рдЖрдгрд┐ accessibility рджреБрд░реБрд╕реНрддреНрдпрд╛ рд╕рдЧрд│реНрдпрд╛ рдПрдХрд╛ рдУрд│реАрдЪреА рдХрд╛рдореЗ рдмрдирддрд╛рдд.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

ui/styles.css рдЪрд╛ рд╡рд░рдЪрд╛ рднрд╛рдЧ: tokens рдЪрд╛ рдПрдХ :root block, рдордЧ prefers-color-scheme рдЦрд╛рд▓рдЪрд╛ dark-mode override, рдордЧ рд╕реНрдкрд╖реНрдЯ [data-theme="dark"] block. рдЦрд╛рд▓рдЪрд╛ рдкреНрд░рддреНрдпреЗрдХ рдирд┐рдпрдо var(--тАж) рд╡рд╛рдкрд░рддреЛ. ui/app.js рдЪреНрдпрд╛ section 5 рдордзрд▓рд╛ toggle data-theme рд▓рд╛рд╡рддреЛ, aria-pressed рдЙрд▓рдЯрд╡рддреЛ, рдЖрдгрд┐ рдирд┐рд╡рдб save рдХрд░рддреЛ.

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

python3 ui/mock_api.py
# 1) one-line rebrand: change --accent to #0d9488 in styles.css, reload тАФ buttons, links, focus rings, grades, all of it
# 2) dark mode three ways: the ЁЯМЩ toggle ┬╖ your OS setting (system тЖТ dark, with the toggle unset) ┬╖ DevTools тЖТ Rendering тЖТ Emulate prefers-color-scheme
# 3) break the uniform on purpose: set .card .grade { color: #ff00aa } and see how a raw colour survives the theme switch badly
# 4) add a token: --shadow: 0 1px 3px rgb(0 0 0 / .08); use it on .card, and add a darker one under the dark blocks
# 5) check contrast: DevTools тЖТ inspect .status тЖТ colour swatch shows the ratio (aim тЙе 4.5:1)

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

рдПрдХрд╛ token рдмрджрд▓рд╛рдиреЗ рд╕рдВрдкреВрд░реНрдг рдлрд▓рдХрд╛рдЪрд╛ accent рдкреБрдиреНрд╣рд╛ рд░рдВрдЧрддреЛ. Dark mode toggle рдиреЗ рдЖрдгрд┐ OS setting рдиреЗ рдЪрд╛рд▓рддреЛ, рдЖрдгрд┐ toggle рд▓рд╛рд╡рд▓реЗрд▓рд╛ рдЕрд╕реЗрд▓ рддрд░ рддреЛ рдЬрд┐рдВрдХрддреЛ тАФ рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ component рдЪрд╛ code рддрд╕рд╛рдЪ рд░рд╛рд╣рддреЛ. рддреБрдордЪрд╛ рдереЗрдЯ рдЧреБрд▓рд╛рдмреА .grade dark mode рдордзреНрдпреЗрд╣реА рдЧреБрд▓рд╛рдмреАрдЪ рд░рд╛рд╣рддреЛ: tokens рдиреЗрдордХреЗ рд╣реЗрдЪ рд╡рд┐рд╕реНрдХрд│реАрдд рд╣реЛрдгреЗ рдерд╛рдВрдмрд╡рддрд╛рдд.

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

рджрд┐рд╕рдгреНрдпрд╛рдЪрд╛ рдкреНрд░рддреНрдпреЗрдХ рдирд┐рд░реНрдгрдп рдПрдХрд╛рдЪ рдард┐рдХрд╛рдгреА рд░рд╛рд╣рддреЛ, themes рдореНрд╣рдгрдЬреЗ token swap, рдЖрдгрд┐ рдХреЛрдгрддреА theme рдЪрд╛рд▓реВ рдЖрд╣реЗ рд╣реЗ component рд▓рд╛ рдХрдзреАрдЪ рдорд╛рд╣реАрдд рдЕрд╕рдгреНрдпрд╛рдЪреА рдЧрд░рдЬ рдирд╕рддреЗ.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: design systems рдЕрд╕реНрддрд┐рддреНрд╡рд╛рдд рдЖрд╣реЗрдд рдХрд╛рд░рдг рдорд╛рдгрд╕реЗ рд╡рд╛рдврд▓реА рдХреА рдПрдХрд╕рд╛рд░рдЦреЗрдкрдгрд╛ рдЯрд┐рдХрдд рдирд╛рд╣реА. Tokens рд╣реЗ рд╕рдЧрд│реНрдпрд╛рдд рд╕реНрд╡рд╕реНрдд 80% рдЖрд╣реЗрдд тАФ рдПрдХ file, рдХреЛрдгрддреЗрд╣реА tooling рдирд╛рд╣реА, рдкреНрд░рддреНрдпреЗрдХ framework рдордзреНрдпреЗ рдЖрдгрд┐ рд╕рд╛рдзреНрдпрд╛ CSS рдордзреНрдпреЗ рдЪрд╛рд▓рддрд╛рдд.

тПня╕П рдкреБрдвреЗ

рддреЗ рдмрд░реЛрдмрд░ рджрд┐рд╕рддреЗ тАФ рдЖрддрд╛ рддреЗ рдкрдЯрдХрди рдкреЛрд╣реЛрдЪрд╡рд╛: performance тАФ bytes, images, caching рдЖрдгрд┐ Core Web Vitals.

git checkout lesson-09-performance

ЁЯО╜ Lesson 08 тАФ Design systems & tokens: the school uniform

ЁЯУН You are here: Lesson 08 of 12 ┬╖ Previous: lesson-07-frameworks ┬╖ Next: lesson-09-performance


ЁЯУж What's in this branch

Lessons 01тАУ07, plus how a board stays consistent while many people edit it: design tokens (named decisions), scales for spacing and type, and dark mode as a token swap.

ЁЯзТ Explain like I'm 5

Give five people a board and no rules and you get five blues, four shades of grey, spacing of 7px, 13px and 18px, and three button styles. Nothing is wrong; the board just looks like five boards.

A uniform fixes it ЁЯО╜. Not "everyone wears the same thing", but "every decision is made once and given a name":

:root {
  --bg: #f8fafc;  --ink: #0f172a;  --accent: #db2777;
  --space-1: 6px; --space-2: 12px; --space-3: 20px;
  --radius: 12px;
}

Now nothing in the stylesheet says #db2777 тАФ it says var(--accent). Change the token once and the whole board changes. These named decisions are design tokens, and they are the smallest useful design system.

The best part is dark mode ЁЯМЩ: because every component asks for var(--bg) and var(--ink) rather than for a colour, switching themes is redefining six tokens. Not a second stylesheet. Not JavaScript editing elements. Six lines.

And the board respects the reader twice: it follows the system setting (prefers-color-scheme) and lets the person override it with the toggle (data-theme on <html>), remembered in localStorage.

ЁЯЧ║я╕П Diagram

flowchart TB
    tok[":root tokens тАФ --bg --ink --accent --space-2 --radius --font"]
    comp["components only use var(--token) тАФ never a raw colour"]
    sys["@media (prefers-color-scheme: dark) тЖТ redefine 6 tokens"]
    user[':root[data-theme="dark"] тЖТ the toggle wins, saved in localStorage']
    tok --> comp
    tok --> sys --> comp
    tok --> user --> comp
    note["change --accent once тЖТ the whole board changes"]

тЭУ What

ЁЯдФ Why

Because consistency is a feature users feel as "this is well made", and because a token sheet turns a redesign from "edit 300 places" into "edit six values". Dark mode, brand changes and accessibility fixes all become one-line operations.

ЁЯФз How (in this repo)

The top of ui/styles.css: a :root block of tokens, then the dark-mode override under prefers-color-scheme, then the explicit [data-theme="dark"] block. Every rule below uses var(--тАж). The toggle in ui/app.js section 5 sets data-theme, flips aria-pressed, and saves the choice.

ЁЯзк Try it

python3 ui/mock_api.py
# 1) one-line rebrand: change --accent to #0d9488 in styles.css, reload тАФ buttons, links, focus rings, grades, all of it
# 2) dark mode three ways: the ЁЯМЩ toggle ┬╖ your OS setting (system тЖТ dark, with the toggle unset) ┬╖ DevTools тЖТ Rendering тЖТ Emulate prefers-color-scheme
# 3) break the uniform on purpose: set .card .grade { color: #ff00aa } and see how a raw colour survives the theme switch badly
# 4) add a token: --shadow: 0 1px 3px rgb(0 0 0 / .08); use it on .card, and add a darker one under the dark blocks
# 5) check contrast: DevTools тЖТ inspect .status тЖТ colour swatch shows the ratio (aim тЙе 4.5:1)

тЬЕ Verify тАФ what you should see

One token change repaints the whole board's accent. Dark mode works from the toggle and from the OS setting, and the toggle wins when set тАФ while every component's code is untouched. Your raw pink .grade stays pink in dark mode: that is exactly the drift tokens prevent.

ЁЯПБ What you just proved

Every look decision lives in one place, themes are a token swap, and a component never needs to know which theme is active.

тЪая╕П Common mistakes

ЁЯПн Why this matters in production: design systems exist because consistency does not survive headcount. Tokens are the cheapest 80% тАФ one file, no tooling, works in every framework and in plain CSS.

тПня╕П Next

It looks right тАФ now make it arrive quickly: performance тАФ bytes, images, caching and Core Web Vitals.

git checkout lesson-09-performance
тЖР PreviousframeworksNext тЖТperformance

This page is the lesson's README from the lesson-08-design-tokens branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.