The server/client split moved the header and sticky-nav markup into SchoolDetailShell, but their responsive rules stayed behind in schoolSections.module.css. CSS Modules hash class names per file, so those rules stopped matching entirely.
Symptom: the staging E2E journey header details collapse behind a "Show all details" toggle on mobile failed — #school-header-details was visible at 390px because .headerDetails { display: none } no longer applied. The toggle button was invisible too (base rule is display: none; the media query flipping it to inline-flex was stranded in the same block).
Wider blast radius: three shell classes had no rule at all in the shell stylesheet — headerDetailsOpen, atEnd, sectionNavBackLabel — so the mobile section-nav swap (menu button in, link row out), the scroll-fade mask and the bottom-sheet panel were dead as well.
Changes
SchoolDetailShell.module.css: ports the stranded blocks verbatim from the pre-refactor stylesheet — the 768px header block (stacked header, wrapped meta pills, details toggle + collapse) and the 640px section-nav blocks.
__tests__/components/schoolStyles.test.ts: guards the class of bug — every styles.X a components/school component references must be defined in the stylesheet that component imports. Verified it fails on the pre-fix tree (3 missing classes) and passes after.
Verification
jest 159/159 pass
tsc --noEmit clean
next build succeeds
The mobile toggle itself is confirmed by CSS-rule restoration + the guard test, not by a local Playwright run (E2E targets staging).
Known, left alone (pre-existing since the refactor)
schoolSections.module.css still carries the header/nav rules as dead code.
.secondaryScope .header { padding: 1rem } at ≤768px is stranded by the same split — the shell's <header> sits outside .secondaryScope, so secondary detail headers keep desktop padding on mobile. The guard test can't catch cross-file descendant selectors.
The server/client split moved the header and sticky-nav markup into `SchoolDetailShell`, but their responsive rules stayed behind in `schoolSections.module.css`. CSS Modules hash class names per file, so those rules stopped matching entirely.
**Symptom:** the staging E2E journey `header details collapse behind a "Show all details" toggle on mobile` failed — `#school-header-details` was visible at 390px because `.headerDetails { display: none }` no longer applied. The toggle button was invisible too (base rule is `display: none`; the media query flipping it to `inline-flex` was stranded in the same block).
**Wider blast radius:** three shell classes had no rule at all in the shell stylesheet — `headerDetailsOpen`, `atEnd`, `sectionNavBackLabel` — so the mobile section-nav swap (menu button in, link row out), the scroll-fade mask and the bottom-sheet panel were dead as well.
## Changes
- `SchoolDetailShell.module.css`: ports the stranded blocks verbatim from the pre-refactor stylesheet — the 768px header block (stacked header, wrapped meta pills, details toggle + collapse) and the 640px section-nav blocks.
- `__tests__/components/schoolStyles.test.ts`: guards the class of bug — every `styles.X` a `components/school` component references must be defined in the stylesheet that component imports. Verified it fails on the pre-fix tree (3 missing classes) and passes after.
## Verification
- `jest` 159/159 pass
- `tsc --noEmit` clean
- `next build` succeeds
- The mobile toggle itself is confirmed by CSS-rule restoration + the guard test, not by a local Playwright run (E2E targets staging).
## Known, left alone (pre-existing since the refactor)
- `schoolSections.module.css` still carries the header/nav rules as dead code.
- `.secondaryScope .header { padding: 1rem }` at ≤768px is stranded by the same split — the shell's `<header>` sits outside `.secondaryScope`, so secondary detail headers keep desktop padding on mobile. The guard test can't catch cross-file descendant selectors.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The server/client split moved the header and sticky nav markup into
SchoolDetailShell, but their responsive rules stayed behind in
schoolSections.module.css. CSS Modules hash class names per file, so those
rules stopped matching entirely: on mobile the header details block never
collapsed ("Show all details" e2e journey failed), the toggle itself never
appeared, and the section-nav mobile swap (menu button in, link row out)
never applied.
Ports the stranded blocks into SchoolDetailShell.module.css verbatim: the
768px header block (stacked header, wrapped meta pills, details toggle +
collapse) and the 640px section-nav blocks (scroll-fade mask, bottom-sheet
panel, control swap). Adds a jest guard that every class a components/school
component references is defined in the stylesheet that component imports —
it fails on the three classes that had no rule at all here
(headerDetailsOpen, atEnd, sectionNavBackLabel).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tudor
merged commit c298a29b61 into main2026-08-06 09:33:34 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The server/client split moved the header and sticky-nav markup into
SchoolDetailShell, but their responsive rules stayed behind inschoolSections.module.css. CSS Modules hash class names per file, so those rules stopped matching entirely.Symptom: the staging E2E journey
header details collapse behind a "Show all details" toggle on mobilefailed —#school-header-detailswas visible at 390px because.headerDetails { display: none }no longer applied. The toggle button was invisible too (base rule isdisplay: none; the media query flipping it toinline-flexwas stranded in the same block).Wider blast radius: three shell classes had no rule at all in the shell stylesheet —
headerDetailsOpen,atEnd,sectionNavBackLabel— so the mobile section-nav swap (menu button in, link row out), the scroll-fade mask and the bottom-sheet panel were dead as well.Changes
SchoolDetailShell.module.css: ports the stranded blocks verbatim from the pre-refactor stylesheet — the 768px header block (stacked header, wrapped meta pills, details toggle + collapse) and the 640px section-nav blocks.__tests__/components/schoolStyles.test.ts: guards the class of bug — everystyles.Xacomponents/schoolcomponent references must be defined in the stylesheet that component imports. Verified it fails on the pre-fix tree (3 missing classes) and passes after.Verification
jest159/159 passtsc --noEmitcleannext buildsucceedsKnown, left alone (pre-existing since the refactor)
schoolSections.module.cssstill carries the header/nav rules as dead code..secondaryScope .header { padding: 1rem }at ≤768px is stranded by the same split — the shell's<header>sits outside.secondaryScope, so secondary detail headers keep desktop padding on mobile. The guard test can't catch cross-file descendant selectors.🤖 Generated with Claude Code
The server/client split moved the header and sticky nav markup into SchoolDetailShell, but their responsive rules stayed behind in schoolSections.module.css. CSS Modules hash class names per file, so those rules stopped matching entirely: on mobile the header details block never collapsed ("Show all details" e2e journey failed), the toggle itself never appeared, and the section-nav mobile swap (menu button in, link row out) never applied. Ports the stranded blocks into SchoolDetailShell.module.css verbatim: the 768px header block (stacked header, wrapped meta pills, details toggle + collapse) and the 640px section-nav blocks (scroll-fade mask, bottom-sheet panel, control swap). Adds a jest guard that every class a components/school component references is defined in the stylesheet that component imports — it fails on the three classes that had no rule at all here (headerDetailsOpen, atEnd, sectionNavBackLabel). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>