Command Palette

Search for a command to run...

Hectal
PHASE 10Intermediate ~22 min· topic 3 of 6

Topic 10.3

Typography and Annotation

In one line

Chart text is part of the data. Pick sizes that stay readable when the SVG scales, line numbers up with tabular digits, write titles that state the finding, and place annotations so they never sit on the data or run off the edge.

Think of it like this

The road signs on a motorway. They are big, use a few plain words, and say the thing you need ("Exit 4: Harbour"), because you read them at speed from far away. A sign that said "Information regarding exits" in small letters would be correct and useless. Chart text is the same: the reader should get the point from the title before reading any axis.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

user unit
One unit of an SVG's own coordinate system. A viewBox decides how many screen pixels it is.
tabular numbers
Digits that all take the same width, so numbers in a column line up.
finding title
A title that states what the chart shows, not just what it is about.
annotation
A note drawn on the chart: a mark, a leader line and a label.
halo
A stroke in the background colour behind text, so it stays readable over lines.

Step by step

01Measure the size of text in a scaled SVG

The first example places the same 12-unit label in a viewBox at three container widths and prints its real size: 6, 12 and 24 pixels. The number comes from getScreenCTM().a, the pixels per user unit.

02Keep the digits in line

The example then measures 1111 and 8888 with tabular-nums on. In the picture, the two columns of numbers show the difference in fonts where digits have different widths.

03Shorten a label that does not fit

A station name that is too long is cut until it fits, and an ellipsis shows it was cut. The example asks the text itself how long it is (getComputedTextLength), so no guessing.

04Write the finding and place the note

The second example writes the chart's title from the data, annotates the lowest day (14 August, 439 rides) and flips the label for a point near the edge.

Write the finding and place the notediagram
Rendering diagram…

Try it yourself

  1. 1

    Make the label too long

    In the first example, change 90 in fit(t, long, 90) to 300. In the preview's font the label is no longer shortened (a wider font could still cut it). The line prints fits 90 units: false | was shortened: false | ends with an ellipsis: false: the whole label is wider than 90 units, and the text of that check still says 90.

  2. 2

    Move the note to the other side

    In the second example, change px < W / 2 to px > W / 2. The console says both labels go to the right, and the note near 29 December runs off the right edge of the picture and is cut. The halo cannot save a label that has no room: the placement rule matters more.

  3. 3

    Give it no data

    In the second example change the first line to const rows = (await d3.csv("/data/lumen/daily.csv", d3.autoType)).slice(0, 0);. The console shows Uncaught TypeError: Cannot read properties of undefined (reading 'rides'): d3.greatest and d3.least return undefined for an empty list, and the title code reads high.rides. A real chart checks for empty data first and writes a plain title such as "No rides to show".

Code & diagrams

How big is 12-unit text at three widths, and do the digits line up? New tab

The scaling numbers are exact: pixels per unit is container width ÷ 400. The default digits in the left column may or may not line up, depending on the font installed on your computer, which is the point: tabular-nums makes the result the same everywhere the font supports it. The cut label's exact text depends on the font, so the example prints facts about it, not the text.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

container 200 px -> a 12-unit label is drawn 6.0 px tall
container 400 px -> a 12-unit label is drawn 12.0 px tall
container 800 px -> a 12-unit label is drawn 24.0 px tall
tabular-nums: 1111 and 8888 the same width: true
fits 90 units: true | was shortened: true | ends with an ellipsis: true
a short label stays whole: true
A finding title and an annotation that picks its side New tab

In the picture: a yearly line rising from about 1,200 in January to a broad summer peak near 4,000 and falling back, with one narrow spike down to 439 in mid-August where an orange dot, a leader line and the label "14 Aug: app down, 439 rides" sit to the left. The second note, near 29 December, also goes left because there is no room on the right. Both labels have a white halo, so the line does not cross the letters. The halo is white here because this page is light; Topic 10.4 makes it follow the theme.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

title: Rides peaked at 4,380 on 18 Jul, then one day fell to 439
outage label goes to the left
annotations drawn: 1
a note on 29 Dec goes to the left

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Use a title that is wider than the chart

In the second example, change the W to 300 (leave everything else).

terminal
$ Console (and the picture)
── what you'll see ──
title: Rides peaked at 4,380 on 18 Jul, then one day fell to 439
outage label goes to the left
annotations drawn: 1
a note on 29 Dec goes to the left

Myth vs fact

Myth

SVG text stays the same size when the chart scales.

Fact

Text in a viewBox is scaled with everything else (6, 12 and 24 pixels in the first example). To keep a size, redraw at the real width instead of scaling.

Myth

A chart's title should name the topic.

Fact

A name says what the chart is about. A finding says what to see. "Rides peaked at 4,380 on 18 Jul" can be checked against the line in one glance.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    getComputedTextLength() needs the text to be in the document and visible (not display: none), and it depends on the font that is actually loaded. Measure after document.fonts.ready, or the width can change when a web font arrives.

  • ▸

    paint-order: stroke draws the stroke first and the fill on top, so a 3-unit stroke becomes a 1.5-unit halo outside the glyph. Without paint-order, the stroke covers half of each letter.

Remember this

  1. 1

    The problem this solves: the dashboard had 12-pixel labels that looked fine on Arjun's monitor and vanished from the back of the operations room, titles like "Rides" that said nothing, and a note on the outage day that sat on top of the line. Text is not decoration: it is how a chart says what it found.

  2. 2

    Text inside a scaled SVG scales with it. (Recall from Topic 1.4: an SVG with a viewBox maps user units to screen pixels.) A label with font-size="12" in a 400-unit-wide viewBox is 6 pixels tall in a 200-pixel box and 24 pixels tall in an 800-pixel box (first example). So a chart that only scales gets unreadably small on a phone. The fix is to redraw at the real width, so a 12-unit label stays 12 pixels (Topic 10.5).

  3. 3

    Pick a floor and respect it. For body text in a chart, 12 CSS pixels is a sensible minimum for axis labels and 14 or more for the title. Below a floor, drop information (fewer ticks, shorter labels) instead of shrinking the text. Zoom and larger system text sizes are also the reader's right: use relative units (rem) for HTML text.

  4. 4

    Tabular numbers keep columns straight. In many fonts the digit 1 is narrower than 8, so a column of numbers jiggles and digits do not line up. font-variant-numeric: tabular-nums switches the font to equal-width digits, if the font has them. In the first example, in the preview's Arial, 1111 is narrower than 8888 by default and the same width with it on. Some computers' Arial already has equal-width digits, and fonts that lack the feature ignore it, so check on the fonts you ship.

  5. 5

    Titles state the finding, from the data. "Rides per day" is a label. "Rides peaked at 4,380 on 18 Jul, then one day fell to 439" is a finding. Build the sentence from the data in code, so it can never disagree with the chart, and keep it short enough to fit the width (a long one wraps or is cut: the first example's last lines show how to shorten a label with an ellipsis).

  6. 6

    Annotations answer the reader's "what happened here?" A good annotation has a mark on the data point, a leader line, a short label, and a halo (a stroke in the background colour, drawn under the text with paint-order: stroke) so the text stays readable over a line. Placement is a decision made in code: the second example puts the label on the side that has room, and flips it for a point near the right edge.

Explain it without notes

01

Why does text in a scaled SVG get small on a phone, and what is the fix?

02

What does font-variant-numeric: tabular-nums do, and when does it have no effect?

03

What makes a chart title a "finding"? Give the Lumen example.

Practice

01

Write fitTitle(textNode, title, maxWidth) that returns the title unchanged if it fits and otherwise cuts words from the end and adds …. Test with a 200-unit and a 600-unit width.

02

Add a second annotation for the second-lowest day. What happens if it is close to the first one, and how would you avoid two labels on top of each other?

Trade-offs

  • ↔

    A finding title is more useful but must be kept in step with the data: build it in code, and fall back to a plain title when the data is empty.

Done when you can

  • I can say how big a label is on screen for a given viewBox and width.

  • I can use tabular numbers and say when they do nothing.

  • I can write a title that states a finding, from the data.

  • I can place an annotation with a leader line and a halo, on the side that has room.