Шрифты Golos Text и Martian Mono через next/font, токены @theme, все блоки по плану: первый экран со штампом даты ближайшей обжарки, лента недели, этикетки сортов, подписка, кто мы, доставка, форма, подвал. Три фото Unsplash локально, priority только у первого экрана. LeadForm перекрашена под токены, логика и data-атрибуты те же. В site.ts и form.ts добавлены служебные подписи для вёрстки.
557 lines
49 KiB
Markdown
557 lines
49 KiB
Markdown
---
|
||
name: Кварта
|
||
description: Лендинг минской обжарки — «недельный календарь и штамп даты»
|
||
colors:
|
||
cream: "#F3EBDD"
|
||
paper: "#FBF7F0"
|
||
kraft: "#DCC7A1"
|
||
roast: "#1E120C"
|
||
ink: "#2A1810"
|
||
muted: "#5E4636"
|
||
placeholder: "#7A6352"
|
||
rule-soft: "#C9B595"
|
||
cherry: "#B3221B"
|
||
cherry-deep: "#8F1A14"
|
||
cherry-on-dark: "#E8604F"
|
||
muted-on-dark: "#BFA88A"
|
||
ok: "#2E5E3A"
|
||
typography:
|
||
display:
|
||
fontFamily: "Golos Text, system-ui, sans-serif"
|
||
fontSize: "clamp(2.75rem, 1.6rem + 5.2vw, 6rem)"
|
||
fontWeight: 900
|
||
lineHeight: 0.92
|
||
letterSpacing: "-0.035em"
|
||
headline:
|
||
fontFamily: "Golos Text, system-ui, sans-serif"
|
||
fontSize: "clamp(2rem, 1.4rem + 2.8vw, 3.5rem)"
|
||
fontWeight: 800
|
||
lineHeight: 1
|
||
letterSpacing: "-0.03em"
|
||
title:
|
||
fontFamily: "Golos Text, system-ui, sans-serif"
|
||
fontSize: "clamp(1.5rem, 1.3rem + 0.6vw, 1.875rem)"
|
||
fontWeight: 800
|
||
lineHeight: 1.05
|
||
letterSpacing: "-0.02em"
|
||
lead:
|
||
fontFamily: "Golos Text, system-ui, sans-serif"
|
||
fontSize: "clamp(1.125rem, 1rem + 0.5vw, 1.375rem)"
|
||
fontWeight: 400
|
||
lineHeight: 1.45
|
||
body:
|
||
fontFamily: "Golos Text, system-ui, sans-serif"
|
||
fontSize: "1.0625rem"
|
||
fontWeight: 400
|
||
lineHeight: 1.55
|
||
small:
|
||
fontFamily: "Golos Text, system-ui, sans-serif"
|
||
fontSize: "0.875rem"
|
||
fontWeight: 500
|
||
lineHeight: 1.45
|
||
mono-tag:
|
||
fontFamily: "Martian Mono, ui-monospace, monospace"
|
||
fontSize: "0.75rem"
|
||
fontWeight: 500
|
||
lineHeight: 1.3
|
||
letterSpacing: "0.08em"
|
||
mono-price:
|
||
fontFamily: "Martian Mono, ui-monospace, monospace"
|
||
fontSize: "clamp(1.375rem, 1.2rem + 0.8vw, 2rem)"
|
||
fontWeight: 600
|
||
lineHeight: 1
|
||
letterSpacing: "-0.02em"
|
||
fontFeature: "\"tnum\" 1"
|
||
mono-date:
|
||
fontFamily: "Martian Mono, ui-monospace, monospace"
|
||
fontSize: "clamp(1.75rem, 1.2rem + 2.2vw, 3.25rem)"
|
||
fontWeight: 700
|
||
lineHeight: 1
|
||
letterSpacing: "-0.04em"
|
||
fontVariation: "\"wdth\" 87.5"
|
||
stamp:
|
||
fontFamily: "Martian Mono, ui-monospace, monospace"
|
||
fontSize: "1rem"
|
||
fontWeight: 700
|
||
lineHeight: 1
|
||
letterSpacing: "0.04em"
|
||
fontVariation: "\"wdth\" 112.5"
|
||
rounded:
|
||
none: "0px"
|
||
hair: "2px"
|
||
spacing:
|
||
"1": "4px"
|
||
"2": "8px"
|
||
"3": "12px"
|
||
"4": "16px"
|
||
"5": "20px"
|
||
"6": "24px"
|
||
"8": "32px"
|
||
"12": "48px"
|
||
"18": "72px"
|
||
"32": "128px"
|
||
components:
|
||
button-primary:
|
||
backgroundColor: "{colors.cherry}"
|
||
textColor: "{colors.paper}"
|
||
rounded: "{rounded.none}"
|
||
padding: "0 24px"
|
||
height: "52px"
|
||
button-primary-hover:
|
||
backgroundColor: "{colors.cherry-deep}"
|
||
textColor: "{colors.paper}"
|
||
button-secondary:
|
||
backgroundColor: "transparent"
|
||
textColor: "{colors.ink}"
|
||
rounded: "{rounded.none}"
|
||
padding: "0 20px"
|
||
height: "52px"
|
||
button-secondary-hover:
|
||
backgroundColor: "{colors.ink}"
|
||
textColor: "{colors.cream}"
|
||
input:
|
||
backgroundColor: "{colors.paper}"
|
||
textColor: "{colors.ink}"
|
||
rounded: "{rounded.none}"
|
||
padding: "0 14px"
|
||
height: "52px"
|
||
label-card:
|
||
backgroundColor: "{colors.kraft}"
|
||
textColor: "{colors.ink}"
|
||
rounded: "{rounded.none}"
|
||
padding: "20px"
|
||
ribbon:
|
||
backgroundColor: "{colors.cherry}"
|
||
textColor: "{colors.paper}"
|
||
typography: "{typography.mono-tag}"
|
||
---
|
||
|
||
# Design System: Кварта
|
||
|
||
## Overview
|
||
|
||
**Creative North Star: «Четверг на этикетке»**
|
||
|
||
«Кварта» — четвёртая, и весь сайт стоит на четверге. Страница читается как настенный календарь и крафтовая пачка одновременно: неделя ПН…ВС набрана плотным гротеском с тонкими линейками, четверг (обжарка) и пятница (доставка) выделены красным кофейной ягоды, а на пачках и в первом экране стоит резиновый штамп с датой ближайшей обжарки. Свежесть доказывается датой, а не словом «свежий». Ассортимент — этикетки пачек: название гротеском, ноты строкой через «•», вес и цена моноширинным, по диагонали лента-наклейка «обжарено в чт».
|
||
|
||
Плотно, плоско, печатно. Никаких теней, градиентов и скруглений: глубину дают линейки, крафт и штамп. Красный — только там, где речь о дне, действии или ошибке.
|
||
|
||
**Key Characteristics:**
|
||
- Кремовый фон, тёмно-коричневые текст и линейки, один акцент — красный ягоды.
|
||
- Golos Text для всего текста, Martian Mono только для дат, цен, веса, часов и штампов.
|
||
- Радиус 0. Тени 0. Линейки 1px и 2px цвета `ink`.
|
||
- Подпись бренда — штамп даты с «неровной краской» (SVG-фильтр) и лента недели.
|
||
- Две авторские анимации: оттиск штампа и «заполнение» недели. Остальное — состояния.
|
||
|
||
### Словарь приёмов
|
||
|
||
| Приём | Что это | Где используется |
|
||
|---|---|---|
|
||
| **Лента недели** (`WeekStrip`) | 7 дней «буква + число», тонкие линейки, ЧТ и ПТ красным, подписи «обжарка»/«доставка» на линейке, скобка «1–3 день — в чашке» под ПТ–ВС | Блок «почему свежесть» (полная), OG-картинка (статичная), микро-версия в логотипе (7 штрихов, 4-й красный) |
|
||
| **Штамп** (`Stamp`) | Двойная прямоугольная рамка, текст Martian wdth 112.5 капсом, поворот −7…+5°, смещение, неровность краски | Первый экран (дата ближайшей обжарки, анимирован), поле «дата обжарки» на этикетках (статичный), успех формы «ПРИНЯТО» (анимирован), OG |
|
||
| **Этикетка** (`Label`) | Крафт-прямоугольник пачки, контент разделён линейками на поля, как на печатной этикетке | Карточки сортов, дрип-пакеты, подписка |
|
||
| **Лента-наклейка** (`Ribbon`) | Красная полоса 45° в правом верхнем углу этикетки, «ОБЖАРЕНО В ЧТ» | Только на этикетках ассортимента (4 шт.). Больше нигде |
|
||
| **Поле этикетки** | Подпись mono-tag слева, значение справа, линейка 1px снизу | Внутри Label, в блоке подписки, в часах самовывоза |
|
||
|
||
## Colors
|
||
|
||
Палитра печатной пачки: крем бумаги, крафт, кофейные чернила и одна красная краска.
|
||
|
||
### Primary
|
||
- **Красный ягоды** (`cherry` #B3221B): основная кнопка, ЧТ/ПТ в ленте недели, штамп, лента-наклейка, линия ошибки. Текст `cherry` допустим на `cream` (5.61:1) и `paper` (6.22:1), но не на `kraft` (4.03:1 — только крупный текст ≥24px или 700 ≥18.66px).
|
||
- **Красный глубокий** (`cherry-deep` #8F1A14): hover/active основной кнопки, текст ошибок полей (8.45:1 на `paper`, 7.63:1 на `cream`), красный мелкий текст на крафте (5.47:1).
|
||
- **Красный на тёмном** (`cherry-on-dark` #E8604F): акцентный текст и штрихи на `roast` (5.42:1). Только в тёмных секциях.
|
||
|
||
### Neutral
|
||
- **Крем** (`cream` #F3EBDD): фон страницы. Сплошной, без градиентов и шума.
|
||
- **Бумага** (`paper` #FBF7F0): фон полей формы, блока формы, уведомлений; текст на красных кнопках и ленте.
|
||
- **Крафт** (`kraft` #DCC7A1): поверхность этикеток и блока подписки. Только сплошной цвет, без текстуры-картинки.
|
||
- **Обжарка** (`roast` #1E120C): фон секции «доставка/самовывоз» и подвала.
|
||
- **Чернила** (`ink` #2A1810): весь основной текст, линейки, рамки, фокус-обводка на светлом.
|
||
- **Приглушённый** (`muted` #5E4636): второстепенный текст, подписи полей этикетки, рамка полей формы.
|
||
- **Плейсхолдер** (`placeholder` #7A6352): только `::placeholder` на `paper` (5.27:1).
|
||
- **Линейка мягкая** (`rule-soft` #C9B595): декоративные разделители внутри одной группы (1.69:1 к крему — не несёт смысла, не годится для рамок контролов). Фон disabled-кнопки.
|
||
- **Приглушённый на тёмном** (`muted-on-dark` #BFA88A): второстепенный текст на `roast` (8.01:1).
|
||
- **Успех** (`ok` #2E5E3A): только иконка-галочка и текст в уведомлении «заявка принята» (6.38:1 на `cream`, 7.08:1 на `paper`). Нигде больше.
|
||
|
||
### Контраст (WCAG 2.x, проверено)
|
||
|
||
| Пара | Контраст | Использование |
|
||
|---|---|---|
|
||
| ink / cream | 14.34 | основной текст |
|
||
| ink / paper | 15.90 | поля, форма |
|
||
| ink / kraft | 10.29 | этикетки |
|
||
| muted / cream | 7.37 | второстепенный |
|
||
| muted / kraft | 5.29 | подписи на этикетке |
|
||
| muted / paper | 8.18 | рамка поля (≥3:1 ок) |
|
||
| placeholder / paper | 5.27 | плейсхолдер |
|
||
| cherry / cream | 5.61 | красный текст, ЧТ/ПТ |
|
||
| paper / cherry | 6.22 | основная кнопка, лента |
|
||
| paper / cherry-deep | 8.45 | hover кнопки |
|
||
| cherry-deep / kraft | 5.47 | красный мелкий текст на крафте |
|
||
| cream / roast | 15.46 | текст на тёмном |
|
||
| muted-on-dark / roast | 8.01 | второстепенный на тёмном |
|
||
| cherry-on-dark / roast | 5.42 | акцент на тёмном |
|
||
| ok / paper | 7.08 | успех |
|
||
|
||
### Named Rules
|
||
**Правило одной краски.** Красный занимает ≤10% экрана. Если на экране уже есть красная кнопка, штамп рядом делается без анимации.
|
||
**Правило чёрного по крафту.** На `kraft` мелкий красный текст — только `cherry-deep`.
|
||
|
||
### Tailwind 4 `@theme` (вставить в `apps/landing/src/app/globals.css`)
|
||
|
||
```css
|
||
@import 'tailwindcss';
|
||
|
||
@theme {
|
||
--color-*: initial; /* отключить палитру Tailwind: никаких neutral-*/red-* */
|
||
--color-cream: #F3EBDD;
|
||
--color-paper: #FBF7F0;
|
||
--color-kraft: #DCC7A1;
|
||
--color-roast: #1E120C;
|
||
--color-ink: #2A1810;
|
||
--color-muted: #5E4636;
|
||
--color-placeholder: #7A6352;
|
||
--color-rule-soft: #C9B595;
|
||
--color-cherry: #B3221B;
|
||
--color-cherry-deep: #8F1A14;
|
||
--color-cherry-on-dark: #E8604F;
|
||
--color-muted-on-dark: #BFA88A;
|
||
--color-ok: #2E5E3A;
|
||
--color-transparent: transparent;
|
||
--color-current: currentColor;
|
||
|
||
--radius-*: initial;
|
||
--radius-none: 0px;
|
||
--radius-hair: 2px;
|
||
--shadow-*: initial;
|
||
|
||
--text-display: clamp(2.75rem, 1.6rem + 5.2vw, 6rem);
|
||
--text-display--line-height: 0.92;
|
||
--text-display--letter-spacing: -0.035em;
|
||
--text-headline: clamp(2rem, 1.4rem + 2.8vw, 3.5rem);
|
||
--text-headline--line-height: 1;
|
||
--text-headline--letter-spacing: -0.03em;
|
||
--text-title: clamp(1.5rem, 1.3rem + 0.6vw, 1.875rem);
|
||
--text-title--line-height: 1.05;
|
||
--text-title--letter-spacing: -0.02em;
|
||
--text-lead: clamp(1.125rem, 1rem + 0.5vw, 1.375rem);
|
||
--text-lead--line-height: 1.45;
|
||
--text-body: 1.0625rem;
|
||
--text-body--line-height: 1.55;
|
||
--text-small: 0.875rem;
|
||
--text-small--line-height: 1.45;
|
||
--text-tag: 0.75rem;
|
||
--text-tag--line-height: 1.3;
|
||
--text-tag--letter-spacing: 0.08em;
|
||
--text-price: clamp(1.375rem, 1.2rem + 0.8vw, 2rem);
|
||
--text-price--line-height: 1;
|
||
--text-date: clamp(1.75rem, 1.2rem + 2.2vw, 3.25rem);
|
||
--text-date--line-height: 1;
|
||
|
||
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
|
||
--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
|
||
--ease-in-quart: cubic-bezier(0.5, 0, 0.75, 0);
|
||
|
||
--container-page: 1280px;
|
||
}
|
||
|
||
@theme inline {
|
||
--font-sans: var(--font-golos), system-ui, sans-serif;
|
||
--font-mono: var(--font-martian), ui-monospace, monospace;
|
||
}
|
||
|
||
@layer base {
|
||
html { background: var(--color-cream); color: var(--color-ink); scroll-padding-top: 16px; }
|
||
body { font-family: var(--font-sans); font-size: var(--text-body); line-height: 1.55; -webkit-tap-highlight-color: transparent; }
|
||
::selection { background: var(--color-cherry); color: var(--color-paper); }
|
||
:focus-visible { outline: 2px solid var(--color-ink); outline-offset: 3px; }
|
||
.on-dark :focus-visible { outline-color: var(--color-cream); }
|
||
input, textarea { caret-color: var(--color-cherry); }
|
||
a { text-decoration-thickness: 1px; text-underline-offset: 3px; }
|
||
}
|
||
```
|
||
|
||
После `--color-*: initial` существующие `border-neutral-*`, `text-red-700` в `LeadForm.tsx` перестанут работать — классы заменить на токены при вёрстке (только `className`, логику и data-атрибуты не трогать).
|
||
|
||
## Typography
|
||
|
||
**Display/Body:** Golos Text (variable, wght 400–900), `--font-golos`.
|
||
**Mono:** Martian Mono (variable, wght 100–800, wdth 75–112.5), `--font-martian`.
|
||
|
||
```ts
|
||
// app/layout.tsx
|
||
import { Golos_Text, Martian_Mono } from 'next/font/google';
|
||
const golos = Golos_Text({ subsets: ['cyrillic', 'latin'], variable: '--font-golos', display: 'swap' });
|
||
const martian = Martian_Mono({ subsets: ['cyrillic', 'latin'], variable: '--font-martian', axes: ['wdth'], display: 'swap' });
|
||
// <html lang="ru" className={`${golos.variable} ${martian.variable}`}>
|
||
```
|
||
|
||
**Character:** плотный гротеск с тяжёлыми заголовками, как на настенном календаре; моно — как цифры датера и весов. Курсива нет вообще, serif нет вообще.
|
||
|
||
### Hierarchy (mobile 390 → desktop 1440)
|
||
|
||
| Роль | Шрифт | Размер 390 → 1440 | LH | Tracking | Вес | Где |
|
||
|---|---|---|---|---|---|---|
|
||
| display | Golos | 44 → 96px | 0.92 | −0.035em | 900 | только H1 первого экрана; `text-wrap: balance` |
|
||
| headline | Golos | 32 → 56px | 1.0 | −0.03em | 800 | H2 секций |
|
||
| title | Golos | 24 → 30px | 1.05 | −0.02em | 800 | название сорта, «Доставка»/«Самовывоз», H3 |
|
||
| lead | Golos | 18 → 22px | 1.45 | 0 | 400 | подзаголовок под H1/H2, ≤ 46ch |
|
||
| body | Golos | 17px | 1.55 | 0 | 400 (500 — выделение) | абзацы, ≤ 62ch |
|
||
| small | Golos | 14px | 1.45 | 0 | 500 | регион сорта, примечания, ошибки полей |
|
||
| label (UI) | Golos | 15px | 1.3 | 0 | 600 | подписи полей, кнопки (кнопки 16px/700) |
|
||
| tag | Martian | 12px | 1.3 | 0.08em | 500, wdth 100, CAPS | подписи полей этикетки, ноты, «обжарка»/«доставка» |
|
||
| price | Martian | 22 → 32px | 1.0 | −0.02em | 600, wdth 100, `tnum` | цены «32 BYN», вес «250 г» (вес — 16px/500) |
|
||
| date | Martian | 28 → 52px | 1.0 | −0.04em | 700, **wdth 87.5** | числа в ленте недели |
|
||
| day-letter | Golos | 14 → 16px | 1 | 0.04em | 800, CAPS | «ПН», «ВТ» в ленте |
|
||
| stamp | Martian | 14–22px (по размеру штампа) | 1 | 0.04em | 700, **wdth 112.5**, CAPS | текст штампа |
|
||
| hours | Martian | 15px | 1.4 | 0 | 500, wdth 100, `tnum` | часы самовывоза, телефон |
|
||
|
||
`wdth` задавать через `font-stretch: 87.5%` / `112.5%`. Если next/font не объявит диапазон `font-stretch`, использовать `font-variation-settings: "wdth" 87.5` (вес продолжает задаваться `font-weight`).
|
||
|
||
### Named Rules
|
||
**Правило моно-фактов.** Martian Mono — только для чисел, дат, веса, цен, часов, телефона и коротких CAPS-меток. Предложения и заголовки моно не набираются.
|
||
**Правило без сплошного капса.** CAPS — только tag, day-letter, stamp и лента-наклейка. Заголовки — в предложном регистре.
|
||
|
||
## Layout
|
||
|
||
- **Mobile-first.** Брейкпоинты Tailwind по умолчанию: `md` 768 (две колонки), `lg` 1024 (12-колоночная сетка), `xl` 1280.
|
||
- **Сетка:** 390 — 4 колонки, гаттер 16px, поля 20px. ≥1024 — 12 колонок, гаттер 24px, поля 48px, контейнер `max-w-page` 1280px по центру (на 1440 поля ≈ 80px).
|
||
- **Ритм секций:** padding-y 72px (390) → 128px (1440). Отступ над H2 больше, чем под ним: H2 → лид 16px, лид → контент 40px (390) / 64px (1440).
|
||
- **Разделение секций:** не фоном-плашкой, а линейкой 2px `ink` по ширине контейнера сверху секции + сменой поверхности только у тёмного блока и формы.
|
||
- **Шкала отступов:** 4, 8, 12, 16, 20, 24, 32, 48, 72, 128.
|
||
|
||
## Elevation & Depth
|
||
|
||
Плоско. `box-shadow` не используется нигде (токены теней обнулены). Глубину дают: смена поверхности (cream → kraft → paper → roast), линейки 1/2px, наложение штампа поверх фото и края колонки, лента-наклейка, срезанная краем этикетки (`overflow: hidden`).
|
||
|
||
**Правило печати.** Если хочется тени — нужна линейка или смена поверхности.
|
||
|
||
## Shapes
|
||
|
||
- Радиус 0 у всего: кнопки, поля, этикетки, фото, уведомления, фокус-обводка. `hair` 2px — запас для мелких SVG-квадратиков (маркер выбранного интереса), больше нигде.
|
||
- Линейки: `1px ink` — внутри этикеток и ленты недели; `1.5px` — рамки кнопок secondary и полей формы (`muted`, в фокусе/ошибке 2px); `2px ink` — верх секций, рамка штампа (внешняя 2px + внутренняя 1px, зазор 3px), рамка блока формы.
|
||
- Перфорация дрип-пакета: верхний край — SVG пунктир (dash 6, gap 4, 1px ink).
|
||
|
||
## Components
|
||
|
||
### Stamp
|
||
Резиновый оттиск даты. Inline SVG, чтобы фильтр краски жил внутри SVG, а анимировалась обёртка.
|
||
- **Props:** `lines: string[]` (1–3 строки, CAPS), `size: 'sm' | 'md' | 'lg'` (ширина 112 / 168 / 232px), `rotate` (−7…+5°, по умолчанию −6), `animate?: boolean`, `tone: 'cherry' | 'cherry-on-dark'`.
|
||
- **Вид:** двойная рамка (2px снаружи, 1px внутри, зазор 3px), цвет `cherry`, `opacity: 0.92`; текст Martian 700, wdth 112.5; строки разделены линейкой 1px. Смещение «мимо приводки»: внутренний текст сдвинут на `translate(1px, -1px)` относительно рамки.
|
||
- **Неровность краски:** один общий `<svg><defs><filter id="stamp-ink">` в layout: `feTurbulence type=fractalNoise baseFrequency=0.9 numOctaves=2 seed=4` → `feDisplacementMap scale=1.6` (дрожание края) + вторая `feTurbulence baseFrequency=0.06` → `feColorMatrix` в альфу → `feComposite operator=in` (пропуски краски ~10%). Фильтр статичен, не анимируется.
|
||
- **Экземпляры:** hero — `lg`, строки «ОБЖАРКА» / «ЧТ 22.10» (дата вычисляется) / «МИНСК»; этикетка — `sm`, «ЧТ 22.10»; успех формы — `md`, «ПРИНЯТО» и «ДОПИСАЛИ» для `clarified`.
|
||
- **Доступность:** SVG `aria-hidden`, рядом визуально скрытый текст «Ближайшая обжарка — четверг, 22 октября».
|
||
|
||
### Дата ближайшей обжарки
|
||
`lib/roastWeek.ts`: `getRoastWeek(now: Date)` в часовом поясе Europe/Minsk → `{ days: {letter, date, iso}[7] (пн–вс), roast: четверг (сегодня, если сегодня чт, иначе следующий), delivery: roast + 1 }`. Страница — серверный компонент с `export const revalidate = 3600`. Формат: `ЧТ 22.10`. Это следует из факта «обжарка по четвергам», ничего не добавляет.
|
||
|
||
### WeekStrip
|
||
- **390 (вертикально, «дни столбиком»):** 7 строк по 48px. Строка: слева колонка 64px — day-letter над числом date; справа линейка 1px `ink` по базовой линии, на ней tag-подпись. ЧТ: буква и число `cherry`, подпись «ОБЖАРКА · ПАРТИЯ 5 КГ». ПТ: `cherry`, «ДОСТАВКА ПО МИНСКУ». СБ, ВС и ПТ справа охвачены вертикальной скобкой 2px `cherry` с подписью «1–3 ДЕНЬ — ЗЕРНО У ВАС». Остальные дни `muted`, без подписи.
|
||
- **1440 (горизонтально):** 7 равных колонок на 12-колоночной сетке, высота ~200px. Сверху day-letter, под ним date (52px), снизу общая линейка 1px через всю ширину; подписи tag под линейкой в колонках ЧТ и ПТ. Под ПТ–ВС — горизонтальная скобка (2px, загнутые концы 8px) с подписью «1–3 ДЕНЬ ПОСЛЕ ОБЖАРКИ — ЗЕРНО У ВАС». Вертикальные разделители колонок — 1px `rule-soft`.
|
||
- **Разметка:** `<ol aria-label="Неделя обжарки">` из 7 `<li>`; у ЧТ и ПТ подпись — текст в `<li>`, не только цвет. Числа в `<time dateTime>`.
|
||
|
||
### Label (карточка сорта)
|
||
```
|
||
┌─────────────────────────────────┐ ← kraft, 1px ink, overflow hidden
|
||
│ КВАРТА · МИНСК ╲лента│ tag, muted
|
||
│ │
|
||
│ Эфиопия │ title 800
|
||
│ Иргачеффе │ small 500, muted
|
||
│─────────────────────────────────│ 1px ink
|
||
│ НОТЫ ЦВЕТОЧНЫЙ • БЕРГАМОТ │ tag muted / tag ink
|
||
│─────────────────────────────────│
|
||
│ ОБЖАРКА СВЕТЛАЯ │ только если есть в ТЗ
|
||
│─────────────────────────────────│
|
||
│ ДАТА ОБЖАРКИ [ЧТ 22.10] │ поле + Stamp sm
|
||
│─────────────────────────────────│
|
||
│ 250 г 32 BYN │ PriceTag
|
||
│ [ Заказать эту пачку ] │ button-secondary, 100%
|
||
└─────────────────────────────────┘
|
||
```
|
||
- Padding 20px (390) / 24px (1440). Поля этикетки — строки min-height 40px, подпись tag `muted` колонкой 96px, значение tag `ink`.
|
||
- Строка третьего поля только из фактов: Эфиопия — «ОБЖАРКА: СВЕТЛАЯ», Бразилия — «ПОД ЭСПРЕССО», у Колумбии строки нет (в ТЗ нет — не выдумывать).
|
||
- **Ribbon:** абсолютный блок 160×28px, `cherry`, текст tag `paper` «ОБЖАРЕНО В ЧТ», `rotate(45deg)`, центр в точке (right: −40px, top: 24px), срезан `overflow: hidden` карточки. `aria-hidden`; тот же смысл есть в поле «дата обжарки».
|
||
- **Дрип-пакеты:** вариант `Label variant="drip"`: на 1440 горизонтальный (вся ширина, высота ~180px; слева название «Дрип-пакеты» + «ассорти», в центре поля «10 ШТ», справа цена и кнопка), сверху перфорация вместо сплошной линии. На 390 — как обычная этикетка, но ниже (без поля нот). Лента та же.
|
||
- Никаких фото внутри этикеток и никаких вложенных карточек.
|
||
|
||
### PriceTag
|
||
`<p>`: вес слева (Martian 16px/500), цена справа (price, 600, `tnum`), «BYN» тем же шрифтом на 60% размера, базовая линия общая. Подписка: «54 BYN» + «/ мес» (16px, `muted`). Сверху линейка 1px `ink`. Цена — всегда текст, не картинка.
|
||
|
||
### Кнопки
|
||
Тач-цель ≥ 52px высотой, на 390 основные CTA на всю ширину. Шрифт Golos 16px/700, без CAPS. Переходы только `background-color`, `color`, `transform`.
|
||
|
||
| Состояние | primary (`cherry`) | secondary (рамка 1.5px `ink`) | on-dark secondary |
|
||
|---|---|---|---|
|
||
| idle | bg cherry, текст paper | bg transparent, текст ink | рамка и текст cream |
|
||
| hover (`@media (hover:hover)`) | bg cherry-deep, 120ms ease-out | bg ink, текст cream, 120ms | bg cream, текст roast |
|
||
| focus-visible | outline 2px ink, offset 3px (на тёмном — cream) | то же | outline cream |
|
||
| active | bg cherry-deep, `translateY(1px)`, 60ms | `translateY(1px)` | `translateY(1px)` |
|
||
| disabled (не отправка) | bg rule-soft, текст ink (8.51:1), `cursor: not-allowed`, без hover | рамка rule-soft, текст muted | — |
|
||
| submitting (`aria-busy="true"`) | bg cherry остаётся (не «серая»), текст «Отправляем…» из FORM_TEXT, справа три квадратика 4px paper, мигают по очереди (opacity 0.3→1, шаг 160ms), `cursor: progress`; ширина кнопки фиксирована `min-width` по idle, чтобы не прыгала | — | — |
|
||
| success | кнопка исчезает вместе с формой (так в LeadForm); фокус уходит на «Отправить ещё одну» (secondary) | | |
|
||
| error | кнопка возвращается в idle; над ней блок уведомления (см. ниже) | | |
|
||
|
||
Стили submitting вешать на `[aria-busy="true"]`, disabled-вид — на `:disabled:not([aria-busy])`. Иконка-стрелка в secondary этикетки — SVG 16px, stroke 1.5.
|
||
|
||
### Поля формы (`LeadForm`, data-атрибуты уже есть)
|
||
- **Блок формы:** `paper`, рамка 2px `ink`, padding 20px (390) / 40px (1440). Шапка блока — строка «ЗАЯВКА» tag слева и Stamp-место справа (пусто до успеха).
|
||
- **Label:** Golos 15px/600 ink, отступ до поля 8px. Между полями 20px.
|
||
- **Input/textarea:** высота 52px (textarea min 112px), bg `paper`, рамка 1.5px `muted`, радиус 0, padding 0 14px, Golos 17px (≥16px — iOS не зумит). Плейсхолдер `placeholder`.
|
||
- **focus-visible / focus:** рамка 2px `ink` (padding компенсировать −0.5px), outline 2px `ink` offset 2px.
|
||
- **`[data-invalid]`:** рамка 2px `cherry`; текст ошибки `[data-field-error]` — small 500 `cherry-deep`, отступ 6px, слева SVG «!» в квадрате 14px `cherry`. Ошибка не только цветом: есть текст и `aria-describedby` (уже в коде).
|
||
- **Интерес (`fieldset[role=radiogroup]`):** три «талона» — `label` min-height 48px, рамка 1.5px `muted`, padding 0 16px; на 390 столбиком на всю ширину, ≥768 в ряд. Нативный radio визуально скрыт (`sr-only`, не `display:none`). `[data-checked]`: bg `ink`, текст `cream`, слева квадрат 10px `cherry`. Фокус: `label:has(:focus-visible)` outline 2px ink offset 2px. `fieldset[data-invalid]` — у всех талонов рамка `cherry`.
|
||
- **Honeypot:** не трогать.
|
||
|
||
### Уведомления (`[data-lead-notice]`, по `data-notice` на обёртке)
|
||
- **created / clarified (`data-state="success"`):** блок paper, рамка 2px `ink`, padding 24px; Stamp `md` «ПРИНЯТО» (или «ДОПИСАЛИ» для clarified), поворот −5°, анимация оттиска; рядом текст title 800 из FORM_TEXT; галочка SVG `ok` 20px. Ниже — кнопка secondary «Отправить ещё одну».
|
||
- **failed / rate_limited (`data-state="error"`):** блок paper, линейка 2px `cherry` сверху (не слева), tag `cherry-deep` «НЕ ОТПРАВЛЕНО», текст body, телефон — Martian 18px/600, ссылка `tel:`, тач-цель 44px.
|
||
- `aria-live="polite"` уже стоит; блок не прыгает: при появлении — только opacity 0→1, 160ms.
|
||
|
||
### StickyCta (только < 768px)
|
||
- Фиксирован снизу, `padding: 8px 20px calc(8px + env(safe-area-inset-bottom))`, bg `cream`, линейка 1px `ink` сверху, без тени. Внутри слева подпись `site.stickyCta.note` («Обжарка в четверг, доставка в пятницу», small 500, `muted`, 2 строки максимум), справа CtaButton primary 52px «Заказать кофе» (`site.stickyCta.label`).
|
||
- `interest: 'one_time'`, без comment, `source="sticky"`.
|
||
- Показывается, когда главная CTA первого экрана ушла из вьюпорта; скрывается, когда в вьюпорте `#lead`, или фокус внутри формы, или открыта клавиатура (`visualViewport.height < innerHeight * 0.75`). Один IntersectionObserver на два якоря. В скрытом состоянии `aria-hidden="true"` и `inert`.
|
||
- Подвалу на мобиле — `padding-bottom: 88px`, чтобы бар не закрывал контакты.
|
||
|
||
### Шапка
|
||
Не липкая. Высота 64px. Слева логотип: SVG-знак (7 вертикальных штрихов 3×20px, шаг 3px, 4-й `cherry`, остальные `ink`) + «Кварта» Golos 900 22px. Справа на 390 — ссылка-иконка телефона 44×44 (`aria-label="Позвонить: +375 29 000-00-00"`); на ≥1024 — якоря «Сорта», «Подписка», «Доставка» (Golos 15px/600, подчёркивание при hover) и телефон Martian 15px.
|
||
|
||
## Секции
|
||
|
||
Тексты — из `content/site.ts` (пишет копирайтер), CTA-подписи — из `CTAS`/site.ts. Ниже — композиция и факты, которые обязаны попасть в блок.
|
||
|
||
### 1. Первый экран `#hero`
|
||
- **Факты:** обжарка по четвергам, развоз по пятницам, Минск. Дата ближайшей обжарки — в штампе.
|
||
- **390:** шапка 64 → H1 display (≤ 3 строк, ≤ 40 знаков) → lead (≤ 3 строк) → 24px → primary CTA на всю ширину → 12px → secondary CTA на всю ширину → 56px → фото зерна во всю ширину экрана (без полей), 390×300, Stamp на бумажной подложке (`backing`, иначе красный на тёмном зерне нечитаем) наезжает на верхний левый край фото (top −56px, left 20px, rotate −6°). Первый вьюпорт (≈ 750px) вмещает H1, lead, CTA и верх штампа.
|
||
- **1440:** 12 колонок. Кол. 1–7: H1 96px, lead (≤ 46ch), ряд CTA (primary + ссылка). Кол. 8–12: фото зерна 4:5 (≈ 500×625), выровнено по верху H1, обрезано правым полем контейнера. Stamp `lg` лежит на левом нижнем углу фото, заходя на 64px в кол. 7. Высота секции ≈ 760px.
|
||
- **CTA (по site.ts):** primary — CtaButton `site.hero.ctaPrimary` (`subscription`), secondary — CtaButton `site.hero.ctaSecondary` (`one_time`, без comment). `site.hero.eyebrow` не выводится: надзаголовки запрещены (см. Don't).
|
||
- **Фото:** GDmiFs9lfrQ (Mona Jain, зерно на весь кадр). `next/image`, `priority`, `fetchPriority="high"`, `sizes="(min-width: 1024px) 500px, 100vw"`, `object-fit: cover; object-position: center`. Alt: «Обжаренное кофейное зерно крупным планом».
|
||
|
||
### 2. Почему свежесть `#fresh`
|
||
- **Факты:** обжарка в четверг партиями по 5 кг; развоз по Минску в пятницу; зерно у вас на 1–3 день после обжарки.
|
||
- **390:** H2 → lead → WeekStrip вертикальный.
|
||
- **1440:** H2 и lead в кол. 1–6, WeekStrip горизонтальный на все 12 колонок ниже.
|
||
- **Анимация:** WeekStrip (см. «Моушн»). CTA нет. Фото нет. SVG: скобка, линейки.
|
||
|
||
### 3. Ассортимент `#coffee`
|
||
- **Факты:** три сорта и дрип-пакеты — названия, ноты, вес, цены точно по ТЗ; под сеткой строка small «Доставка по Минску от 50 BYN — бесплатно» со ссылкой на `#delivery`.
|
||
- **390:** H2 → 3 Label столбиком (gap 16px) → Label drip.
|
||
- **768:** 2 колонки, drip на всю ширину.
|
||
- **1440:** 3 Label по 4 колонки, ниже drip горизонтальный на 12 колонок.
|
||
- **CTA (secondary в каждой этикетке, `source="coffee"`):**
|
||
- Эфиопия: `one_time`, comment «Эфиопия Иргачеффе, 250 г» (совпадает с CTAS.ethiopia)
|
||
- Колумбия: `one_time`, «Колумбия Уила, 250 г»
|
||
- Бразилия: `one_time`, «Бразилия Серрадо, 250 г»
|
||
- Дрип: `one_time`, «Дрип-пакеты ассорти, 10 шт»
|
||
- Фото нет. SVG: лента, перфорация, стрелка, штампы `sm` (статичные, повороты −4°, 3°, −2°, 5° — разные, чтобы не было копипасты).
|
||
|
||
### 4. Подписка `#subscription`
|
||
- **Факты:** 2 пачки по 250 г раз в месяц, доставка бесплатно, 54 BYN/мес.
|
||
- **Вид:** одна большая этикетка-абонемент на `kraft`, без ленты. Внутри поля: «СОСТАВ — 2 ПАЧКИ × 250 Г», «КАК ЧАСТО — РАЗ В МЕСЯЦ», «ДОСТАВКА — БЕСПЛАТНО»; справа/снизу PriceTag крупно: «54 BYN / мес» (price ×1.5 = 48px на 1440). SVG: два силуэта пачки (прямоугольник с клапаном, 1.5px ink, без заливки), во второй пачке маленький знак «7 штрихов».
|
||
- **390:** H2 → lead → этикетка (поля столбиком, цена внизу) → primary CTA во всю ширину.
|
||
- **1440:** H2 + lead в кол. 1–5; этикетка в кол. 6–12: слева поля, справа цена и primary CTA.
|
||
- **CTA:** CtaButton `site.subscription.cta` (`subscription`, comment «Подписка: 2 пачки в месяц»).
|
||
|
||
### 5. Кто мы `#about`
|
||
- **Факты:** с 2019 года, основатели Аня и Дмитрий, бывшие бариста, партии по 5 кг. Ничего больше (ни цитат, ни наград).
|
||
- **390:** фото на всю ширину 390×260 → H2 → body → строка Martian tag «С 2019 · МИНСК · ПАРТИИ ПО 5 КГ» между линейками 1px.
|
||
- **1440:** фото кол. 1–6 (3:2, ≈ 610×407), текст кол. 8–12, выровнен по низу фото.
|
||
- **Фото:** _7CVm353m7A (Scott Soltys-Curry, охладитель ростера). `sizes="(min-width: 1024px) 610px, 100vw"`, `object-position: center 40%`, lazy. Alt: «Свежеобжаренное зерно остывает в охладителе ростера». Подпись/текст не утверждает, что это цех «Кварты».
|
||
- CTA нет.
|
||
|
||
### 6. Доставка и самовывоз `#delivery` (тёмная, класс `on-dark`)
|
||
- **Фон:** `roast`, текст `cream`. Факты: доставка по Минску в пятницу, от 50 BYN бесплатно, дешевле — стоимость уточнит менеджер; самовывоз ул. Короля, 9; пн–пт 8:00–20:00, сб–вс 10:00–18:00.
|
||
- **390:** фото мешка 390×220 сверху (на всю ширину) → H2 → блок «Доставка» (title; поля этикетки: подпись `muted-on-dark`, значение `cream`, линейки 1px `muted-on-dark`) → блок «Самовывоз»: адрес title, часы таблицей Martian (`<dl>`: «ПН–ПТ 8:00–20:00», «СБ–ВС 10:00–18:00»), ссылка «Открыть в Яндекс Картах» (`https://yandex.by/maps/?text=Минск, ул. Короля, 9`, `target=_blank rel=noopener`) → secondary on-dark CTA.
|
||
- **1440:** фото кол. 1–5 на всю высоту секции (bleed влево до края экрана), справа кол. 7–12: H2, два блока рядом (по 3 колонки), CTA снизу.
|
||
- **Фото:** obV_LM0KjxY (Tina Guina, мешок на тёмном). `sizes="(min-width: 1024px) 40vw, 100vw"`, `object-position: center`, lazy. Alt: «Открытый мешок с кофейным зерном».
|
||
- **CTA:** CtaButton secondary on-dark, `interest: 'question'`, без comment (подпись CTAS.question). Акценты (ПТ, «бесплатно») — `cherry-on-dark`.
|
||
- Карты-iframe нет (производительность).
|
||
|
||
### 7. Форма `#lead`
|
||
- Существующий `LeadForm` в блоке paper (см. «Поля формы»). `scroll-mt-4` уже есть.
|
||
- **390:** H2 → lead → блок формы на всю ширину → строка «или позвоните» + телефон Martian.
|
||
- **1440:** кол. 1–5: H2, lead, телефон Martian 22px, часы работы tag; кол. 6–12: блок формы.
|
||
- Комментарий, пришедший из CTA, виден в textarea — посетитель его видит и может стереть.
|
||
|
||
### 8. Подвал (`roast`, `on-dark`)
|
||
Логотип (знак + «Кварта» cream), адрес, часы, телефон; строка small `muted-on-dark`: «Фото: Mona Jain, Tina Guina, Scott Soltys-Curry — Unsplash». Без соцсетей (их нет в ТЗ). На 390 — колонкой, на 1440 — 4 колонки по 3.
|
||
|
||
### Фото: общая техника
|
||
Скачать три фото в `apps/landing/public/photos/` (`beans.jpg`, `sack.jpg`, `cooler.jpg`, ширина 2000px, JPEG q≈78) — без `remotePatterns`. Фото без фильтров и цветокора «под бежевый»; допустим только кроп.
|
||
|
||
## Моушн
|
||
|
||
Только CSS (`@keyframes`, `transition`) + один общий хук `useInView` на IntersectionObserver (`threshold: 0.35`, `once`). Анимируются только `transform` и `opacity` (у скобки — `transform: scaleX/scaleY`). Bounce, overshoot, spring — нет.
|
||
|
||
**Видимое по умолчанию.** Без JS всё в финальном состоянии. Инлайн-скрипт в `<head>` ставит `document.documentElement.dataset.motion = ''` только если `!matchMedia('(prefers-reduced-motion: reduce)').matches && 'IntersectionObserver' in window`. Начальные состояния анимаций пишутся под `[data-motion] .x:not([data-inview])`.
|
||
|
||
| Движение | Триггер | Длительность / задержка | Easing | Что | Reduced motion |
|
||
|---|---|---|---|---|---|
|
||
| Оттиск штампа (hero) | загрузка, CSS-анимация, без IO | 380ms, delay 250ms | `--ease-out-expo` | opacity 0→0.92, scale 1.18→1, rotate (r−3°)→r | статичен с начала |
|
||
| Оттиск штампа (успех формы) | `data-state="success"` | 380ms, delay 80ms | `--ease-out-expo` | то же | статичен |
|
||
| Штампы на этикетках | — | — | — | не анимируются | — |
|
||
| Лента недели | IO 0.35 | линейка scaleX 0→1 600ms (origin left); ЧТ: цвет-подложка opacity 0→1 + подпись translateY(6px)→0, opacity, 320ms, delay 300ms; ПТ — то же, delay 480ms; скобка scaleX (на 390 scaleY) 0→1 500ms, delay 680ms, подпись скобки opacity 240ms, delay 1000ms | `--ease-out-quart` | красный текст ЧТ/ПТ — отдельный слой поверх серого, меняется opacity (не `color`) | всё в финале сразу |
|
||
| Кнопка hover | pointer | 120ms | ease-out | background-color | без изменений (это не движение) |
|
||
| Кнопка active | press | 60ms | linear | translateY(1px) | убрать translate |
|
||
| Submitting-точки | `aria-busy` | цикл 480ms, шаг 160ms | steps(1) | opacity 0.3↔1 | точки статичны (все 1), текст «Отправляем…» остаётся |
|
||
| Уведомление | появление | 160ms | ease-out | opacity | мгновенно |
|
||
| StickyCta | IO по якорям | показ 240ms `--ease-out-quart`, скрытие 180ms `--ease-in-quart` | | translateY(100%)↔0 | только мгновенное появление/скрытие |
|
||
| Скролл к форме | CTA | нативный smooth (уже в prefillLead) | | | `auto` (уже в коде) |
|
||
|
||
`will-change` не ставить заранее. Никаких parallax, scroll-linked, marquee, анимаций на каждой секции.
|
||
|
||
## Do's and Don'ts
|
||
|
||
### Do:
|
||
- **Do** показывать свежесть датой: штамп «ЧТ 22.10», лента недели.
|
||
- **Do** держать все цены и условия из ТЗ текстом, видимыми без клика.
|
||
- **Do** использовать линейки 1/2px `ink` для структуры вместо плашек.
|
||
- **Do** давать Martian Mono только числам и коротким меткам.
|
||
- **Do** разные повороты штампов (−7…+5°), смещение текста относительно рамки.
|
||
- **Do** проверять на 390 и 1440 с реальными текстами из site.ts.
|
||
|
||
### Don't (impeccable + «бежевый AI»):
|
||
- **Don't** мягкие бежевые градиенты, шум/зерно-оверлеи, «тёплую» дымку на фото.
|
||
- **Don't** скруглённые карточки с тенями; `box-shadow` любой, включая жёсткую смещённую.
|
||
- **Don't** generic hero с чашкой/латте-артом, руками, людьми, паром.
|
||
- **Don't** фиолетовые, градиентные, неоновые акценты; второй акцентный цвет; градиентный текст.
|
||
- **Don't** курсив и serif — ни в заголовках, ни в цитатах.
|
||
- **Don't** надзаголовки-«кикеры» над H2 и номера секций «01/02/03».
|
||
- **Don't** сетку «иконка + заголовок + текст» в одинаковых карточках, hero-метрики «5 кг / 2019 / 1–3 дня» крупными цифрами.
|
||
- **Don't** вложенные карточки (этикетка внутри этикетки).
|
||
- **Don't** сплошной CAPS и тотальный моно (берём из FreshColor/Altura только поле даты и ноты).
|
||
- **Don't** пёструю палитру Altura, белый фон и сетку месяца календаря Rationale.
|
||
- **Don't** `border-left` цветной на уведомлениях; эмодзи и юникод-глифы вместо иконок (иконки — свой SVG, stroke 1.5).
|
||
- **Don't** выдумывать отзывы, рейтинги, «лучший кофе в Минске», соцсети, адреса, цены доставки ниже 50 BYN.
|
||
- **Don't** тяжёлые библиотеки анимаций (framer-motion, GSAP, Lottie), карты-iframe, видео.
|
||
|
||
## Доступность
|
||
|
||
- Порядок фокуса = визуальный порядок: шапка → hero CTA → … → форма → подвал. StickyCta в DOM последней; скрытая — `inert`.
|
||
- Фокус: `:focus-visible` outline 2px `ink` offset 3px; на тёмном — `cream`; у талонов интереса — на `label:has(:focus-visible)`. Никогда `outline: none` без замены.
|
||
- Тач-цели ≥ 44×44 (кнопки 52px, талоны 48px, ссылки-якоря с padding до 44px, телефон в шапке 44×44).
|
||
- Ошибки — текстом + `aria-invalid` + `aria-describedby` (уже в коде), не только цветом. ЧТ/ПТ в ленте — подписью, не только цветом.
|
||
- `lang="ru"`. Один H1. H2 у каждой секции, у секций `aria-labelledby`.
|
||
- Alt фото — см. секции; декоративные SVG `aria-hidden`.
|
||
- Поля 17px (iOS без зума). `inputmode`/`autocomplete` уже стоят.
|
||
- Контраст — таблица в «Colors». Масштаб текста 200% без горизонтального скролла на 390.
|
||
|
||
## OG-картинка 1200×630 (`app/opengraph-image.tsx`, next/og)
|
||
|
||
```
|
||
┌────────────────────────────────────────────────────────────┐ cream
|
||
│ ▮▮▮▮▮▮▮ Кварта ┌──────────────┐ │ знак + Golos 900 64px
|
||
│ │ ОБЖАРКА ПО │ │ Stamp cherry,
|
||
│ Свежая обжарка │ ЧЕТВЕРГАМ │ │ rotate −6°, 300px
|
||
│ в Минске └──────────────┘ │ Golos 900 88px, ink, lh .92
|
||
│────────────────────────────────────────────────────────────│ 2px ink, y=380
|
||
│ ПН ВТ СР [ЧТ] [ПТ] СБ ВС │ day-letter 26px; ЧТ/ПТ cherry
|
||
│ ОБЖАРКА ДОСТАВКА │ Martian 20px
|
||
│ └──── 1–3 день — зерно у вас ──┘ │ скобка 2px cherry
|
||
│ kvarta.unuchak.com +375 29 000-00-00 │ Martian 20px muted
|
||
└────────────────────────────────────────────────────────────┘
|
||
```
|
||
- Поля 64px. Без фото и без дат (картинка кешируется), только дни недели.
|
||
- Satori не поддерживает SVG-фильтры и variable-шрифты: штамп — двойная рамка без «краски», шрифты — статичные TTF (Golos Text 900 и 500, Martian Mono 700), загружаются `fetch` из файлов в `src/app/og-fonts/` (подмножество кириллица+латиница). `alt`: «Кварта — свежая обжарка в Минске, обжарка по четвергам».
|