Skip to content

Visual essay style

5 min read · Updated 2 Nov 2026

A visual essay is a story the reader takes in by scrolling. Full-bleed sections alternate between two backgrounds, a playful display face sets the headings and a serif body face carries the text. Charts stay in view while short steps of text scroll past and change what the chart shows. It is modelled on the traits of interactive data essays such as those published by The Pudding. It does not use that publication’s name, logo or fonts.

Pair it with the long-form layout: its sticky chart with steps variant owns the step markup, CSS and script. This page covers colour, type, chrome and chart settings.

Visual essay style: A narrative composition with generous transitions.

Illustrative example with synthetic data. A narrative composition with generous transitions.

Use it when the page is read once, in order, by a broad audience, and the story needs a few charts that build on each other.

The page is mainly Use instead
Several chart forms to browse, in no fixed order Information is Beautiful
Charts for a business audience, one takeaway each Storytelling with data
Dense, sourced analysis in standard chart forms Economist or Financial Times
Indicators read beside prose, without a narrative Explainer
A dashboard that looks like the tenant’s Workbooks Astrato default
Read again, compared section by section, or printed Any other style on the long-form sections variant
Use case Example Steps
Cultural data story What a city streamed this year, by genre All genres; then weekends only; then the top five
Public product-launch results Sign-ups and activation by week since launch Sign-ups alone; then with activation; then one region
Yearly review A community’s year in five findings One section per finding; steps inside a section narrow it to the months it covers

Define light and dark in one block; see Define colour tokens. --band is the second section background; --highlight is the colour a step brings forward and --context the colour of every other measure. Spacing (--gap, --header-h, --chart-min) comes from the long-form layout.

:root {
--page: #fdf8f0;
--band: #f1e6d4;
--line: #c9b89c;
--ink: #1f1a14;
--muted: #5e5548;
--highlight: #bf1d53;
--context: #8a8174;
--font-display: 'Bricolage Grotesque', system-ui, sans-serif;
--font-body: 'Source Serif 4', Georgia, serif;
--bleed-pad: clamp(48px, 10vh, 120px);
--measure: 38rem;
--radius: 12px;
}
:root[data-theme='dark'] {
--page: #16131b;
--band: #27212f;
--line: #4a4257;
--ink: #f5efe6;
--muted: #b8afa3;
--highlight: #ff5c8a;
--context: #7b7386;
}

Bricolage Grotesque and Source Serif 4 are openly licensed and served by Google Fonts; see Load visualization fonts.

Role Font Size and weight
Essay title --font-display clamp(44px, 8vw, 112px), 800
Section title --font-display clamp(28px, 4vw, 44px), 700
Byline and chart note --font-display 15px, 500, --muted
Body and steps --font-body 19px, 400, line height 1.6

For charts that colour by category, use this palette; the first colour is --highlight.

export const series = {
light: ['#bf1d53', '#1f6f8b', '#a65400', '#5b3fa8'],
dark: ['#ff5c8a', '#4cc3e0', '#ffa347', '#b89cff'],
};

Sections run the full width of the window; their text keeps a readable measure. Step cards are the only cards: a thin border, a radius and the section’s background, with no shadow.

body {
margin: 0;
background: var(--page);
color: var(--ink);
font: 400 19px / 1.6 var(--font-body);
}
.bleed {
background: var(--section-bg, var(--page));
padding: var(--bleed-pad) max(var(--gap), calc((100% - 72rem) / 2));
}
.bleed--tint {
--section-bg: var(--band);
}
.bleed > .prose {
max-width: var(--measure);
margin-inline: auto;
}
.display {
margin: 0;
font: 800 clamp(44px, 8vw, 112px) / 0.95 var(--font-display);
letter-spacing: -0.02em;
}
.section-title {
margin: 0 0 12px;
font: 700 clamp(28px, 4vw, 44px) / 1.05 var(--font-display);
}
.byline,
.chart-note {
color: var(--muted);
font-family: var(--font-display);
font-weight: 500;
font-size: 15px;
}
.mark {
color: var(--highlight);
font-weight: 700;
}
/* The long-form step card, restyled. Load after the long-form CSS. */
.step__card {
max-width: 24rem;
padding: 20px 24px;
background: var(--section-bg, var(--page));
border: 1px solid var(--line);
border-radius: var(--radius);
}
.step[data-active] .step__card {
border-color: var(--highlight);
}
  • Alternate .bleed and .bleed.bleed--tint down the page. The tint sets --section-bg once, and the section and its step cards read it.
  • Round chart hosts on the host with overflow: hidden; see Rounded corners. Keep overflow visible on the sticky chart’s ancestors, as the long-form variant explains.

Use sticky chart with steps. Each step applies a selection, operations or both, and carries the chart’s complete state. Good step changes in this style:

  • Narrow the data with a selection: one period, one region, one group.
  • Bring a measure forward: setMeasureGraphColor with --highlight on that measure and --context on the others.
  • Show values with the type’s data-label operation once the reader should read them.

Write each step so it states its finding in words, so a reader who does not see the chart change still gets the story. Transitions the page adds sit inside @media (prefers-reduced-motion: no-preference); do not add scroll-behavior: smooth or scroll snapping outside it.

Charts start in the built-in light theme; colour each with its own operations. On every chart that has them, set axis and legend text to --muted and axis lines to --line with setAxisLabelColor, setAxisTitleColor, setAxisLineColor, setAxisTickColor and setLegendItemLabelFill.

Type Use it for Operations to set
bar Categories setColoringColor --context for one measure, or setMeasureGraphColor per measure; setBarChartBarRadius; setMeasureDataLabelsShow; setShowAxisTicks false
line Change over time setMeasureGraphColor --highlight on the key measure, --context on the others; setMeasureLineSize; setLineType; setMeasureDataLabelsShow with setMeasureDataLabelDensity
scatter Items on two measures setColoringType categorical, setCustomPaletteColors with the series palette; setShowDataLabels; setDataLabelDensity; setDataLabelTextColor
dot Before-and-after or two-group comparisons setColoringType categorical, setCustomPaletteColors with the series palette; setOrientation
pie A share of one whole, two or three parts setColoringType categorical, setCustomPaletteColors; setPieChartType donut; setBorderColor with the section’s background (--page or --band); setPieLabelColor; setPieMeasureLabelFormat percentage
number A headline figure between sections setNumberFontFamily Bricolage Grotesque; setNumberFontWeight; setNumberFontColor --highlight
  • Title each section with its finding, in the display face.
  • Name series in the text. Colour the words that name the highlighted measure with .mark, and turn the legend off when the text names every coloured series.
  • Annotate in the steps. Each step is one or two sentences pointing at one part of the chart.
  • Label values directly with data labels, instead of relying on tooltips.
  • Put the source and method in a .chart-note after the section, or in one closing section.

WCAG ratios from Measure contrast, on both section backgrounds. Text needs 4.5:1, series and --context 3:1; the surfaces are checked against the 1.15 and 1.4 guides.

Pair Light, --page Light, --band Dark, --page Dark, --band
--page against --band 1.17 — 1.18 —
--line against the background 1.84 1.57 1.93 1.64
--ink 16.33 13.99 16.07 13.66
--muted 6.92 5.93 8.48 7.21
--highlight (text and series 1) 5.65 4.84 6.25 5.32
--context 3.63 3.11 4.06 3.45
Series 2 5.36 4.59 8.91 7.58
Series 3 5.11 4.37 9.26 7.87
Series 4 7.30 6.25 8.08 6.87

See App limitations: no colour for a named category value, so a step cannot highlight one bar or one category; no notes or reference lines on the plot; chart text stays in Roboto; no animated transition between chart types.

Use chart animation controls to apply reduced motion and line-end labels on vertical, unstacked line charts.

Kind Page Why
Layout Long-form: sticky chart with steps The step pattern this style dresses
Layout Long-form: reveal on scroll Charts mount just before they are reached
Expert Kirk Framing a story for its audience and choosing what to annotate