From dbb74d9b60073104ed8bac41ed8915c57db06379 Mon Sep 17 00:00:00 2001 From: Tudor Date: Fri, 21 Aug 2026 22:15:02 +0100 Subject: [PATCH] fix(places): align the measure column's heading with its values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The heading sat on the right edge of the column and every value on the left. A specificity collision, not a layout problem: the two were aligned by different selectors and only one of them won. .table td (0,1,1) text-align: left <- won for the value .num (0,1,0) text-align: right <- lost .table th:last-child (0,2,1) text-align: right <- won for the heading The heading and the value cell now share one class and one rule, so they cannot drift apart again whatever else changes around them. The column also stretched to half the table. It now hugs its content with width:1% and nowrap, so the school name takes the remaining width — which is what made the gap read as misalignment on a wide screen, and what crowded the name column on a narrow one. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_015mWQnpye9F299NVRCCSRvj --- .../__tests__/components/PlaceView.test.tsx | 31 +++++++++++++++++++ .../components/places/PlaceView.module.css | 18 +++++++++-- nextjs-app/components/places/PlaceView.tsx | 4 ++- 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/nextjs-app/__tests__/components/PlaceView.test.tsx b/nextjs-app/__tests__/components/PlaceView.test.tsx index 931f2db..aeec09e 100644 --- a/nextjs-app/__tests__/components/PlaceView.test.tsx +++ b/nextjs-app/__tests__/components/PlaceView.test.tsx @@ -168,3 +168,34 @@ describe('PlaceView presentation', () => { .not.toBeInTheDocument(); }); }); + +describe('PlaceView table alignment', () => { + const aligned: PlaceDetail = { + place: { kind: 'town', slug: 'brentwood', name: 'Brentwood', count: 2, + parent_authority: 'Essex', phases: ['primary'] }, + schools: [ + { urn: 1, school_name: 'Alpha Primary', phase: 'Primary', + rwm_expected_pct: 82, attainment_8_score: null } as never, + ], + averages: { rwm_expected_pct: 63, attainment_8_score: null }, + }; + + it('aligns the measure heading and its values with the same class', () => { + // They were aligned by two different selectors whose specificity did not + // match: `.table th:last-child` (0,2,1) won and went right, while `.num` + // (0,1,0) lost to `.table td` (0,1,1) and stayed left. Sharing one class + // is what makes them impossible to drift apart. + const { container } = render(); + const th = container.querySelectorAll('th')[1]; + const td = container.querySelectorAll('tbody td')[1]; + expect(th.className).toBeTruthy(); + expect(td.className).toBe(th.className); + }); + + it('leaves the school-name column unclassed so it takes the spare width', () => { + const { container } = render(); + expect(container.querySelectorAll('th')[0].className).toBe(''); + }); +}); diff --git a/nextjs-app/components/places/PlaceView.module.css b/nextjs-app/components/places/PlaceView.module.css index 3f69233..b304b37 100644 --- a/nextjs-app/components/places/PlaceView.module.css +++ b/nextjs-app/components/places/PlaceView.module.css @@ -144,10 +144,24 @@ border-bottom: none; } -.table th:last-child, -.num { +/* + * Header and value share one class and one rule, so they cannot drift apart. + * + * The first cut aligned them with two different selectors: `.table th:last-child` + * at (0,2,1) beat the element rule and went right, while `.num` at (0,1,0) lost + * to `.table td` at (0,1,1) and stayed left. The heading and its numbers sat on + * opposite edges of the column. + * + * width:1% with nowrap makes the measure column hug its content so the school + * name takes the remaining width — without it the two columns split evenly and + * the gap between heading and value reads as misalignment on a wide screen. + */ +.table th.num, +.table td.num { text-align: right; font-variant-numeric: tabular-nums; + width: 1%; + white-space: nowrap; } /* The measure is spelled out; the tooltip carries the definition. */ diff --git a/nextjs-app/components/places/PlaceView.tsx b/nextjs-app/components/places/PlaceView.tsx index 5c4b463..a8efc5d 100644 --- a/nextjs-app/components/places/PlaceView.tsx +++ b/nextjs-app/components/places/PlaceView.tsx @@ -71,7 +71,9 @@ function SchoolTable({ schools, phase }: { schools: School[]; phase: PhaseKey }) School - + {/* Same class as the value cell below: one rule aligns both, so + they cannot drift apart. */} + {metric.heading} -- 2.54.0