Files
school_compare/nextjs-app
TudorandClaude Opus 5 97ac5c9cef
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m3s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 11s
PR Checks / Build Frontend (no push) (pull_request) Successful in 49s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 10s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 5m29s
fix(a11y): clear the four AA failures blocking the staging E2E gate
Main's staging E2E has been red since #90. Two of the four failures were the
staging container serving a stale image at the moment the gate ran — apple-icon
and the header lockup both check out against staging now. The other two were
real, and both are the same mistake: a colour pairing verified against one
ground while the element sits on another.

  * .heroEyebrow  --brand on its own 10% tint over Sand is 4.19:1. The token
    clears AA on Sand, but the tint darkens the ground under the label, and
    that composite was never the thing measured. --brand-strong is 5.85:1.

  * .hiwVisual    The preview panel was grounded in Sand while everything
    inside it is a translucent status tint — and those tints are specced to
    clear AA over --bg-card. Over Sand the report-card chips landed at 4.22:1
    and 4.29:1. The ratio depended on a background two levels up. Moving the
    panel to the card surface puts them at 5.69:1 and 5.81:1; a border keeps
    it reading as an inset frame now that panel and card share a colour.

  * Footer .sectionTitle  --sage flips with the theme; the footer band does
    not (it is teal in both). So the pairing only held in one of them — the
    dark sage measured 4.08:1, on every page. Now --on-sunken-muted, which is
    what the --on-sunken-* family exists for, as Footer.tsx's own header
    comment already says.

  * .compareHeadLabel  --text-muted on the header row's tint is 4.45:1 in the
    dark theme. Under by a hair, same cause. Now --text-secondary.

The harness gained the footer, because it had no footer and so could not have
caught the one failure that appeared on all three pages. Rewriting fragments
now uses each CSS module's own hash — the footer rewritten with HomeView's
prefix renders unstyled, which would have made any contrast measured on it
meaningless while still reporting a number.

Verified: 0 AA failures in both themes across the assembled page, measured with
the same probe the e2e test uses, against the real compiled CSS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 18:08:38 +01:00
..

SchoolCompare Next.js Application

Modern Next.js application for comparing primary school KS2 performance across England.

Features

  • Server-Side Rendering (SSR): Fast initial page loads with pre-rendered content
  • Individual School Pages: Dedicated pages for each school with full SEO optimization
  • Side-by-Side Comparison: Compare up to 5 schools simultaneously
  • School Rankings: Top-performing schools by various metrics
  • Interactive Maps: Leaflet integration for geographic visualization
  • Performance Charts: Chart.js visualizations for historical data
  • Responsive Design: Mobile-first approach with full responsive support
  • SEO Optimized: Dynamic sitemaps, meta tags, and structured data

Tech Stack

  • Framework: Next.js 16 (App Router)
  • Language: TypeScript 5
  • Styling: CSS Modules + CSS Variables
  • State Management: React Context API + URL state
  • Data Fetching: SWR (client-side) + Next.js fetch (server-side)
  • Charts: Chart.js + react-chartjs-2
  • Maps: Leaflet + react-leaflet
  • Testing: Jest + React Testing Library
  • Validation: Zod

Getting Started

Prerequisites

  • Node.js 24+ (using nvm recommended)
  • FastAPI backend running on port 8000

Installation

# Install dependencies
npm install

# Copy environment variables
cp .env.example .env.local

# Update .env.local with your configuration

Development

# Start development server
npm run dev

# Open http://localhost:3000

Building

# Build for production
npm run build

# Start production server
npm start

Testing

# Run tests
npm test

# Run tests in watch mode
npm run test:watch

# Run tests with coverage
npm run test:coverage

Linting

# Run ESLint
npm run lint

Project Structure

nextjs-app/
├── app/                      # App Router pages
│   ├── layout.tsx           # Root layout
│   ├── page.tsx             # Home page
│   ├── compare/             # Compare page
│   ├── rankings/            # Rankings page
│   ├── school/[urn]/        # Individual school pages
│   ├── sitemap.ts           # Dynamic sitemap
│   └── robots.ts            # Robots.txt
├── components/              # React components
│   ├── SchoolCard.tsx       # School card component
│   ├── FilterBar.tsx        # Search/filter controls
│   ├── ComparisonView.tsx   # Comparison interface
│   ├── RankingsView.tsx     # Rankings table
│   └── ...
├── lib/                     # Utility libraries
│   ├── api.ts              # API client
│   ├── types.ts            # TypeScript types
│   └── utils.ts            # Helper functions
├── hooks/                   # Custom React hooks
├── context/                 # React Context providers
├── styles/                  # Global styles
├── public/                  # Static assets
└── __tests__/              # Test files

Environment Variables

Variable Description Default
NEXT_PUBLIC_API_URL Public API endpoint (client-side) http://localhost:8000/api
FASTAPI_URL Server-side API endpoint http://localhost:8000/api
NODE_ENV Environment mode development

Performance Optimizations

  • Server-Side Rendering: Initial HTML rendered on server
  • Static Generation: Where possible, pages are pre-generated
  • Image Optimization: Next.js Image component with AVIF/WebP support
  • Code Splitting: Automatic route-based code splitting
  • Dynamic Imports: Heavy components loaded on demand
  • API Caching: Configurable revalidation for data fetching
  • Bundle Optimization: Tree shaking and minification
  • Compression: Gzip compression enabled

SEO Features

  • Dynamic Meta Tags: Generated per page with Next.js Metadata API
  • Open Graph: Social media optimization
  • JSON-LD: Structured data for search engines
  • Sitemap: Auto-generated from database
  • Robots.txt: Search engine crawling rules
  • Canonical URLs: Duplicate content prevention

Browser Support

  • Chrome (latest)
  • Firefox (latest)
  • Safari (latest)
  • Edge (latest)

License

Proprietary - SchoolCompare

Support

For issues and questions, please contact the development team.