Topic 16.1
Dashboard Layout and KPI Tiles
In one line
A dashboard is read in a glance, not studied. Put the few numbers that matter first, give each one a comparison and a trend, lay the rest out on a grid, and stop adding charts when each question already has one.
Think of it like this
The dashboard of a car. Speed is big and in the middle. Fuel and temperature are small. A warning light is silent until something is wrong. You never read it: you glance, and in one second you know whether to keep driving. A web dashboard is named after this panel for the same reason.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- KPI
- Key performance indicator: one number the team watches to know how things are going.
- tile
- One box of a dashboard: a label, a number, a comparison and a small trend.
- sparkline
- A tiny line chart without axes that shows the shape of a series next to a number.
- context
- What a number is compared with: last week, a target, the same day last year.
- hero chart
- The one large chart that answers the dashboard's main question.
- above the fold
- What you see without scrolling. On a wall screen this is everything.
Step by step
01Start with the question, then the numbers
Tomás's question is "How are the bikes doing?" The answer in numbers: rides in the last 7 days, compared with the 7 days before, and a few others. The first example computes them from daily.csv and builds a tile for each.
Each tile shows the label, the number, the change in words and a sparkline. The log prints the same text a screen reader would hear.
02Make the change safe
The second example feeds the change helper every awkward input: a normal rise, a fall, no change, a zero in before, a missing before, a tiny base. None prints Infinity or NaN.
It then draws five sparkline tiles for odd series: one point, all equal, a gap, a long series, and a series that only falls.
03Lay the tiles on a grid
In the first example the tiles sit in a flex row that wraps (flex-wrap: wrap), and the hero chart takes the full width below. The same HTML fills one row on a wall screen and stacks on a phone, with no media query.
The project's .grid uses repeat(auto-fit, minmax(300px, 1fr)) for charts for the same reason. Keep one gap size everywhere.
04Count the charts
Open the plan table from Phase 2 and tick each chart that answers a daily question. Charts with no tick move behind a click or go. The first screen of Lumen's dashboard shows the KPI row, the rides line and the live station box, and nothing else.
Try it yourself
- 1
Compare with another week
In the first example change
rows.slice(-14, -7)torows.slice(-21, -14). The change line then compares with the week before last. Always say what the comparison is incontext. - 2
Flip what is good
In the second example remove
higherIsGood: falsefrom "late returns". The change line becomesgoodalthough more late returns is bad news. Colour follows the flag, so the flag must be right. - 3
Give a tile a zero before
In the second example change the
beforeof "only falls" to0. The line now readsno earlier value to compare with, the arrow turns into a dash and the colour class changes frombadtoflat: the tile refuses to print an infinite percentage.
Code & diagrams
The tile code is the real src/parts/kpi.js of the Lumen project, pasted in because the preview cannot import local files.
Preview
Console
Nothing logged yet.Expected console output
Rides in 2025: 933,651 | no change line
Last 7 days: 9,224 | ▼ down 6.7% vs the 7 days before
Rainy days (30 days): 13 | ▲ up 30.0% vs the 30 days before
Sparklines drawn: 3Nothing prints Infinity or NaN. A rise in late returns is bad, so its change line is marked bad even though the number went up.
Preview
Console
Nothing logged yet.Expected console output
normal rise -> up 14.5% vs last week
fall -> down 10.0% vs last week
no change -> no change vs last week
before is 0 -> no earlier value to compare with
before is missing -> no earlier value to compare with
now is NaN -> no earlier value to compare with
tiny base -> up 150.0% vs last week
tiles with a sparkline: all equal, a gap, only falls, late returns
change colours: bad, badBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Divide without checking
In percentChange, replace the first line (if (!Number.isFinite(now) …) with nothing, so the function always returns ((now - before) / Math.abs(before)) * 100. Then run the second example.
Myth vs fact
Myth
A dashboard should show everything, so nobody has to ask.
Fact
A dashboard that shows everything shows nothing. It answers the few questions people ask daily and links to detail for the rest.
Myth
Green means good and red means bad.
Fact
Up is not always good (late returns), and colour alone is invisible to many readers. Use words and arrows, then let colour follow.
Myth
Sparklines of two tiles can be compared by height.
Fact
Each sparkline has its own scale, so only the shape is comparable. If heights must compare, share one scale and say so.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
A percentage hides the base. "Up 150%" from 2 to 5 is three rides. Show both when the base is small, or hide the percentage below a minimum base that the team agrees on.
- ▸
d3.scaleLinearwith a zero-width domain returns the middle of the range, so the all-equal sparkline is flat and centred instead ofNaN. Check withd3.scaleLinear([5, 5], [0, 100])(5), which is50. - ▸
Tiles built with
textContentcannot inject markup. A station name such as<img onerror=…>from a data file would show as text. UseinnerHTMLfor a tile only if you escape every value. - ▸
Big numbers need a rule too.
933,651fits a tile, but1,234,567,890may not.new Intl.NumberFormat("en-GB", { notation: "compact" }).format(933651)gives934K(and1.2Mfor 1,234,567). Show the short form in the tile and the full number in atitleor in the table twin, so nothing is rounded away for good. - ▸
On a wall display, test from the real distance. Text under about 24 px on a TV is unreadable from across a room, and wall screens often run at 1920 × 1080 with the browser zoomed out.
Remember this
- 1
The problem this solves: the board wants to look at one screen on the wall and know how the bikes are doing. Twenty equal charts give no starting point: every chart shouts, so nothing is heard. Good layout decides what is read first, second and last.
- 2
What goes first. In languages that read left to right, eyes start at the top left. Put the question with the most consequence there: how many rides, and are we up or down? A row of 3 to 6 KPI tiles (KPI means key performance indicator: one number the team watches) comes first. One large chart that answers the main question comes next, then smaller supporting charts, then detail on request.
- 3
A KPI tile is four things. A label ("Last 7 days"), the number (
24,120), a comparison (up 4.2% vs the 7 days before) and a small trend. A number alone is not a fact: is3,451rides good or bad? Say what it is compared with, in words, so nobody guesses. ThekpiTilefunction in this topic builds all four. Before the numbers arrive, the same row shows grey placeholder tiles (skeletonTiles, Topic 16.4), so the page does not jump when they load. - 4
Computing the change safely.
(now − before) ÷ before × 100breaks whenbeforeis0(division by zero givesInfinity), when it is missing (nullcounts as0in arithmetic andundefinedbecomesNaN) and when it is tiny (2 rides to 5 rides is "up 150%", which means nothing). The helper returnsnulland the tile saysno earlier value to compare withinstead of printingInfinity%. For small bases, show the plain difference too. - 5
Colour never works alone. Green for good and red for bad hides the message from the one in twelve men with a colour-vision difference, and from Meera's screen reader. Each change line has an arrow and words (
up,down), and the colour only follows them. Also, "up" is not always good: more late returns is bad, so the tile takeshigherIsGood. - 6
Sparklines. A sparkline is a word-sized chart without axes (the name is Edward Tufte's). It shows the shape of the last days, not the values, so each tile has its own scale and the heights of two sparklines must not be compared. Draw a dot on the last point, break the line at a gap, draw nothing for fewer than two points, and draw a flat line when every value is equal.
- 7
Layout on a grid. CSS Grid with
repeat(auto-fit, minmax(160px, 1fr))lets the tiles fill a wall screen in one row and stack on a phone with no media query. Give the hero chart the full width, and let supporting charts sit two or three across. Keep the same gap everywhere: uneven space reads as grouping that is not there (Topic 2.4). - 8
Too many charts. Each chart must answer a question somebody asked (the plan table from Phase 2). If a chart answers none, remove it. When a box needs more than one chart, use a tab or a click that opens detail, and keep the first screen small. A dashboard that needs a scroll bar on the wall has already failed at its job.
Explain it without notes
Why does a KPI tile show a comparison and not only a number?
Why does the change helper return null instead of a number in some cases?
Why must a change line use words and arrows as well as colour?
How do you decide whether a chart belongs on the first screen?
Practice
Write tileText(label, now, before) that returns "label: now (up X% vs before)", using describeChange. What does it print for ("Rides", 100, 0)?
A tile shows the busiest station. Which comparison would you give it, and what would you show when the busiest station changed since last week?
Trade-offs
- ↔
Fewer tiles are read faster, but a team that needs ten numbers will open another screen. Group the extra numbers behind a click rather than shrinking everything.
- ↔
A sparkline adds context at the cost of space and of one more thing to describe for screen readers. Use it when the trend matters more than the exact value.
Done when you can
I can say what goes first on a dashboard and why.
I can build a KPI tile with a number, a comparison and a trend.
I can make the change helper safe for zero, missing and tiny numbers.
I can make a sparkline for one point, equal values and a gap.
I can lay tiles out on a grid that fits a wall screen and a phone.
I can decide which charts do not belong on the first screen.