The makeover, one move at a time
Self-paced bonus activity · Nussbaumer Knaflic’s design moves, in code
Before you start
In Monday’s lecture you saw the before and after of this makeover — the rough draft, and the chart you’d put in a research talk. This page is the middle: the same chart rebuilt five times, each version adding exactly one of Cole Nussbaumer Knaflic’s design moves.
Work through it at your own pace. Every chunk runs in your browser, so change things and re-run — that is the whole point of having it here rather than on a slide. Nothing on this page is assessed.
The six moves
- Understand the context — who’s reading, what do they already know, what action do you want them to take?
- Choose an appropriate display — line, bar, scatter, table; the choice flows from the question, not your tools.
- Eliminate clutter — every pixel that isn’t carrying meaning is hurting the chart.
- Focus attention — color, size, and contrast direct the eye to the finding.
- Think like a designer — alignment, whitespace, hierarchy of typography.
- Tell a story — title-as-finding, annotation as narration, captions that situate.
The first two happen before you write any ggplot: understand the context is the audience-and-action question you answer when you decide why you’re making the chart, and choose an appropriate display is the line-vs-bar-vs-scatter decision that follows from the research question. The five tabs below do everything from move 3 onward.
The data
We’re applying the moves to after-tax income inequality (the Gini coefficient) for five rich countries from 1990 to 2023, from TidyTuesday’s August 2025 release. The data frame inequality_tidy is already loaded in every chunk on this page.
Run str() to see the columns, their types, and the variable labels attached to them:
Three columns — country (text), year (numeric), gini_dhi (numeric). Notice the attr(*, "label")= chr "..." lines: those are variable labels, human-readable names stored as metadata alongside the column.
Why some axes look pre-labeled
The dataset has variable labels attached to its columns (e.g., "Year", "Gini coefficient (disposable household income, post-tax)", "Country"). When ggplot2 draws an axis or legend, it checks for a label first and uses it; only if there’s no label does it fall back to the raw column name. That’s why the charts below show human-readable axis text without you writing labs(y = "...").
This is part of a workflow you’ll learn in M05 (Describing Data with R) — the labelled package and its set_variable_labels() function. The same labels propagate to gtsummary tables and ggplot legends, so you write the human-readable name once and never again.
For now: if an axis looks pre-labeled, the data brought the label with it. You can override any of them with labs(), as the last tab does.
inequality_tidy · 5 countries · 34 years · TidyTuesday 2025-08-05
The five countries are United States, United Kingdom, Germany, France, Sweden — a small comparison set chosen for the makeover, not a sample of “rich countries.” Any claim we make from this chart is a claim about these five.
The measure is the Gini coefficient of equivalized disposable household income, after taxes and most benefits. Higher means more unequal.
Coverage is uneven, and that matters for how you read the lines:
| Country | First year | Last year | Observations |
|---|---|---|---|
| United States | 1990 | 2023 | 34 |
| United Kingdom | 1990 | 2021 | 32 |
| Germany | 1990 | 2020 | 31 |
| France | 1990 | 2020 | 26 |
| Sweden | 1992 | 2021 | 24 |
Only 21 of the 34 years have all 5 countries reporting. The series end in different years, so the right-hand edge of every chart below is thinner than the middle — and geom_line() will happily draw a straight segment across a gap without telling you. Keep that in view when we get to the title.
The makeover in five tabs
Each tab applies one move on top of the previous tab’s code. Click through them in order and read each chunk — the changes from tab to tab are the moves.
This is a bare-bones ggplot() — three lines of code, all five series on screen. It is a perfectly good exploratory chart: you can see the broad pattern, spot the outlier series, and check that nothing looks broken. What it is not is designed for a particular reader or a particular takeaway. That’s the gap the next four tabs close.
(One note on the code: this page sets a clean theme for every chunk, so we ask explicitly for theme_grey() — R’s real default — to show you the actual starting point.)
The gray panel and minor gridlines aren’t carrying meaning — they’re just noise. Drop them. Same data, same lines; less for the eye to filter.
Five equally-weighted colors give the eye nowhere to land — not because five is too many in principle, but because nothing in the design says which comparison carries the story. Our takeaway is about the United States, so we color that series and mute the rest. The design choice follows from the purpose, not from a rule about line counts.
Notice what the previous tab cost us: suppressing the legend told the reader which line matters, but it also left the four gray comparison lines unidentified. Rather than restore a separate key — which would force readers to bounce between chart and legend, and would give equal billing to all five — put each country’s name directly at the end of its own line.
This is the move you already met on the opening chart of Monday’s lecture, where the designer put the three group names, in their own colors, inside the subtitle.
The last move pulls everything together: a title that states the finding, a subtitle that names what’s measured and across what years, a source caption, and one annotation in the chart itself that narrates the takeaway. This is the chart that goes in a research talk.
From Tab 1 to Tab 5 the chart went from “indistinguishable from every other rough draft on the internet” to “the chart you’d put in a research talk.” Scroll back through the tabs to see exactly which move each step applied — that’s the workflow Nussbaumer Knaflic teaches, made explicit in code.
The annotation layer · why the last layer is the editorial one
A useful reframe from Michela Tjan at the Open Visualization Academy: every chart has two layers. The data layer shows what happened. The annotation layer says what it means. One is description; the other is argument — and an argument is the only thing a reader can act on.
The annotation layer does four jobs, and only the first is neutral:
- Orientation — titles, units, axis labels. The basics that let a reader enter the chart. (Tabs 1–2 cover this.)
- Emphasis — the colored line in a field of gray. Your editorial decision about what matters. (Tab 3.)
- Direction — telling the reader where to look first. (Tabs 3 + 4 together — the highlight color plus the end-of-line labels — do this.)
- Explanation — the callout that names the meaning. (Tab 5’s title-as-finding and in-chart annotation.)
The test: read Tab 5’s title first, then look at the data. Does the title tell you what to examine, and does the plotted evidence let you verify it? Both halves matter. A chart whose words carry the claim while the data can’t support it is worse than one with no title at all — and a chart whose data is unreadable without the words is an unfinished argument wearing minimalism as a disguise.
And the discipline: “Every annotation should answer a question the reader was already about to ask. If it doesn’t, it’s clutter in a helpful costume.”
Try it yourself
The fastest way to make these moves stick is to break them. In the Tell a story chunk above:
- Change who gets the highlight. Swap
"United States"for another country incountry == "United States"— it appears twice. Does the chart’s story change? Does the title still hold? - Delete the annotation. Remove the whole annotate() block and re-run. How much work does that one sentence do?
- Put the legend back. Delete
guide = "none"from scale_color_manual() and the geom_text_repel() block. Compare: which version answers “which line is the U.S.?” faster? - Try a descriptive title. Replace the finding-title with
"Gini coefficient by country, 1990–2023". Notice what the reader now has to do for themselves.
Back to the M03 lecture.