UI/UX Design Module 6 – Visual design for interfaces
Typography for interfaces
Choose a type scale, line length and line height for MealBox screens, then test text at 200 % size, under user spacing overrides and in Hindi.
What you will learn
- Choose sizes for interface text from a type scale
- Set line length and line height for comfortable reading
- Design for 200 % text, Dynamic Type and user text-spacing overrides
- Set Devanagari labels so that no mark is clipped
Before you start
What you’ll learn · Before you start
What you will learn
- Choose sizes for interface text from a type scale
- Set line length and line height for comfortable reading
- Design for 200 % text, Dynamic Type and user text-spacing overrides
- Set Devanagari labels so that no mark is clipped
Before you start
On this page
A MealBox help article set in 14 px type at a line height of 1.2 runs about 174 characters to a line on a 1280 px screen, more than twice the 80 characters that WCAG 2.2 sets at its highest conformance level. The screen, article and widths in this lesson are invented.
Typography is most of an interface, because most of an interface is text. You will choose sizes from a type scale, set the length and height of lines, and then test the result where designs break: when a visitor enlarges the text, restyles its spacing or reads it in Hindi. All three tests use numbers you can compute.
A type scale gives every level a size you can defend
A type scale: A short list of text sizes in a fixed ratio, for example each step 1.2 times the one before, from which every size in a design is taken. is a short list of sizes in a fixed ratio, so that each level of text is a clear step from the next. This lesson’s scale starts from the 16 px body size and multiplies by 1.2 at each step, a ratio chosen for this lesson. Start from the platform’s own default: MDN documents that a browser’s default font size is usually 16 px, and Apple’s guidance lists 17 pt as the default and 11 pt as the minimum on iOS and iPadOS.
| Platform | Default | Minimum |
|---|---|---|
| iOS and iPadOS | 17 pt | 11 pt |
| macOS | 13 pt | 10 pt |
| Browsers | 16 px | none set |
The program prints the seven steps in px and rem, the unit that follows the visitor’s own default size, and checks that rounding to whole pixels keeps the ratio:
# A type scale for MealBox articles: each step is 1.2 times the one before, starting from the 16 px body size.
BASE_PX = 16
RATIO = 1.2
STEPS = [(-1, "caption"), (0, "body"), (1, "small heading"), (2, "heading"), (3, "large heading"), (4, "page title"), (5, "display")]
print(f"ratio {RATIO}, base {BASE_PX} px = 1 rem")
print(f"{'step':>4} {'role':14}{'px':>7}{'rem':>8} {'rounded px':>10}")
for step, role in STEPS:
px = BASE_PX * RATIO ** step
print(f"{step:>4} {role:14}{px:>7.2f}{px / BASE_PX:>8.3f} {round(px):>10}")
sizes = [BASE_PX * RATIO ** s for s, _ in STEPS]
gaps = [b / a for a, b in zip(sizes, sizes[1:])]
print("\nratio between neighbours:", ", ".join(f"{g:.2f}" for g in gaps))
rounded = [round(s) for s in sizes]
print("neighbour ratios after rounding to whole px:", ", ".join(f"{b / a:.2f}" for a, b in zip(rounded, rounded[1:]))) Output
ratio 1.2, base 16 px = 1 rem step role px rem rounded px -1 caption 13.33 0.833 13 0 body 16.00 1.000 16 1 small heading 19.20 1.200 19 2 heading 23.04 1.440 23 3 large heading 27.65 1.728 28 4 page title 33.18 2.074 33 5 display 39.81 2.488 40 ratio between neighbours: 1.20, 1.20, 1.20, 1.20, 1.20, 1.20 neighbour ratios after rounding to whole px: 1.23, 1.19, 1.21, 1.22, 1.18, 1.21
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 type_scale.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
The page title lands at 33.18 px and the display size at 39.81 px. Rounded to whole pixels the neighbouring ratios wander between 1.18 and 1.23, which is why a scale is stored as numbers, not rounded by eye. Use three or four steps on one screen and let weight and colour do the rest, as the previous lesson showed.
Set sizes in rem
A size in rem scales with the visitor’s chosen default; a size in px does not. The rem column of the output is the value to put in the style sheet.
Checkpoint
Check: A type scale and the body size
Line length and line height set how far a reader gets
A line that is too long loses the reader on the way back to the next line, and one that is too short breaks sentences into fragments. WCAG 2.2 success criterion 1.4.8, a level AAA criterion, asks for lines of no more than 80 characters, or 40 for Chinese, Japanese and Korean, and for line spacing of at least one and a half within paragraphs. MDN gives the same minimum of 1.5 for the main paragraph text and recommends a unitless value, which scales with the font size.
The program estimates characters per line from the column width, assuming an average character is half an em wide:
# How many characters fit on one line of body text? An estimate for a MealBox article, with invented page widths.
# Assumption: an average character of a Latin sans-serif face is about 0.5 em wide (real faces differ).
FONT_PX = 16
CHAR_EM = 0.5
LIMIT = 80 # the longest line WCAG 2.2 success criterion 1.4.8 (AAA) allows, in characters
PAGES = [(360, 16), (768, 32), (1280, 32)] # (window width, side margin) in px
CAP_PX = 640 # a cap on the column: 40 rem at 16 px
def chars(width_px):
return width_px / (FONT_PX * CHAR_EM)
print("window column chars/line within 80 with a 640 px cap")
for width, margin in PAGES:
column = width - 2 * margin
capped = min(column, CAP_PX)
print(f"{width:>6} {column:>6} {chars(column):>10.0f} {'yes' if chars(column) <= LIMIT else 'no':9} {chars(capped):.0f} chars, {'yes' if chars(capped) <= LIMIT else 'no'}")
print(f"\nlongest column for {LIMIT} characters at {FONT_PX} px: {LIMIT * FONT_PX * CHAR_EM:.0f} px ({LIMIT * CHAR_EM:.0f} em)") Output
window column chars/line within 80 with a 640 px cap 360 328 41 yes 41 chars, yes 768 704 88 no 80 chars, yes 1280 1216 152 no 80 chars, yes longest column for 80 characters at 16 px: 640 px (40 em)
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 line_length.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
At 360 px the 328 px column holds 41 characters, comfortably short. At 768 px it holds 88 and at 1280 px it holds 152, both over 80. A cap of 640 px on the column, which is 40 em, brings both to exactly 80. The estimate depends on the typeface, so measure the real page when the number is close.
Two more checks decide whether these settings hold in real use. Text enlarged to 200 % or restyled by the visitor must still fit its box. And scripts such as Devanagari need taller line boxes, because their marks rise higher than Latin letters do.
Checkpoint
Check: Line length and line height
Text that survives enlarging and user spacing, with numbers Pro: opens with a Pro, Premium or Ultimate pass
A design that fits at 100 % can fail the moment a visitor changes their settings. This part reproduces the failure with a button, shows the rule that prevents it, and sets out what the platforms ask for.
In this part:
- What 200 % text and Dynamic Type require
- Four user overrides, tried on a button
- Choosing between fixed and flexible boxes
1 diagram · 1 runnable example (Python) · about 5 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Devanagari marks that fit the line box, measured from a font file Pro: opens with a Pro, Premium or Ultimate pass
Latin line heights that look generous can clip the vowel signs of Hindi, and the clipping is invisible when only English is checked. This part measures the clipping from a real font file, shows the arithmetic, and ends with an exercise on three Hindi buttons.
In this part:
- How far Devanagari marks rise, from the font’s own tables
- The line box and the half-leading arithmetic
- Fixing three Hindi buttons
- Fix a MealBox article page and its Hindi buttons
- Results of the sample tests
1 diagram · 1 runnable example (Python) · 1 exercise with sample tests · about 7 minutes
Opening this part with your pass…Pro part. It opens with the same pass as the part above.
Checking a type system with numbers, before and after Pro: opens with a Pro, Premium or Ultimate pass
A type check is quicker and less personal when each check is a number with a threshold. This part runs one invented article through four checks, shows what fixing it costs in length, and sets out a rubric a team can reuse.
In this part:
- Four checks with thresholds and sources
- What the fix costs, in length
- A rubric for checking type
1 runnable example (Python) · about 3 minutes
Opening this part with your pass…Pro part. It opens with the same pass as the part above.
Practise
The questions below cover the scale and the line settings, and the first one comes from whichever you have found hardest:
Practice that adapts to you
Practise this lesson
8 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up: how do you pick the body text size for a new product?
Start from the platform’s default, which is 16 px in browsers and 17 pt on iOS, and treat it as a floor, not a target. Check it against the people who will read it, since long articles and older readers need the default or more. Set the size in rem so that a visitor’s own setting carries through, then test at 200 % and with a real paragraph in every script you ship. Choose the other sizes from a scale by ratio, and keep to three or four on one screen.
Three harder questions follow. Think about what you would measure first, before you open any of them:
-
Mid to senior
Ward Desk officers write reply letters in a template set at 13 px, line height 1.3, across a 1,100 px column. Officers say they tire by the afternoon, and a manager wants 18 px type to fix it. What do you check and change?
-
Mid to staff
MealBox status chips in an order table are 28 px tall. A tester who enlarged the browser text to 200 % sees the labels cut off. The engineer says it is an edge case and not worth fixing. What do you do?
-
Senior to staff
Ward Desk's Hindi buttons look fine in the design file, but on devices the tops of some vowel signs are cut off. The English buttons are fine. The designer says the font is the same. How do you find the cause and fix it?
Flashcards · 6 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
What is a type scale, and where should it start?
A short list of sizes in a fixed ratio, so each level of text is a clear step from the next. Start from the platform's default body size and multiply, as this track's scale does by 1.2 from 16 px; use three or four steps on one screen.
-
What default and minimum text sizes do browsers and iOS use?
Browsers usually default to 16 px, with no minimum set; Apple lists 17 pt as the default and 11 pt as the minimum on iOS and iPadOS, and 13 pt and 10 pt on macOS. Treat the default as a floor for body text, not a target.
-
Why set text sizes in rem rather than px?
A size in rem scales with the visitor's chosen default font size; a size in px does not.
-
How long may a line of body text be under WCAG 2.2 criterion 1.4.8?
At most 80 characters, or 40 for Chinese, Japanese and Korean, at level AAA. Cap the column in em, such as 40 em, rather than in pixels, and measure the real page when the estimate is close.
-
What line height suits body text, and in which unit?
At least 1.5 within paragraphs, as WCAG 1.4.8 and MDN give it, written as a unitless value so it scales with the font size.
-
Where do typographic settings usually break?
When a visitor enlarges text to 200 % or restyles its spacing, so text must still fit its box, and in scripts such as Devanagari, whose marks rise higher than Latin letters and need taller line boxes.
Key takeaways
- A type scale is a fixed ratio from a body size that starts at the platform default, and sizes in rem follow the visitor’s own setting.
- A line holds at most 80 characters, so cap the column in em, not in pixels, and measure the real page when the number is close.
- Line height is unitless and at least 1.5 for body text, so that it scales with the font size.
- Text must still fit its box when it is enlarged to 200 % or restyled by the visitor.
- Devanagari marks rise as far as the font’s ascent, so a line height that suits Latin letters can clip Hindi.
Terms in this lesson
- Dynamic Type
- An Apple system setting that lets people choose how large text appears across apps; apps respond by scaling their text styles.
- half-leading
- Half of the difference between a line's height and its font's content area. CSS adds it above and below the text when it lays out a line box.
- type scale
- A short list of text sizes in a fixed ratio, for example each step 1.2 times the one before, from which every size in a design is taken.
References
- Typography (Apple)
- Layout (Apple)
- Accessibility (Apple)
- Understanding Success Criterion 1.4.8 Visual Presentation (W3C)
- Understanding Success Criterion 1.4.12 Text Spacing (W3C)
- Understanding Success Criterion 1.4.4 Resize Text (W3C)
- font-size (MDN Web Docs)
- line-height (MDN Web Docs)
- SIL Open Font License (SIL International)
- Noto Sans Devanagari (Fontsource package, version 5.3.0) (Fontsource)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress