PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m6s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 12s
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) Failing after 3m13s
Implements the direction agreed from the identity board: Route C ("Cohort")
with the paper ground from C1, the Schibsted Grotesk / Literata pairing from
C2, and the iris accent from C3. Dark theme is in scope from the start rather
than retrofitted.
The audit found three things wrong beyond taste:
* No brand asset set. og:image was absent entirely, so every link shared into
a class WhatsApp group rendered as a bare grey card. apple-touch-icon pointed
at an SVG, which iOS ignores, and the manifest shipped no PNGs, so Android
installs had no icon. The header mark and the favicon had also drifted into
two different logos.
* No colour discipline. --primary and --trend-down were the same coral, so the
main CTA and "below average" shared a hue. 58 distinct hex values were spread
across component CSS, and the chart palette was still Chart.js's stock demo
colours.
* A dark theme that was declared but never built — themeColor announced a dark
variant with no dark styling behind it.
What changed:
Colour now has exactly three jobs that never borrow each other's hues: brand
(iris) for interactive and identity, status (teal/amber) for above/below a
comparison point, and phase for categories. Teal/amber rather than green/red
keeps the above/below signal readable for every form of colour blindness.
Every chromatic literal in component CSS is now a token, and the JS-painted
surfaces (Chart.js, Leaflet) read the tokens through lib/theme so they follow
the theme instead of ignoring it.
The mark is the five-bar cohort spread — the same object as the distribution
strip inside a school row, built from opacity steps so it inverts cleanly.
components/Logo.tsx is the single source; the favicon, apple-icon and share
card all derive from its geometry.
globals.css drops 123 dead global classes left over from the vanilla-JS app
(only the btn family, .skip-link and .main were still referenced), along with
the noise overlay. It also gains prefers-reduced-motion support, which was
missing entirely, and a type scale so the 54 ad-hoc font sizes have somewhere
to converge.
Verified: tsc clean, 159 unit tests pass, production build succeeds and
prerenders /icon.svg, /apple-icon and /opengraph-image. Three e2e journeys
added for the asset set, the themeColor/background match, and the dark theme
actually repainting — all silent failures that nothing on the page reveals.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.