feat(design): adopt the Cohort identity — palette, type, mark and dark theme #86
@@ -47,6 +47,12 @@ COPY --from=builder /app/public ./public
|
||||
COPY --from=builder /app/.next/standalone ./
|
||||
COPY --from=builder /app/.next/static ./.next/static
|
||||
|
||||
# Fonts for the generated share card. File tracing already places these in
|
||||
# .next/standalone/assets, so the COPY above carries them — this makes the
|
||||
# dependency explicit rather than implicit in the tracer's behaviour, because
|
||||
# a miss here is a silent 500 on /opengraph-image, not a build failure.
|
||||
COPY --from=builder /app/assets ./assets
|
||||
|
||||
# Set correct permissions
|
||||
RUN chown -R nextjs:nodejs /app
|
||||
|
||||
|
||||
@@ -6,10 +6,14 @@
|
||||
* five-bar spread that appears inside every school row.
|
||||
*
|
||||
* Colour has exactly three jobs and they never borrow each other's hues:
|
||||
* brand (iris) interactive + identity. Never means "good".
|
||||
* status (teal / amber) above / below the comparison point. Never a CTA.
|
||||
* brand (iris) interactive + identity. Never carries valence.
|
||||
* status (teal / amber) valence — teal is above/good, amber is
|
||||
* below/needs-attention. Never a CTA.
|
||||
* phase (desaturated) category chips, deliberately subordinate.
|
||||
*
|
||||
* Escalate within status by weight, not by reaching for a new hue: a tinted
|
||||
* amber chip for "requires improvement", a solid amber one for "inadequate".
|
||||
*
|
||||
* Teal/amber rather than green/red so the above/below signal survives every
|
||||
* form of colour blindness — the audience includes parents reading closely.
|
||||
*/
|
||||
|
||||
@@ -181,8 +181,8 @@
|
||||
.chipOffer::before { background: var(--status-above); }
|
||||
|
||||
.chipUrgent {
|
||||
border-color: rgba(var(--brand-rgb), 0.4);
|
||||
background: rgba(var(--brand-rgb), 0.04);
|
||||
border-color: rgba(var(--status-below-rgb), 0.4);
|
||||
background: rgba(var(--status-below-rgb), 0.04);
|
||||
}
|
||||
|
||||
.chipTrack {
|
||||
@@ -218,7 +218,7 @@
|
||||
|
||||
.chipDeadline .chipDays,
|
||||
.chipUrgent .chipDays {
|
||||
color: var(--brand-strong);
|
||||
color: var(--status-below);
|
||||
}
|
||||
|
||||
.chipOffer .chipDays {
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
}
|
||||
|
||||
.bad {
|
||||
background: var(--brand-bg);
|
||||
color: var(--brand-strong);
|
||||
background: var(--status-below-bg);
|
||||
color: var(--status-below);
|
||||
}
|
||||
|
||||
.neutral {
|
||||
|
||||
@@ -1308,8 +1308,8 @@
|
||||
}
|
||||
|
||||
.countdownChipUrgent {
|
||||
border-color: rgba(var(--brand-rgb), 0.4);
|
||||
background: rgba(var(--brand-rgb), 0.04);
|
||||
border-color: rgba(var(--status-below-rgb), 0.4);
|
||||
background: rgba(var(--status-below-rgb), 0.04);
|
||||
}
|
||||
|
||||
.chipTrack {
|
||||
@@ -1350,7 +1350,7 @@
|
||||
|
||||
.countdownChipDeadline .chipDays,
|
||||
.countdownChipUrgent .chipDays {
|
||||
color: var(--brand-strong);
|
||||
color: var(--status-below);
|
||||
}
|
||||
|
||||
.countdownChipOffer .chipDays {
|
||||
|
||||
@@ -604,7 +604,7 @@ function CompactSchoolItem({ school, onAddToCompare, isInCompare, nationalAvgRwm
|
||||
...(ofstedBadge.cssClass === 'ofsted1' ? { background: 'var(--status-above-bg)', color: 'var(--status-above)' } :
|
||||
ofstedBadge.cssClass === 'ofsted2' ? { background: 'var(--status-above-bg)', color: 'var(--status-above)' } :
|
||||
ofstedBadge.cssClass === 'ofsted3' ? { background: 'var(--status-below-bg)', color: 'var(--status-below)' } :
|
||||
ofstedBadge.cssClass === 'ofsted4' ? { background: 'var(--brand-bg)', color: 'var(--brand)' } :
|
||||
ofstedBadge.cssClass === 'ofsted4' ? { background: 'var(--status-below)', color: 'var(--text-inverse)' } :
|
||||
ofstedBadge.cssClass === 'ofstedRc' ? { background: 'var(--phase-secondary-text)', color: 'var(--text-inverse)' } :
|
||||
ofstedBadge.cssClass === 'ofstedPending' ? { background: 'var(--bg-secondary)', color: 'var(--text-muted)' } :
|
||||
{ background: 'var(--bg-secondary)', color: 'var(--text-muted)' }),
|
||||
|
||||
@@ -58,7 +58,7 @@ function buildPopupBadge(school: School): PopupBadge {
|
||||
1: 'background:var(--status-above-bg);color:var(--status-above)',
|
||||
2: 'background:var(--status-above-bg);color:var(--status-above)',
|
||||
3: 'background:var(--status-below-bg);color:var(--status-below)',
|
||||
4: 'background:var(--brand-bg);color:var(--brand)',
|
||||
4: 'background:var(--status-below);color:var(--text-inverse)',
|
||||
};
|
||||
return { label: `${labels[school.ofsted_grade]}${yearStr}`, style: colours[school.ofsted_grade] };
|
||||
}
|
||||
|
||||
@@ -132,8 +132,8 @@
|
||||
}
|
||||
|
||||
.trendDown {
|
||||
color: var(--brand);
|
||||
background: var(--brand-bg);
|
||||
color: var(--status-below);
|
||||
background: var(--status-below-bg);
|
||||
}
|
||||
|
||||
.trendStable {
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
}
|
||||
|
||||
.trendUp { color: var(--status-above); }
|
||||
.trendDown { color: var(--brand); }
|
||||
.trendDown { color: var(--status-below); }
|
||||
.trendStable { color: var(--text-muted); }
|
||||
|
||||
/* Line 4: location */
|
||||
@@ -204,7 +204,7 @@
|
||||
.ofsted1 { background: var(--status-above-bg); color: var(--status-above); }
|
||||
.ofsted2 { background: rgba(var(--status-above-rgb), 0.12); color: var(--status-above); }
|
||||
.ofsted3 { background: var(--status-below-bg); color: var(--status-below); }
|
||||
.ofsted4 { background: var(--brand-bg); color: var(--brand); }
|
||||
.ofsted4 { background: var(--status-below); color: var(--text-inverse); }
|
||||
|
||||
/* ── Ofsted badge variants ──────────────────────────────────────────────── */
|
||||
/* ofsted1–4 already defined above; these cover the two new framework states */
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
}
|
||||
|
||||
.deltaPositive { color: var(--status-above); }
|
||||
.deltaNegative { color: var(--brand); }
|
||||
.deltaNegative { color: var(--status-below); }
|
||||
|
||||
/* Line 4: location + distance */
|
||||
.line4 {
|
||||
@@ -207,7 +207,7 @@
|
||||
.ofsted1 { background: var(--status-above-bg); color: var(--status-above); }
|
||||
.ofsted2 { background: rgba(var(--status-above-rgb), 0.12); color: var(--status-above); }
|
||||
.ofsted3 { background: var(--status-below-bg); color: var(--status-below); }
|
||||
.ofsted4 { background: var(--brand-bg); color: var(--brand); }
|
||||
.ofsted4 { background: var(--status-below); color: var(--text-inverse); }
|
||||
|
||||
/* ── Ofsted badge variants ──────────────────────────────────────────────── */
|
||||
.ofstedRc { background: var(--phase-secondary-text); color: var(--text-inverse); }
|
||||
|
||||
@@ -123,8 +123,8 @@
|
||||
}
|
||||
|
||||
.chipBad {
|
||||
background: var(--brand-bg);
|
||||
color: var(--brand-strong);
|
||||
background: var(--status-below-bg);
|
||||
color: var(--status-below);
|
||||
}
|
||||
|
||||
.chipNeutral {
|
||||
@@ -151,8 +151,8 @@
|
||||
}
|
||||
|
||||
.badgeBad {
|
||||
background: var(--brand-bg);
|
||||
color: var(--brand-strong);
|
||||
background: var(--status-below-bg);
|
||||
color: var(--status-below);
|
||||
}
|
||||
|
||||
.rcList {
|
||||
|
||||
@@ -860,7 +860,7 @@
|
||||
}
|
||||
|
||||
.progressNegative {
|
||||
color: var(--brand-strong);
|
||||
color: var(--status-below);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@@ -876,8 +876,8 @@
|
||||
}
|
||||
|
||||
.statusBad {
|
||||
background: var(--brand-bg);
|
||||
color: var(--brand-strong);
|
||||
background: var(--status-below-bg);
|
||||
color: var(--status-below);
|
||||
}
|
||||
|
||||
/* Charts Section */
|
||||
@@ -1025,8 +1025,8 @@
|
||||
color: var(--status-below);
|
||||
}
|
||||
.ofstedGrade4 {
|
||||
background: var(--brand-bg);
|
||||
color: var(--brand-strong);
|
||||
background: var(--status-below);
|
||||
color: var(--text-inverse);
|
||||
}
|
||||
|
||||
/* Report Card grade colours (5-level scale, lower = better) */
|
||||
@@ -1047,8 +1047,8 @@
|
||||
color: var(--status-below);
|
||||
} /* Needs attention */
|
||||
.rcGrade5 {
|
||||
background: var(--brand-bg);
|
||||
color: var(--brand-strong);
|
||||
background: var(--status-below);
|
||||
color: var(--text-inverse);
|
||||
} /* Urgent improvement */
|
||||
|
||||
/* Safeguarding value (used inside a standard metricCard) */
|
||||
|
||||
@@ -3,6 +3,15 @@ const nextConfig = {
|
||||
// Enable standalone output for Docker
|
||||
output: 'standalone',
|
||||
|
||||
// app/opengraph-image.tsx reads the Schibsted Grotesk files off disk at
|
||||
// request time (Satori needs a font buffer; it has no system fallback).
|
||||
// File tracing currently picks these up on its own, but that relies on the
|
||||
// tracer resolving a runtime join() — declare them so a Next upgrade can't
|
||||
// silently drop them and turn every link preview into a 500.
|
||||
outputFileTracingIncludes: {
|
||||
'/opengraph-image': ['./assets/**'],
|
||||
},
|
||||
|
||||
// The /api/* and /sitemap.xml proxies to the FastAPI backend are route
|
||||
// handlers (app/api/[...path]/route.ts, app/sitemap.xml/route.ts) rather
|
||||
// than rewrites, so the backend host is read from FASTAPI_URL at runtime
|
||||
@@ -72,7 +81,9 @@ const nextConfig = {
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '/favicon.svg',
|
||||
// favicon.svg was replaced by app/icon.svg (the mark now has one
|
||||
// source in components/Logo.tsx); this rule was caching a 404.
|
||||
source: '/icon.svg',
|
||||
headers: [
|
||||
{
|
||||
key: 'Cache-Control',
|
||||
|
||||
Reference in New Issue
Block a user