133 lines
4.6 KiB
TypeScript
133 lines
4.6 KiB
TypeScript
import { readFile } from 'node:fs/promises';
|
|||
|
|
import { join } from 'node:path';
|
||
|
|
import { ImageResponse } from 'next/og';
|
||
|
|
import { BARS } from '@/components/Logo';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* The site had no og:image at all, so every link pasted into a class WhatsApp
|
||
|
|
* group or a Slack channel rendered as a bare grey text card — on the highest
|
||
|
|
* intent channel this product has.
|
||
|
|
*
|
||
|
|
* The card is the mark at poster scale: the cohort as a wide distribution with
|
||
|
|
* one school lit up. It states the proposition rather than decorating it.
|
||
|
|
*/
|
||
|
|
export const alt = 'SchoolCompare — compare every school in England against the national picture';
|
||
|
|
export const size = { width: 1200, height: 630 };
|
||
|
|
export const contentType = 'image/png';
|
||
|
|
|
||
|
|
const PAPER = '#f2f1ed';
|
||
|
|
const INK = '#16202a';
|
||
|
|
const MUTED = '#5c6570';
|
||
|
|
const BRAND = '#584a9b';
|
||
|
|
|
||
|
|
// A plausible national distribution, drawn once at poster scale. The lit bar
|
||
|
|
// is the same position the mark uses.
|
||
|
|
const SPREAD = [8, 12, 17, 24, 33, 44, 57, 70, 82, 92, 98, 100, 96, 88, 77, 65, 53, 41, 31, 22, 15, 10, 7, 5];
|
||
|
|
// Right of the peak: score runs left to right, so the lit school reads as
|
||
|
|
// above the national average. At index 9 it sat on the low side, which quietly
|
||
|
|
// said the opposite.
|
||
|
|
const LIT = 14;
|
||
|
|
|
||
|
|
async function font(file: string) {
|
||
|
|
return readFile(join(process.cwd(), 'assets', file));
|
||
|
|
}
|
||
|
|
|
||
|
|
export default async function OpengraphImage() {
|
||
|
|
const [regular, bold] = await Promise.all([
|
||
|
|
font('SchibstedGrotesk-Regular.ttf'),
|
||
|
|
font('SchibstedGrotesk-Bold.ttf'),
|
||
|
|
]);
|
||
|
|
|
||
|
|
return new ImageResponse(
|
||
|
|
(
|
||
|
|
<div
|
||
|
|
style={{
|
||
|
|
width: '100%',
|
||
|
|
height: '100%',
|
||
|
|
display: 'flex',
|
||
|
|
flexDirection: 'column',
|
||
|
|
justifyContent: 'space-between',
|
||
|
|
background: PAPER,
|
||
|
|
padding: '58px 72px 52px',
|
||
|
|
fontFamily: 'Schibsted',
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
{/* Lockup */}
|
||
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
|
||
|
|
<div style={{ position: 'relative', width: 40, height: 40, display: 'flex' }}>
|
||
|
|
{BARS.map(([x, y, w, h, isSchool], i) => (
|
||
|
|
<div
|
||
|
|
key={x}
|
||
|
|
style={{
|
||
|
|
position: 'absolute',
|
||
|
|
left: x,
|
||
|
|
top: y,
|
||
|
|
width: w,
|
||
|
|
height: h,
|
||
|
|
borderRadius: 1.5,
|
||
|
|
background: isSchool ? BRAND : INK,
|
||
|
|
opacity: isSchool ? 1 : [0.22, 0.38, 0.55, 1, 0.22][i],
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
<div style={{ display: 'flex', fontSize: 29, fontWeight: 700, letterSpacing: '-0.028em' }}>
|
||
|
|
<span style={{ color: INK }}>School</span>
|
||
|
|
<span style={{ color: BRAND }}>Compare</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Proposition. Sized so the headline holds two lines and the subhead
|
||
|
|
one — an orphaned word reads as an accident at this size. */}
|
||
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
|
||
|
|
<div
|
||
|
|
style={{
|
||
|
|
display: 'flex',
|
||
|
|
fontSize: 66,
|
||
|
|
fontWeight: 700,
|
||
|
|
letterSpacing: '-0.036em',
|
||
|
|
lineHeight: 1.03,
|
||
|
|
color: INK,
|
||
|
|
maxWidth: 1010,
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
A score means nothing until you see the spread.
|
||
|
|
</div>
|
||
|
|
<div style={{ display: 'flex', fontSize: 26, color: MUTED, letterSpacing: '-0.005em' }}>
|
||
|
|
24,000+ schools in England · SATs, GCSEs, Ofsted and admissions
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* The device, at poster scale. Same object as the mark above. */}
|
||
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||
|
|
<div style={{ display: 'flex', alignItems: 'flex-end', gap: 7, height: 120 }}>
|
||
|
|
{SPREAD.map((h, i) => (
|
||
|
|
<div
|
||
|
|
key={i}
|
||
|
|
style={{
|
||
|
|
flex: 1,
|
||
|
|
height: `${h}%`,
|
||
|
|
borderRadius: 2,
|
||
|
|
background: i === LIT ? BRAND : INK,
|
||
|
|
opacity: i === LIT ? 1 : 0.15,
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 18, color: MUTED, letterSpacing: '0.09em' }}>
|
||
|
|
<span>NATIONAL SPREAD</span>
|
||
|
|
<span style={{ letterSpacing: '-0.005em' }}>schoolcompare.co.uk</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
),
|
||
|
|
{
|
||
|
|
...size,
|
||
|
|
fonts: [
|
||
|
|
{ name: 'Schibsted', data: regular, weight: 400, style: 'normal' },
|
||
|
|
{ name: 'Schibsted', data: bold, weight: 700, style: 'normal' },
|
||
|
|
],
|
||
|
|
}
|
||
|
|
);
|
||
|
|
}
|