feat(landing): OG-картинка и метаданные
opengraph-image и twitter-image 1200×630 через next/og: знак, заголовок, штамп «ЧТ · обжарка», лента недели с ЧТ/ПТ красным, без фото и дат. Статичные TTF Golos Text и Martian Mono для Satori. Метаданные из site.meta: title, description, openGraph, twitter, metadataBase kvarta.unuchak.com, canonical. Иконка — SVG-знак из семи штрихов.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" fill="#f3ebdd"/><g fill="#2a1810"><rect x="4" y="6" width="2.4" height="20"/><rect x="7.8" y="6" width="2.4" height="20"/><rect x="11.6" y="6" width="2.4" height="20"/><rect x="19.2" y="6" width="2.4" height="20"/><rect x="23" y="6" width="2.4" height="20"/><rect x="26.8" y="6" width="2.4" height="20"/></g><rect x="15.4" y="6" width="2.4" height="20" fill="#b3221b"/></svg>
|
||||
|
After Width: | Height: | Size: 465 B |
@@ -11,8 +11,23 @@ const golos = Golos_Text({ subsets: ['cyrillic', 'latin'], variable: '--font-gol
|
||||
const martian = Martian_Mono({ subsets: ['cyrillic', 'latin'], variable: '--font-martian', axes: ['wdth'], display: 'swap' });
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL('https://kvarta.unuchak.com'),
|
||||
title: site.meta.title,
|
||||
description: site.meta.description,
|
||||
alternates: { canonical: '/' },
|
||||
openGraph: {
|
||||
type: 'website',
|
||||
locale: 'ru_RU',
|
||||
url: '/',
|
||||
siteName: site.footer.name,
|
||||
title: site.meta.ogTitle,
|
||||
description: site.meta.ogDescription,
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: site.meta.ogTitle,
|
||||
description: site.meta.ogDescription,
|
||||
},
|
||||
};
|
||||
|
||||
export const viewport: Viewport = {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,101 @@
|
||||
// OG 1200×630 по DESIGN.md: знак и слово, заголовок, штамп, лента недели ПН–ВС с ЧТ/ПТ красным.
|
||||
// Без фото и без дат: картинка кешируется. Satori не умеет SVG-фильтры и variable-шрифты —
|
||||
// штамп без «краски», шрифты — статичные TTF из ./og-fonts.
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import { ImageResponse } from 'next/og';
|
||||
import { CONTACT_PHONE } from '@/content/form';
|
||||
import { site } from '@/content/site';
|
||||
|
||||
export const alt = site.meta.ogTitle;
|
||||
export const size = { width: 1200, height: 630 };
|
||||
export const contentType = 'image/png';
|
||||
|
||||
const C = {
|
||||
cream: '#f3ebdd',
|
||||
ink: '#2a1810',
|
||||
muted: '#5e4636',
|
||||
rule: '#c9b595',
|
||||
cherry: '#b3221b',
|
||||
};
|
||||
|
||||
const DAYS = ['ПН', 'ВТ', 'СР', 'ЧТ', 'ПТ', 'СБ', 'ВС'];
|
||||
|
||||
export default async function OpengraphImage() {
|
||||
const dir = join(process.cwd(), 'src/app/og-fonts');
|
||||
const font = (file: string) => readFile(join(dir, file));
|
||||
const [golos900, golos500, martian700] = await Promise.all([font('golos-900.ttf'), font('golos-500.ttf'), font('martian-700.ttf')]);
|
||||
const [roast, delivery, cup] = site.freshness.days;
|
||||
|
||||
return new ImageResponse(
|
||||
(
|
||||
<div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', background: C.cream, color: C.ink, padding: 64, fontFamily: 'Golos Text' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 18 }}>
|
||||
<div style={{ display: 'flex', gap: 7 }}>
|
||||
{DAYS.map((d, i) => (
|
||||
<div key={d} style={{ width: 7, height: 44, background: i === 3 ? C.cherry : C.ink }} />
|
||||
))}
|
||||
</div>
|
||||
<div style={{ fontSize: 56, fontWeight: 900, letterSpacing: -1 }}>{site.footer.name}</div>
|
||||
</div>
|
||||
<div style={{ marginTop: 32, fontSize: 76, fontWeight: 900, lineHeight: 0.95, letterSpacing: -2.5, maxWidth: 760 }}>
|
||||
{site.hero.title}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
marginTop: 24,
|
||||
padding: 6,
|
||||
border: `5px solid ${C.cherry}`,
|
||||
transform: 'rotate(-6deg)',
|
||||
opacity: 0.92,
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', border: `2px solid ${C.cherry}`, padding: '18px 30px', color: C.cherry, fontFamily: 'Martian Mono' }}>
|
||||
<div style={{ fontSize: 30, letterSpacing: 2 }}>{site.hero.stamp.toUpperCase()}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', marginTop: 'auto' }}>
|
||||
<div style={{ display: 'flex', borderBottom: `2px solid ${C.ink}` }}>
|
||||
{DAYS.map((d, i) => {
|
||||
const hot = i === 3 || i === 4;
|
||||
return (
|
||||
<div key={d} style={{ display: 'flex', flex: 1, flexDirection: 'column', paddingBottom: 14, paddingLeft: i === 0 ? 0 : 16, borderLeft: i === 0 ? 'none' : `1px solid ${C.rule}` }}>
|
||||
<div style={{ fontSize: 30, fontWeight: 900, color: hot ? C.cherry : C.ink }}>{d}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div style={{ display: 'flex', height: 76, fontFamily: 'Martian Mono', fontSize: 18, color: C.cherry, letterSpacing: 1 }}>
|
||||
<div style={{ display: 'flex', flex: 3 }} />
|
||||
<div style={{ display: 'flex', flex: 1, paddingLeft: 16, paddingTop: 14 }}>{roast?.label.toUpperCase()}</div>
|
||||
<div style={{ display: 'flex', flex: 3, flexDirection: 'column', paddingLeft: 16, paddingTop: 14 }}>
|
||||
<div style={{ display: 'flex' }}>{delivery?.label.toUpperCase()}</div>
|
||||
<div style={{ display: 'flex', marginTop: 10, height: 10, border: `2px solid ${C.cherry}`, borderTop: 'none' }} />
|
||||
<div style={{ display: 'flex', marginTop: 8 }}>
|
||||
{cup ? `${cup.day} · ${cup.label}`.toUpperCase() : ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 22, fontFamily: 'Martian Mono', fontSize: 20, color: C.muted }}>
|
||||
<div>kvarta.unuchak.com</div>
|
||||
<div>{CONTACT_PHONE.display}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
...size,
|
||||
fonts: [
|
||||
{ name: 'Golos Text', data: golos900, weight: 900, style: 'normal' },
|
||||
{ name: 'Golos Text', data: golos500, weight: 500, style: 'normal' },
|
||||
{ name: 'Martian Mono', data: martian700, weight: 700, style: 'normal' },
|
||||
],
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default, alt, size, contentType } from './opengraph-image';
|
||||
Reference in New Issue
Block a user