fix(home): correct what the landing page claims, and give it one rhythm #91

Merged
tudor merged 1 commits from fix/homepage-truth-and-rhythm into feat/brand-logo-artwork 2026-08-14 16:16:25 +00:00
Owner

Reviewed the live staging homepage at 1440px, 390px and in dark mode. Everything below was measured on the deployed page or verified against the code, not inferred.

Claims that were false

On the page Reality
"24,000+ schools" (×3) API returns unique_schools: 27230
"Up to three schools side by side" MAX_SCHOOLS = 5, and a card 400px below said five
"Class sizes, SEN support…" Class size exists nowhere in the codebase — that line was the only grep hit
Compare preview columns Invented results and Ofsted grades against two real, named schools

The coverage figure was a silent bug, not a typo. DataInfoResponse declared a total_schools field the API has never sent (it sends unique_schools), so the read was undefined on every request and the hardcoded "24,000+" fallback rendered while the fetch reported success. The interface, not the code, was wrong — which is why it type-checked cleanly and no test caught it.

Also: one feature under three names (Compare / shortlist / pin), and <title> was the bare string Home.

Cut

  • The trust line — repeated the coverage figure one paragraph after the hero gave it, behind three decorative dots (the concept's avatar stack, avatars removed).
  • "Start exploring" — three chips linking to two destinations already in the nav.
  • The six-row "Coverage at a glance" table, one row of which was wrong.
  • Three of four countdown cards. They gave the page's largest numeral to dates up to 245 days away, two of them offer days — dates you receive something on, which cannot be missed. All four dates survive as one supporting line.
  • Value-prop titles demoted <h2> → <p>; they were outranking the page's real headings in the document outline.

Why it read "disjointed"

Measured gaps between the seven landing bands: 24 / 32 / 24 / 16 / 48 / 32 / 16px — no scale, each band setting its own margin. Plus four different section-header treatments in one scroll. The page container now owns a single gap and there is one header pattern; an e2e test asserts every band gap is identical.

The illustration

Two separate defects, both of which shipped:

  1. Dark mode. It kept a fixed light palette in both themes, so a pale sky slab was the brightest object on a near-black page, out-shouting the H1 and the search box. Now driven by --ill-* tokens with a dark re-grade — same scene, lit at dusk, with the pin as the only bright thing left.
  2. Mobile crop. The hero slot ranges from 1.34:1 to 4.9:1 across breakpoints. Under slice, at 860×176 a 540×520 scene shows only its bottom 110 user units — so the schoolhouse (at y 218–324) was cropped away entirely on phones, leaving hills and a pin pointing at nothing. No single composition covers that range, so there are now two, each drawn against the crop window its own breakpoint actually produces, with CSS showing one. Both are static server-rendered SVG — no JS, no second request.

Detail also reduced per the brand guideline's "minimal detail, maximum clarity": the gable clock, flagpole pennant and four-window grid are gone.

One thing measuring caught

The deadline bar was originally computed in an effect (to dodge a hydration mismatch), which meant the section had to reserve its own height. One guessed number cannot cover a block whose supporting line wraps differently at every width — measured, the reservation was short at all four breakpoints, shifting the page by up to 108px on a phone. It now renders on the server, with suppressHydrationWarning on the two text nodes that can differ across a midnight boundary.

Tests

Seven new e2e journeys, each guarding a failure mode that is silent by nature:

  • the coverage figure matches /api/data-info and "24,000+" appears nowhere
  • the stated comparison limit matches MAX_SCHOOLS
  • no real school names in the illustrative preview
  • the page title is not "Home"
  • every landing band gap is identical
  • the illustration swaps composition rather than cropping its subject
  • the illustration's sky is substantially darker in the dark theme

Verification

No local server, so this was verified through an offline render harness: the real compiled CSS chunks plus real server-rendered markup, screenshotted in Playwright at 1440 / 1024 / 640 / 390 in both themes. tsc clean, 159/159 unit tests, build green.

Note on the base branch: this targets feat/brand-logo-artwork (PR #90) rather than main, because both touch Footer.tsx. Retarget to main once #90 merges.

🤖 Generated with Claude Code

Reviewed the live staging homepage at 1440px, 390px and in dark mode. Everything below was measured on the deployed page or verified against the code, not inferred. ## Claims that were false | On the page | Reality | |---|---| | "24,000+ schools" (×3) | API returns `unique_schools: 27230` | | "Up to **three** schools side by side" | `MAX_SCHOOLS = 5`, and a card 400px below said five | | "**Class sizes**, SEN support…" | Class size exists nowhere in the codebase — that line was the only grep hit | | Compare preview columns | Invented results and Ofsted grades against **two real, named schools** | The coverage figure was a silent bug, not a typo. `DataInfoResponse` declared a `total_schools` field the API has never sent (it sends `unique_schools`), so the read was `undefined` on every request and the hardcoded "24,000+" fallback rendered while the fetch reported success. The interface, not the code, was wrong — which is why it type-checked cleanly and no test caught it. Also: one feature under three names (Compare / shortlist / pin), and `<title>` was the bare string `Home`. ## Cut - The trust line — repeated the coverage figure one paragraph after the hero gave it, behind three decorative dots (the concept's avatar stack, avatars removed). - "Start exploring" — three chips linking to two destinations already in the nav. - The six-row "Coverage at a glance" table, one row of which was wrong. - Three of four countdown cards. They gave the page's largest numeral to dates up to **245 days** away, two of them offer days — dates you *receive* something on, which cannot be missed. All four dates survive as one supporting line. - Value-prop titles demoted `<h2>` → `<p>`; they were outranking the page's real headings in the document outline. ## Why it read "disjointed" Measured gaps between the seven landing bands: **24 / 32 / 24 / 16 / 48 / 32 / 16px** — no scale, each band setting its own margin. Plus four different section-header treatments in one scroll. The page container now owns a single gap and there is one header pattern; an e2e test asserts every band gap is identical. ## The illustration Two separate defects, both of which shipped: 1. **Dark mode.** It kept a fixed light palette in both themes, so a pale sky slab was the brightest object on a near-black page, out-shouting the H1 and the search box. Now driven by `--ill-*` tokens with a dark re-grade — same scene, lit at dusk, with the pin as the only bright thing left. 2. **Mobile crop.** The hero slot ranges from 1.34:1 to 4.9:1 across breakpoints. Under `slice`, at 860×176 a 540×520 scene shows only its bottom 110 user units — so the schoolhouse (at y 218–324) was cropped away entirely on phones, leaving hills and a pin pointing at nothing. No single composition covers that range, so there are now **two**, each drawn against the crop window its own breakpoint actually produces, with CSS showing one. Both are static server-rendered SVG — no JS, no second request. Detail also reduced per the brand guideline's "minimal detail, maximum clarity": the gable clock, flagpole pennant and four-window grid are gone. ## One thing measuring caught The deadline bar was originally computed in an effect (to dodge a hydration mismatch), which meant the section had to reserve its own height. One guessed number cannot cover a block whose supporting line wraps differently at every width — measured, the reservation was short at **all four** breakpoints, shifting the page by up to 108px on a phone. It now renders on the server, with `suppressHydrationWarning` on the two text nodes that can differ across a midnight boundary. ## Tests Seven new e2e journeys, each guarding a failure mode that is silent by nature: - the coverage figure matches `/api/data-info` and "24,000+" appears nowhere - the stated comparison limit matches `MAX_SCHOOLS` - no real school names in the illustrative preview - the page title is not "Home" - every landing band gap is identical - the illustration swaps composition rather than cropping its subject - the illustration's sky is substantially darker in the dark theme ## Verification No local server, so this was verified through an offline render harness: the real compiled CSS chunks plus real server-rendered markup, screenshotted in Playwright at 1440 / 1024 / 640 / 390 in both themes. `tsc` clean, 159/159 unit tests, build green. **Note on the base branch:** this targets `feat/brand-logo-artwork` (PR #90) rather than `main`, because both touch `Footer.tsx`. Retarget to `main` once #90 merges. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
tudor added 1 commit 2026-08-14 16:15:41 +00:00
The homepage made four statements that were not true, carried elements that
asked nothing of anyone, and had a hero illustration that broke in both the
places it had to work.

Claims, all verified against the code or the API:

  * "24,000+ schools" (three places) against a real 27,230. The fact box meant
    to show the live figure rendered its own fallback on every request, because
    DataInfoResponse declared a `total_schools` field the API has never sent —
    it sends `unique_schools`. The fetch succeeded; only that field was
    undefined, so nothing threw and nothing failed. The interface, not the
    code, was the thing that was wrong.
  * "Up to three schools side by side" against MAX_SCHOOLS = 5, contradicting a
    card 400px below it that correctly said five.
  * "Class sizes" — data the codebase has never held. That copy line was the
    only hit in a full-repo grep.
  * Invented results and Ofsted grades attributed to two real, named schools
    in the compare preview.

Also one feature, three words: Compare (nav), shortlist (footer), pin (cards).
Settled on Compare everywhere. And <title> was the bare string "Home".

Cut: the trust line (repeated the coverage figure one paragraph after the hero
gave it, behind three decorative dots), the "Start exploring" row (three links
to two destinations already in the nav), the six-row coverage table, and three
of the four countdown cards — which gave the page's largest numeral to dates up
to 245 days away, two of them offer days, which cannot be missed. All four
dates remain, at proportionate weight. Value-prop titles drop from <h2> to <p>;
they were outranking the page's real headings in the document outline.

Rhythm: the gaps between the seven landing bands were 24/32/24/16/48/32/16px,
each band setting its own margin, with four different section-header
treatments between them. The page container now owns one gap, and there is one
header pattern. An e2e test asserts the gaps are identical.

Illustration: it kept a fixed light palette in both themes, which left a pale
sky slab as the brightest object on a near-black page, out-shouting the H1 and
the search box. It now reads from --ill-* tokens with a dark re-grade. And the
hero slot ranges from 1.34:1 to 4.9:1 across breakpoints, which no single
composition survives under `slice` — at 860x176 a 540x520 scene shows only its
bottom 110 units, so the schoolhouse was cropped away entirely on phones,
leaving hills and a pin pointing at nothing. There are now two compositions,
each drawn against the crop window its own breakpoint produces, with CSS
showing one. Both are static server-rendered SVG.

The deadline bar renders on the server rather than on hydrate. The effect-based
version needed a reserved height, and one guessed number cannot cover a block
whose supporting line wraps differently at every width — measured, it was short
at all four, shifting the page up to 108px on a phone.

Verified on the built output through an offline render harness (no local
server): real compiled CSS, real rendered markup, four widths, both themes.
tsc clean, 159/159 unit tests, build green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tudor merged commit 8636c96d35 into feat/brand-logo-artwork 2026-08-14 16:16:25 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: tudor/school_compare#91