Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e373241d51 | ||
|
|
e65688d600 | ||
|
|
7f4dfa2748 | ||
|
|
bdaa05cd54 | ||
|
|
043506cb6b | ||
|
|
0466986195 |
No files matched your search
@@ -789,9 +789,21 @@ test('the brand asset set is complete and served', async ({ page }) => {
|
||||
expect(og.ok()).toBe(true);
|
||||
expect(og.headers()['content-type']).toContain('image/png');
|
||||
|
||||
// iOS ignores SVG touch icons, so this must be a real raster.
|
||||
const apple = await page.request.get('/apple-icon');
|
||||
expect(apple.ok()).toBe(true);
|
||||
/*
|
||||
* iOS ignores SVG touch icons, so this must be a real raster.
|
||||
*
|
||||
* Read the href off the page rather than hardcoding a path. The route moved
|
||||
* when the generated app/apple-icon.tsx became a static app/apple-icon.png:
|
||||
* a generated icon is served at /apple-icon, a static one at /apple-icon.png
|
||||
* with a content hash. This test kept asking for /apple-icon and got a 404 —
|
||||
* reporting a missing icon while the icon was present and correctly linked.
|
||||
*/
|
||||
const appleLink = page.locator('link[rel="apple-touch-icon"]');
|
||||
await expect(appleLink).toHaveCount(1);
|
||||
const appleHref = await appleLink.getAttribute('href');
|
||||
expect(appleHref).toBeTruthy();
|
||||
const apple = await page.request.get(appleHref!);
|
||||
expect(apple.ok(), `apple touch icon at ${appleHref} should be served`).toBe(true);
|
||||
expect(apple.headers()['content-type']).toContain('image/png');
|
||||
|
||||
// Android needs a maskable PNG or the install prompt has no icon.
|
||||
@@ -912,8 +924,22 @@ test('the header carries the schoolcompare lockup', async ({ page }) => {
|
||||
// hashed at build time and change on any unrelated edit.
|
||||
await expect(home).toHaveText(/^\s*schoolcompare\s*$/);
|
||||
|
||||
const mark = home.locator('svg').first();
|
||||
/*
|
||||
* The mark is raster artwork, not an inline SVG — it has been since the
|
||||
* supplied logo replaced the reconstruction, and this locator went on
|
||||
* looking for an <svg> that no longer exists.
|
||||
*
|
||||
* Assert on naturalWidth rather than visibility: a <picture> whose sources
|
||||
* all 404 still lays out and still passes toBeVisible(), so the weaker
|
||||
* assertion would go green on a broken lockup.
|
||||
*/
|
||||
const mark = home.locator('img').first();
|
||||
await expect(mark).toBeVisible();
|
||||
const markState = await mark.evaluate((el: HTMLImageElement) => ({
|
||||
loaded: el.complete && el.naturalWidth > 0,
|
||||
src: el.currentSrc,
|
||||
}));
|
||||
expect(markState.loaded, `header mark failed to load: ${markState.src}`).toBe(true);
|
||||
});
|
||||
|
||||
test('no visible text falls back to the browser default black', async ({ page }) => {
|
||||
|
||||
@@ -161,10 +161,22 @@
|
||||
* page and out-shouts the H1 and the search box. Dimming it in CSS is the
|
||||
* whole treatment, and the scrim then fades it into the dark panel instead of
|
||||
* into cream.
|
||||
*
|
||||
* Raising the brightness raises the background under light dark-theme text, so
|
||||
* this value has a contrast floor, not just a taste range. Measured off
|
||||
* rendered pixels, sampling background up to 120px past each line:
|
||||
*
|
||||
* brightness title body
|
||||
* 0.52 11.01:1 6.44:1
|
||||
* 0.75 9.48:1 5.21:1 ← current
|
||||
*
|
||||
* Body text is the binding one. Going much above 0.75 walks it toward the
|
||||
* 4.5:1 floor, and at that point the scrim needs to carry further right rather
|
||||
* than the artwork being dimmed less.
|
||||
*/
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.heroArt img {
|
||||
filter: brightness(0.52) saturate(0.72) contrast(1.02);
|
||||
filter: brightness(0.75) saturate(0.72) contrast(1.02);
|
||||
}
|
||||
.heroPanel::before {
|
||||
background: linear-gradient(
|
||||
|
||||
@@ -60,6 +60,18 @@ export function HeroIllustration() {
|
||||
srcSet="/brand/hero-band-500.webp 500w, /brand/hero-band-700.webp 700w, /brand/hero-band-972.webp 972w"
|
||||
sizes={SIZES}
|
||||
/>
|
||||
{/*
|
||||
The band's JPEG, and the reason it exists.
|
||||
|
||||
<img src> cannot vary by viewport, so it is always the wide crop. A
|
||||
browser that takes neither AVIF nor WebP would therefore fall through
|
||||
to the desktop frame on a phone and lose the schoolhouse — the exact
|
||||
failure this whole component is arranged to prevent, surviving in the
|
||||
one path nobody looks at. No `type` here, so it matches anywhere the
|
||||
media query does, and it sits after the modern formats so they still
|
||||
win where supported.
|
||||
*/}
|
||||
<source media="(max-width: 860px)" srcSet="/brand/hero-band-700.jpg" />
|
||||
<source
|
||||
type="image/avif"
|
||||
srcSet="/brand/hero-wide-900.avif 900w, /brand/hero-wide-1200.avif 1200w, /brand/hero-wide-1672.avif 1672w"
|
||||
|
||||
Generated
+6
-5
@@ -31,7 +31,8 @@
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/leaflet": "^1.9.21",
|
||||
"jest": "^30.2.0",
|
||||
"jest-environment-jsdom": "^30.2.0"
|
||||
"jest-environment-jsdom": "^30.2.0",
|
||||
"sharp": "^0.34.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@adobe/css-tools": {
|
||||
@@ -937,8 +938,8 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz",
|
||||
"integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -4160,8 +4161,8 @@
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -8584,9 +8585,9 @@
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
|
||||
"integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@img/colour": "^1.0.0",
|
||||
"detect-libc": "^2.1.2",
|
||||
@@ -8629,8 +8630,8 @@
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"devOptional": true,
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/leaflet": "^1.9.21",
|
||||
"jest": "^30.2.0",
|
||||
"jest-environment-jsdom": "^30.2.0"
|
||||
"jest-environment-jsdom": "^30.2.0",
|
||||
"sharp": "^0.34.5"
|
||||
}
|
||||
}
|
||||
@@ -25,8 +25,16 @@
|
||||
* squeezing further toward 4.9:1 crops the empty sides, not the subject.
|
||||
*
|
||||
* AVIF and WebP are both emitted. This is flat-shaded vector-style artwork, so
|
||||
* AVIF lands roughly an order of magnitude under the source PNG; the JPEG is
|
||||
* only the <img> fallback and is never served to a current browser.
|
||||
* AVIF lands roughly an order of magnitude under the source PNG. Both crops
|
||||
* also get a JPEG: the wide one backs the <img src>, and the band one backs a
|
||||
* <source media> — without it a browser taking neither modern format would
|
||||
* fall through to the desktop frame on a phone, since <img src> cannot vary by
|
||||
* viewport.
|
||||
*
|
||||
* sharp is declared in devDependencies rather than leaned on as a transitive
|
||||
* dependency of next. It does currently arrive that way, but relying on that
|
||||
* means a Next upgrade can silently break this script — the same reasoning as
|
||||
* the outputFileTracingIncludes note in next.config.js.
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
|
||||
Reference in new issue
Block a user