Design systems: typography, colour, spacing and icons¶
Exercise design-systems-annex.pdf · PDF
Friday, 17:00. A client calls: "We changed our brand. The app must be green instead of yellow, and the demo is tomorrow at nine." You open the file and click the New note button. Its fill is #F5C400. You click Save: #FFCC00. Which one is the brand yellow?
Nobody knows, and neither does the developer. The problem is not the repainting. The decision "our yellow" was never written down anywhere: it was typed, twice, differently. This page shows you where such decisions should live. Our example is NoteBox, a small notes app, and you will write its decisions once and never type a colour again.
What you will learn
- Define a design system and its three layers: tokens, components and rules
- Build a type scale with one typeface, and justify each size
- Name colours by their role, and check every text pair against 4.5:1
- Pick every gap from a spacing scale, and say why 14 is not on it
- State the three rules of an icon set
- Build it all in Figma with variables, text styles and a button with variants
- The trap: a design system makes a change instant, but it does not make the change right
Figma: frames, auto layout, styles, variables and components explains what those Figma tools are. This page answers the next question: what do you put in them?
The catalogue in the box¶
We go back to the LEGO factory from the Figma page. A LEGO set works because of three things in the box: the colour chart (every red brick is the same red), the moulds (every 2 × 4 brick is identical) and the building instructions (which brick goes where). Take one away and you have a bag of plastic.
A design system is the shared decisions of a product, written once and reused everywhere.
It has three layers:
| Layer | What it is | In the factory | In Figma |
|---|---|---|---|
| Tokens | The smallest named decisions: a colour, a size, a gap | The colour chart | Variables and styles |
| Components | Reusable pieces built from tokens | The moulds | Components and variants |
| Rules | When to use which piece | The building instructions | Your notes, and later the documentation |
flowchart LR
T["<b>Tokens</b><br>the colour chart:<br>color/brand, space/16"] -->|"are used to build"| C["<b>Components</b><br>the moulds:<br>Button, Row"]
C -->|"are placed according to"| R["<b>Rules</b><br>the instructions:<br>one main button per screen"]
R --> S["<b>Screens</b><br>consistent, without<br>deciding everything again"]
If you know Java, you have met this idea already. Why write static final int MAX_USERS = 100 instead of typing 100 in twelve places? So there is one name to read and one place to change it. A token is that constant, for design.
You have seen real design systems. Material Design, which Google presented in June 2014, is a public one: tokens, components and rules at m3.material.io. This site is another. Count its fonts and colours: three fonts, a few flat inks, square corners, everywhere. Someone decided that once.
The term design token was coined around 2014 by Jina Anne and Jon Levine at Salesforce. Since October 2025 there is even a stable format, from a W3C community group, for moving tokens between tools.
Careful: a design system is not a Figma file
The file is one copy of it. The system is the set of decisions. The same tokens will become CSS variables and the same pieces will become Svelte components when you reach Unit 3. Figma, CSS and Svelte each hold a copy of the same decisions.
A product makes four big kinds of visual decision: typography, colour, spacing and icons. Let us take them one by one.
Typography: few sizes, clearly different¶
Two words people mix up. A typeface is the design, such as Inter. A font is one cut of it, such as Inter Bold 28. NoteBox uses one typeface: Inter, drawn by Rasmus Andersson for screens.
A type scale is the short, fixed list of text sizes a product is allowed to use, each with a job.
| Name | Font | Used for | Why this size |
|---|---|---|---|
| Title | Bold 28 | The screen title: "NoteBox" | The first thing you see (visual hierarchy) |
| Heading | Bold 24 | A section or a dialog: "Share note" | It counts as large text in WCAG, so 3:1 is enough |
| Body | Regular 16 | What you read: the note | The comfortable minimum on a phone |
| Body strong | Semi Bold 16 | Button labels, note names | Same size, more weight: emphasis without a new size |
| Label | Medium 14 | Field labels | Smaller, and still above a field |
| Caption | Medium 13 | Status, hints | The floor. Nothing smaller |
Three rules keep the scale honest:
- One family, two at most. Every extra typeface is a new voice talking.
- Few sizes, clearly different. 16 and 15 are not two levels. They are a mistake.
- Lines you can read. Aim for about 45 to 75 characters per line (Robert Bringhurst, The Elements of Typographic Style, 1992) and a line height, the distance between lines, of around 1.5 times the text size for body text.
And one habit: the style is called Body, not Inter 16. The day you change the typeface, Body is still true.
Colour: roles, not hues¶
A colour role is the job a colour does in the interface: background, error, main action. A palette is a list of those jobs, not five colours that look nice together.
You start from what the interface must say (this is the action, this is an error), and only then pick the colours. Here is NoteBox, with each colour written as a hex code, the six-character value design tools use:
| Role | Light | Dark | Job |
|---|---|---|---|
| surface | #FFFFFF |
#111827 |
The screen background |
| text | #111111 |
#F3F4F6 |
Everything you read |
| text-muted | #595959 |
#9CA3AF |
Subtitles, placeholders, captions: about 7:1 against surface |
| border | #767676 |
#9CA3AF |
Field and button outlines (WCAG asks 3:1) |
| brand | #F5C400 |
#F5C400 |
The one main action per screen |
| on-brand | #111111 |
#111111 |
Text that sits on brand |
| link | #1D4ED8 |
#60A5FA |
Links and the focus ring |
| danger | #B42318 |
#FCA5A5 |
Errors, Delete, "Not synced" |
| success | #15803D |
#86EFAC |
"Synced" |
Three things to notice:
- The name is the job.
dangersurvives a rebrand.reddoes not. Andon-brandexists for a reason: white on that yellow is 1.64:1, unreadable, while#111111on it is 11.49:1. The role makes the safe choice the obvious one. - Mostly neutral. A rule of thumb borrowed from interior design, 60–30–10, says most of the screen is one colour, a good part is a second, and only a little is the accent. In NoteBox: surface, text, brand. If everything is yellow, nothing is.
- Dark mode is a second column, not a second design. Same nine roles, other values. That column is a mode. And you measure every pair again, because contrast rules get no night off.
Spacing: a pick, not an opinion¶
The Figma page showed you that a gap is a number in auto layout. Which numbers?
A spacing scale is the short, fixed list of distances a product is allowed to use.
NoteBox uses 4 · 8 · 12 · 16 · 24 · 32: multiples of 4, with bigger steps as it grows. Material Design builds on an 8 dp grid for the same reason: fewer choices, and every choice visibly different.
| Token | Typical use in NoteBox |
|---|---|
| space-4 | Between a name and its status |
| space-8 | Between a label and its field; between two buttons |
| space-12 | Padding inside a field or a row |
| space-16 | Between the blocks of a screen; side padding |
| space-24 / space-32 | Button padding; top and bottom of the screen |
Is a gap of 14 wrong? It works. But 14 next to 16 is a difference nobody sees and everybody has to remember. A scale turns "how much space?" from an opinion into a pick. It also keeps the proximity rule cheap to follow: small steps inside a group, bigger steps between groups.
Icons: one family, one grid, one stroke¶
An icon set is a collection of icons drawn to the same rules, so they look like they belong together.
Three rules:
- One family. Outline with outline, filled with filled. This site uses one family, Lucide. Google has Material Symbols. Apple has SF Symbols, for Apple platforms only.
- One grid, one stroke. In NoteBox every icon is drawn on a 24 × 24 square with a stroke of 2, with round ends. An 18 px icon with a thinner line next to a 24 px one looks like a bug.
- Never alone. An icon-only button still needs a name. Unless the icon is universal (search, close, back), show the word too.
Try it: what do a floppy disk, a heart and three dots do, in an app you do not know? Save, probably. The heart: like, favourite, health? The three dots: "something".
Careful: icons are not universal
A handful are: search, home, close. The rest are guesses. An icon with its word is faster to find and impossible to misread.
Build it in Figma¶
Now you put the decisions where they cannot drift. The handout has these steps as a checklist, and the starter file is on Classroom. Figma moves some buttons between releases, so if a label is not where described, look near it in the right panel.
First, the one question that catches everyone: which tool holds what? The Figma page explains the difference. In practice:
| Decision | Tool | Why |
|---|---|---|
| Colours, spacing, radius | Variable | One value each, and a variable can have modes |
| The type scale | Text style | A recipe of several values: family, size, weight, line height |
| Buttons, rows, fields | Component | A reusable piece whose instances stay linked |
| Primary or danger, default or disabled | Variant | One version of a component, picked by a property |
Variables: the colour chart¶
- Click the empty canvas. In the right panel, open Local variables, then Create variable → Color. Rename the collection NoteBox.
- Create the nine colours:
color/surface,color/text,color/text-mutedand so on. The slash makes a group in the list. Type the hex, and never use the eyedropper: the eyedropper is how near-identical greys get in. - Create the numbers with Create variable → Number:
space/4tospace/32, andradius/control= 8. - Apply them. Select a button, go to Fill, click the library icon (four dots) and pick
color/brand. On a gap or padding field, use the variable icon and pick aspace/....
The field now shows a name, not a number. That is how you can tell: bound values show the variable's name, typed ones show a hex.
Text styles: the type scale¶
Select a title, go to Typography, click the four-dot icon, then +, and name it Title. Repeat for Heading, Body, Body strong, Label and Caption. From then on, you apply Label to a text instead of choosing a size. You are not fixing one text: you are removing a decision that should never have been made twice.
A button with variants: the mould¶
- Select a finished button and press Option+Cmd+K / Alt+Ctrl+K. Rename it Button. Check it uses
color/brand,color/on-brand, Body strong andradius/control. - Add a variant and name the property Type, with three values. Primary is the original. Secondary has no fill and a 2 px stroke in
color/text. Danger has no fill, with stroke and text incolor/danger. - Add a second property, State: Default and Disabled. Disabled is the same variant at 40 % opacity: dimmed, not removed. You now have six variants in one component set, the dashed purple box that holds them all.
- Select the label inside a variant and, next to the text content in the right panel, choose Create property. Name it Label. Now each instance shows a Label field, with no double-clicking into layers.
- Drag Button onto the screen from the Assets panel (or from Resources, Shift+I). Pick Type and State in the right panel, and type the label.
When something does not behave
- You edit an instance and the others do not follow. Only the main component is the mould. Ctrl+Option+Cmd+K / Shift+Alt+Ctrl+K jumps to it.
- A variant is called "Button 2". Variants are named
Property=Value, such asType=Primary, State=Default. Otherwise the right panel shows no menu. - You want
yelloworgrey-3. Ask what it is for, and rename it by role. - You want to detach for a quick change. A detached instance misses every future change. If the component cannot do what you need, it is missing a variant.
Spot what drifted¶
In the handout, two raw NoteBox screens break the spec in six ways. Your tool is the right panel: click a layer and read its numbers. Look in five places: colour, corner radius, type, space and icons. How do so many mistakes get into two screens by one person? Nobody chose them. Somebody typed them from memory. Typed values drift. Named values cannot.
The proof¶
Now the client calls again. Open your local variables, and change color/brand to #22C55E.
flowchart LR
V["<b>You change one variable</b><br>color/brand = #22C55E"] --> B["<b>Button component</b><br>its fill uses color/brand"]
B --> I["<b>Every instance</b><br>New note, Save..."]
I --> S["<b>Both screens turn green</b><br>in five seconds"]
S --> M["<b>You still measure</b><br>is the label readable?"]
Every screen follows at once. That is the whole lesson in five seconds. But look at the last box. #111111 on that green is 8.29:1, fine. White on it would be 2.28:1, a failure.
The trap: consistent is not the same as correct
A system makes a change instant. It does not make it right. It guarantees consistency, and you still measure every pair, in every mode.
To see dark mode, add a mode in the Local variables window, rename the columns Light and Dark, and fill in the dark values. Then select a screen, go to Appearance and apply Dark. Extra modes need an Education or paid Figma plan, and a file in your own drafts may be on the free Starter plan, so check where your file lives if the option is missing.
Do it for your project
Write your own app's spec sheet in Figma, as variables and text styles: at most 9 colours named by role, one typeface, at most 6 text styles and one spacing scale. Then build one component: your main button, with at least two variants.
Key takeaways¶
- A design system is a product's shared decisions, written once. Its three layers are tokens, components and rules: the colour chart, the moulds and the instructions.
- A type scale is a short list of sizes, each with a job. Use one typeface, and name styles by role (Body, not Inter 16).
- A palette is a list of jobs. Name colours by role (
danger,on-brand), and treat dark mode as a second column of values. - A spacing scale turns "how much space?" into a pick: 4, 8, 12, 16, 24, 32.
- An icon set has one family, one grid and one stroke, and an unclear icon needs its word.
- In Figma, colours and numbers go in variables, typography in text styles, pieces in components. A system makes changes instant, but you still measure the result.
Quick check¶
1. What are the three layers of a design system?
- Tokens, components and rules
- Colours, fonts and icons
- Frames, groups and styles
- Wireframes, prototypes and tests
Tokens are the smallest named decisions, components are the pieces built from them, and rules say when to use which piece. Colours and fonts are examples of what tokens hold, not layers of their own.
2. Why is a colour better named danger than red?
- The name states its job, so it stays true if the colour changes
- Figma rejects variable names that describe a colour
- Screen readers read the name of the variable out loud
- A longer name makes the file load a little faster
A role describes the job in the interface. If danger later becomes orange, the name is still correct and nobody renames anything. A variable called red that holds orange would mislead everyone who reads it.
3. A screen uses body text at 16 and a second text style at 15. What is the problem?
- The sizes are too close to read as two levels
- Text of 15 is too small to read on a phone
- Two text styles on one screen break WCAG
- Size 16 only works with a second typeface
A type scale uses few sizes, clearly different, so each level is visible. 16 and 15 look like a mistake rather than a hierarchy. To emphasise text at the same size, use more weight, as Body strong does.
4. Your spacing scale is 4, 8, 12, 16, 24 and 32, and a designer wants a gap of 14. What should happen?
- Pick 12 or 16, the nearest values on the scale
- Keep 14, since any small difference is harmless
- Add 14 to the scale for this one screen only
- Replace the gap with a line between the items
A spacing scale turns the choice into a pick. A gap of 14 next to 16 is a difference nobody sees and everybody has to remember. If the scale really is wrong, you change it for everyone, never for a single screen.
5. Which Figma tool should hold a complete typography such as Heading: Inter, Bold, 24, with its line height?
- A text style, which stores several values as one recipe
- A colour variable, which stores one value per mode
- A number variable, which stores the size in one place
- A layout guide, which stores the rows and the columns
A variable holds one value, so it suits colours, spacing and radius. A text style is a recipe of several values, so it suits the type scale.
6. A designer puts white text on the NoteBox yellow button and the contrast is 1.64:1. Which role should the text use?
- on-brand
- text-muted
- surface
- link
on-brand exists for text that sits on the brand colour: in NoteBox it is #111111, which reaches 11.49:1 on that yellow. Having a role for it means nobody has to pick that colour again.
7. You change color/brand to a new green, and every screen follows. What must you still do?
- Measure the contrast of the pairs that use it
- Update each instance of the button by hand
- Detach the instances so that they keep the green
- Rename the variable to
color/greento match
A design system makes a change instant, not right. The screens follow by themselves, but you must still check that the label is readable on the new colour, in every mode. Renaming to color/green would break the rule of naming by role.
8. A bus ticket app has its main button redrawn by hand on 30 screens, with slightly different reds typed in. Select the two changes that stop this from happening again.
- Create a colour variable named by role and use it as the button's fill
- Make the button a component and use instances of it on every screen
- Group the buttons of each screen so they move together
- Pick one of the reds by eye and paste it onto every button
The fix follows the layers: a token holds the one red, and a component holds the one button. Then a change reaches all 30 screens from one place. Pasting a colour by hand only fixes today's copies, and the next typed value drifts again.
The pieces exist now. But which piece goes where? Next: Controls, menus and messages, where you choose the right control for each job.