Topic 0.7
Build: The Dashboard's First Page
In one line
Turn your index.html into the first page of the Lumen Bikes dashboard: a header, three boxes, and last week's rides as seven bars you size by hand. It answers Tomás's first question: how did last week go?
Before you start
You will learn
- Lay out a dashboard page with a header and boxes
- Size seven bars by hand from real numbers with one rule
- Describe a chart in its accessible label
You write code in
In your own lumen-dashboard/index.html (replace its contents), and in the editor below
Files you touch
- index.html
Think of it like this
Before a shop opens, someone puts up the shelves and labels them, even if some shelves are still empty. Customers can already see what will go where. Today you put up the dashboard's shelves and fill the first one.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- layout
- How the parts of a page are arranged: here, a header and a grid of boxes.
- grid (CSS)
- A CSS layout (
display: grid) that puts boxes into rows and columns, wrapping them onto new rows when the screen is narrow. - baseline
- The line the bars stand on, here at y = 180. Every bar's bottom touches it.
- aria-label
- An attribute that gives an element a text name for screen readers. With role="img" it describes the whole picture.
- hard-coded
- Typed directly into the code by hand, instead of computed from data.
Step by step
01Start from your Topic 0.4 file
Your lumen-dashboard/index.html from Topic 0.4 has a heading and one bar. Replace its whole contents with the file shown in the Files section below (or the live example), save, and open it.
02Build the page's frame
The <style> in the <head> gives the page a light background, and turns <main> into a grid: repeat(auto-fit, minmax(300px, 1fr)) means "as many columns of at least 300 pixels as fit". On a wide screen the three boxes sit side by side; on a phone they stack.
Each box is a <section class="box"> with an id that never changes: #this-week, #rides-line, #stations-bar. Later phases draw into these ids.
03Size each bar by hand
Work out each bar with the rule, rounded to one decimal place. Then the top is 180 − height.
day rides height = rides ÷ 3992 × 160 y = 180 − height x
Mon 2979 119.4 60.6 30
Tue 3992 160 20 80
Wed 3759 150.7 29.3 130
Thu 3742 150 30 180
Fri 3648 146.2 33.8 230
Sat 3013 120.8 59.2 280
Sun 3451 138.3 41.7 33004Label it for eyes and for screen readers
Under the bars, seven <text> labels (Mon to Sun) are centred under each bar with text-anchor="middle" at x = bar's x + 20.
The <svg> has role="img" and an aria-label that lists all seven numbers and says Monday was quietest. Someone who can't see the bars hears the same answer.
05Check it
The small script at the end counts the bars and finds the tallest. The Console should say Bars: 7 and Tallest bar: 160 (Tuesday). On your computer, check the browser's Console too: no red lines.
Then make the browser window narrow (or use the phone view in the developer tools, for example 390 pixels wide). The boxes stack, and the week box gets a horizontal scrollbar, because its SVG is a fixed 380 pixels wide and the box is narrower than that. That is honest for now: Phase 1 fixes it with viewBox.
Try it yourself
- 1
Make the bars shorter
Redo two rows of the table in step 3 with 120 instead of 160. Tuesday becomes height 120, y 60; Monday becomes height 89.5, y 90.5. Every bar shrinks by a quarter and the comparison stays the same: the rule changed, not the data.
- 2
Highlight a different day
In the first example, give Tuesday's bar (the second
<rect>)fill="#264653"and Monday'sfill="#2a9d8f". The eye now goes to the busiest day. Which one you highlight is part of the message: update thearia-labelto match. - 3
Watch the boxes stack
Drag your browser window narrower. Below about 950 pixels the grid drops to two columns, and below about 650 to one. Whenever the week box becomes narrower than its 380-pixel SVG (the two-column layout, and phone widths), the box shows a horizontal scrollbar. Phase 1 makes the SVG shrink instead.
Code & diagrams
This is the whole lumen-dashboard/index.html after Phase 0, exactly as in the Files section. Make the preview narrow to see the boxes stack.
Preview
Console
Nothing logged yet.Expected console output
Bars: 7
Tallest bar: 160 (Tuesday)Your project files
Your lumen-dashboard project after this topic. Click a file to see it in full.
The whole file after this topic. If yours differs, you can copy this one and carry on.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Lumen Bikes dashboard</title>
<style>
body { margin: 0; padding: 16px; font-family: system-ui, sans-serif; background: #f6f7f9; color: #1d2b36; }
header h1 { margin: 0; font-size: 22px; }
header p { margin: 4px 0 16px; color: #5b6b78; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 12px; }
.box { background: white; border: 1px solid #dde3e8; border-radius: 8px; padding: 12px; overflow-x: auto; }
.box h2 { margin: 0 0 8px; font-size: 15px; }
.soon { color: #5b6b78; font-size: 14px; }
</style>
</head>
<body>
<header>
<h1>Lumen Bikes</h1>
<p>Ardenvale bike share, operations dashboard</p>
</header>
<main class="grid">
<section class="box" id="this-week">
<h2>Rides this week (2–8 June 2025)</h2>
<!-- 7 bars drawn by hand: height = rides ÷ 3,992 × 160 (the busiest day is 160 tall) -->
<svg width="380" height="210" role="img"
aria-label="Rides per day from Monday 2 to Sunday 8 June 2025: Monday 2,979, Tuesday 3,992, Wednesday 3,759, Thursday 3,742, Friday 3,648, Saturday 3,013, Sunday 3,451. Monday, a rainy day, was the quietest.">
<line x1="20" y1="180" x2="380" y2="180" stroke="#9aa8b4"></line>
<rect x="30" y="60.6" width="40" height="119.4" fill="#e76f51"></rect>
<rect x="80" y="20" width="40" height="160" fill="#2a9d8f"></rect>
<rect x="130" y="29.3" width="40" height="150.7" fill="#2a9d8f"></rect>
<rect x="180" y="30" width="40" height="150" fill="#2a9d8f"></rect>
<rect x="230" y="33.8" width="40" height="146.2" fill="#2a9d8f"></rect>
<rect x="280" y="59.2" width="40" height="120.8" fill="#2a9d8f"></rect>
<rect x="330" y="41.7" width="40" height="138.3" fill="#2a9d8f"></rect>
<g font-size="12" text-anchor="middle" fill="#1d2b36">
<text x="50" y="198">Mon</text><text x="100" y="198">Tue</text><text x="150" y="198">Wed</text>
<text x="200" y="198">Thu</text><text x="250" y="198">Fri</text><text x="300" y="198">Sat</text>
<text x="350" y="198">Sun</text>
</g>
</svg>
</section>
<section class="box" id="rides-line">
<h2>Rides through the year</h2>
<p class="soon">Coming in Phase 3: a line of all 365 days.</p>
</section>
<section class="box" id="stations-bar">
<h2>Busiest stations</h2>
<p class="soon">Coming in Phase 5: bars that update with the data.</p>
</section>
</main>
<script>
// A quick self-check: count the bars and find the tallest one
const bars = document.querySelectorAll("#this-week rect");
const heights = Array.from(bars, (b) => Number(b.getAttribute("height")));
console.log("Bars:", bars.length);
console.log("Tallest bar:", Math.max(...heights), "(Tuesday)");
</script>
</body>
</html>
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
Forget to subtract from the baseline
In the first example, change Saturday's bar to y="120.8" (its height) instead of y="59.2" (180 − its height).
Myth vs fact
Myth
A first version with typed-in numbers is a waste: you'll throw it away.
Fact
It fixes the page layout, the box ids, the look of the bars and the accessible label. Phase 3 replaces only the typed numbers with code; everything else stays.
Myth
The aria-label is optional decoration.
Fact
Without it, a screen reader announces the chart as an unnamed image: Meera hears nothing useful. One sentence with the key numbers makes the chart usable for her (Phase 15 goes further).
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Rounding heights to one decimal and then computing y = 180 − height keeps every bar's bottom exactly on the baseline. Rounding y and height independently can leave bars ending at 179.95 or 180.05, which shows as a hairline gap or overlap at some zoom levels.
- ▸
overflow-x: autoon the box is a deliberate stopgap: it keeps the page from scrolling sideways on phones (the box scrolls instead). It hides the real problem (a fixed-size SVG), which is why the build says so and Phase 1 replaces it with aviewBox.
Remember this
- 1
The problem this solves: Tomás couldn't see anything in 365 rows. The dashboard starts with the smallest useful answer: one week, seven bars, one look. It also fixes the page's layout now, so every later chart has a box waiting for it.
- 2
What the page has now. A header ("Lumen Bikes" and a one-line description), and three boxes in a grid. The first box,
#this-week, shows rides from Monday 2 to Sunday 8 June 2025 as seven vertical bars. The other two,#rides-lineand#stations-bar, say which phase fills them. - 3
The rule for the bars. The busiest day (Tuesday, 3,992 rides) gets a bar 160 pixels tall. Every other day gets
rides ÷ 3,992 × 160. Monday's 2,979 rides become 119.4 pixels. Because SVG's y grows downwards and bars stand on a baseline at y = 180, each bar's top is aty = 180 − height. Seven bars of width 40 stand 50 pixels apart, starting at x = 30. - 4
Monday stands out. Monday is drawn in orange: it is the quietest day, and it rained (4.8 mm). The colour points the eye at the answer before you read a label, and the label (
aria-label) says the same thing in words for anyone using a screen reader. - 5
What the dashboard can do now that it couldn't before: it exists, it opens on any computer, and it answers "how did last week go?" at a glance. Still missing: the bars are typed by hand, so new data means retyping them; there are no axes or numbers on the chart; and on a narrow phone the week box scrolls sideways because the SVG has a fixed width. Phase 1 makes it scale to any screen, Phase 3 draws charts from data with code, and Phase 6 adds real axes.
Explain it without notes
How was each bar's height worked out, and why is Tuesday exactly 160?
Why is a bar's y equal to 180 minus its height?
What can the dashboard do now, and what is still missing?
Practice
Add each day's number above its bar (for example "2,979" above Monday), centred, 6 pixels above the bar's top.
Add a fourth box with id status that says "Live station status: Phase 16". Where in the HTML does it go so it appears last?
Trade-offs
- ↔
Typing the bars by hand is quick for seven values and teaches the arithmetic, but every data change means retyping. Code (Phase 3) costs a little more at first and pays back on the first update.
Done when you can
My lumen-dashboard/index.html shows the header, three boxes and seven bars.
I can work out any bar's height and y from its number with the rule.
The browser's Console shows Bars: 7 and no red lines.
I can say what the dashboard can do now and which phases add what's missing.