Command Palette

Search for a command to run...

Hectal
PHASE 15Advanced ~43 min· topic 2 of 7

Topic 15.2

Short and Long Descriptions

In one line

A chart needs a short name that says the finding and a longer description that gives the shape, the extremes and the exceptions. Write both from the data, so they cannot disagree with the picture.

Think of it like this

A radio sports presenter describing a race to listeners. "Blue pulls ahead on the last bend" is the name: one sentence, the finding. Then a few more sentences give what a viewer would see: who started, who led, the gap at the end. A good presenter never reads every split time, and never says only "a race".

Words you'll meet

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

short description
One or two sentences that say what the chart is and, ideally, its finding. It becomes the accessible name.
long description
A paragraph with the shape, the extremes and the exceptions. It is joined to the chart as the accessible description.
aria-describedby
An attribute that points at another element by id, whose text becomes this element's accessible description.
figure and figcaption
HTML elements for a self-contained picture and its caption. The browser knows the caption belongs to the picture.
generated description
A description a function writes from the chart's own data, so it always matches.
stale text
Words that were true for an earlier version of the data and now say something else.

Step by step

01Start from a chart with no words

The panel on the right shows one week of Lumen rides as seven hand-drawn bars. It has no role and no name. The Console says so. Nothing is wrong with the picture; it just has no words yet.

02Add the role and a short name

One change: role="img" and an aria-label that says what the chart is. A screen reader can now announce the chart, and the name says "Rides per day, 2 to 8 June 2025". It does not say which day was quiet.

03Add a visible long description

A paragraph under the chart says the finding in words: Monday was the quietest (the rainy day), Tuesday the busiest. aria-describedby joins it to the chart. Everyone can read the paragraph, not only a screen reader user.

04Group them in a figure

Wrapping the chart and its paragraph in <figure> with a <figcaption> tells the browser they belong together. The Console confirms that the chart sits inside a figure. Use this when the text is truly a caption for the chart.

05Add the table twin

The same seven numbers as a real table, inside a <details> that opens on demand. It is the longest description there is: every value. Topic 15.3 builds it properly.

Try it yourself

  1. 1

    Add a row with NaN

    In the first example, add a case before "a gap": "NaN row": [{ date: day("2025-06-02"), rides: 2979 }, { date: day("2025-06-03"), rides: NaN }],. The Console prints NaN row: Line chart of rides per day. One day with a number: 2 Jun 2025, 2,979 rides (1 day has no number and shows as a gap). The word NaN never reaches a person, and the gap is counted.

  2. 2

    Bring one day back after empty

    In the second example, add after the last show: june.push({ date: day("2025-06-02"), rides: 2979 }); show("4. one day back");. The Console prints describedby: desc-1 and description paragraphs on the page: 1, with the text One day with a number: 2 Jun 2025, 2,979 rides. One paragraph carries every state.

Code & diagrams

Descriptions written from the data, edge cases included New tab

describeRides is the function the dashboard uses (src/parts/describe.js). The first four cases are made-up rows. The last line reads the real daily.csv. Dates are UTC midnight, like d3.autoType makes them (Topics 4.2 and 4.3).

Console

(nothing was printed)

Expected console output

empty: Line chart of rides per day: no data. There are no rides to show.
one day: Line chart of rides per day. One day with a number: 2 Jun 2025, 2,979 rides.
all equal: Line chart of rides per day. 2 days, from 2 Jun 2025 to 3 Jun 2025. Every day had 100 rides.
a gap: Line chart of rides per day. 3 days, from 2 Jun 2025 to 4 Jun 2025 (1 day has no number and shows as a gap). It starts at 2,979 rides, peaks at 3,759 on 4 Jun 2025 and ends at 3,759. The quietest day is 2 Jun 2025, with 2,979 rides.
full year: Line chart of rides per day. 365 days, from 1 Jan 2025 to 31 Dec 2025. It starts at 1,193 rides, peaks at 4,380 on 18 Jul 2025 and ends at 910. The quietest day is 14 Aug 2025, with 439 rides (app down).
labelChart keeps the description in step New tab

labelChart reuses the paragraph that follows the chart, so the id stays desc-1 for every update. Three states: first draw, new data, no data.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

1. first draw
  aria-label: Line chart of rides per day
  describedby: desc-1 | description paragraphs on the page: 1
  text: 3 days, from 2 Jun 2025 to 4 Jun 2025. It starts at 2,979 rides, peaks at 3,992 on 3 Jun 2025 and ends at 3,759. The quietest day is 2 Jun 2025, with 2,979 rides.
2. after the data changed
  aria-label: Line chart of rides per day
  describedby: desc-1 | description paragraphs on the page: 1
  text: 3 days, from 2 Jun 2025 to 4 Jun 2025. It starts at 2,979 rides, peaks at 4,100 on 4 Jun 2025 and ends at 4,100. The quietest day is 2 Jun 2025, with 2,979 rides.
3. after the data was emptied
  aria-label: Line chart of rides per day: no data
  describedby: desc-1 | description paragraphs on the page: 1
  text: There are no rides to show.

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

Add a new paragraph at every update

In the second example, change if (!note || !note.classList.contains("desc")) { to if (true) {, so a new paragraph is made on every call.

terminal
$ Console, third state
── what you'll see ──
3. after the data was emptied
aria-label: Line chart of rides per day: no data
describedby: desc-3 | description paragraphs on the page: 3
text: There are no rides to show.

Myth vs fact

Myth

A long description should list every value.

Fact

That is the table's job (Topic 15.3). The description gives the shape and the places to look.

Myth

Describe the colours and the line style.

Fact

Colour rarely matters to the answer. Say what the data does. Mention colour only when the chart's legend uses it to tell groups apart and the words do not.

Myth

Write the description once, by hand, when the chart is made.

Fact

The data changes and the words do not. Generate the words from the same rows the chart draws.

Myth

Hidden descriptions are fine, because only screen readers need them.

Fact

People with low vision, with a cognitive difference or in a hurry read a visible description too, and a visible one gets checked by the team.

Pro corner

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

  • ▸

    Chart libraries often write a description for you, but the words say what the library knows (series names, point counts), not what matters here. Treat library text as a starting point and test it with real data.

  • ▸

    A description in the page's language needs the right lang on the page (and on a quoted phrase in another language). Dates and numbers come from one formatter (formatDay, formatNumber), so the text uses the same style as the chart's labels.

  • ▸

    A description over about 150 words is rarely read. When the description grows, move detail into the table and keep the paragraph to range, peak, low, end and the exception.

Remember this

  1. 1

    The problem this solves: one short name cannot hold a year of data. A person who cannot see the chart still wants the shape, the highest and lowest points, and anything odd. Without that, the reader has the table (Topic 15.3) and nothing to tell them where to look.

  2. 2

    Two layers: a short name and a long description. The W3C's guidance on complex images gives the same advice: a short description that says what the image is, and a long description that gives the detail. For a chart: the name says what it shows (and ideally the finding), the description gives the range, the peak, the low, the end and what is unusual.

  3. 3

    Write the description from the data. Hand-written text goes stale: the data changes, the picture updates, and the old words stay. A function that takes the same rows the chart is drawn from and returns the sentences cannot disagree with the picture. This is describeRides in the build (Topic 15.7).

  4. 4

    Cover the cases, not only the usual one. Empty data says "no data". One point says one day, not "a line from X to X". All values equal says so instead of "peaks at 100 and quietest 100". Rows with no number are counted and named as gaps. The first example runs all of these through one function.

  5. 5

    **Join it with aria-describedby, and show it.** aria-describedby points at an element by id: the browser takes that element's text as the accessible description. A visible paragraph under the chart serves everyone (people who are low-vision, people who are new to the chart, people who want a quote for a report). A hidden description helps only one group, and nobody can check that it is right. Wrap the chart and its text in <figure> and <figcaption> when the text is a caption (WCAG 1.3.1 Info and Relationships).

  6. 6

    Keep it in step when the data updates. Charts update: a new month, a new range, a refresh. Change the same paragraph in place (the same id), and call the description function again every time the chart redraws. The second example shows the id staying the same and the text following the data. A page that adds a new paragraph on every update leaves stale paragraphs behind (see Break it).

Explain it without notes

01

Why write a short description and a long description?

02

Why generate the description from the data?

03

What does aria-describedby do?

04

Which edge cases should a description function handle?

Practice

01

Write describeBars(rows) for rows { name, rides } that returns one sentence naming the top station and its rides, and "No stations" for an empty list.

02

Change the second example so the description paragraph is a <figcaption> inside a <figure>. What changes in the accessible tree and in the code?

Trade-offs

  • ↔

    A generated description is always true and reads a little stiff. A hand-written one reads better and goes stale. Generate the facts and let a person add one sentence of context where it matters.

  • ↔

    Putting the description on screen costs space. It is worth it: it is the sentence most readers read first.

Done when you can

  • I can write a short name that says the finding and a long description with the shape and extremes.

  • I can join a visible paragraph to a chart with aria-describedby.

  • I generate descriptions from the same rows the chart draws.

  • My description function handles empty, one-point, equal and gap cases.

  • I update the same paragraph in place and never add a second one.