design: итерация критика 2
Заголовки через typo(): «и» не висит. Подписка: у цены нет своей линейки, цена и CTA по низу полей. Этикетки сортов делят строки через subgrid, ступеньки под названием нет; значения полей с интерлиньяжем 1.3, «•» держится за следующую ноту, вес «250 г» — число и единица отдельными span. Под ассортиментом строка про бесплатную доставку от 50 BYN. В тёмной секции строки доставки — поля с подписями, акцент только на «бесплатно». В подвале слеш не отрывается.
This commit is contained in:
@@ -370,6 +370,9 @@ const martian = Martian_Mono({ subsets: ['cyrillic', 'latin'], variable: '--font
|
|||||||
- Поле `spec` (site.ts) только из фактов: Эфиопия — «ОБЖАРКА — светлая», Бразилия — «ДЛЯ — эспрессо», дрип — «КАК — заваривается прямо в чашке», у Колумбии поля нет (в ТЗ нет — не выдумывать). Значение — Golos small 500.
|
- Поле `spec` (site.ts) только из фактов: Эфиопия — «ОБЖАРКА — светлая», Бразилия — «ДЛЯ — эспрессо», дрип — «КАК — заваривается прямо в чашке», у Колумбии поля нет (в ТЗ нет — не выдумывать). Значение — Golos small 500.
|
||||||
- Строка «ДАТА ОБЖАРКИ»: min-height 72px, `align-items: center`, padding-y 12px; под ней сразу линейка цены (без зазора). Штамп `lg`-строкой шириной 136px; повороты −4°, 3°, −2°, 5° и сдвиги по X 0 / 12 / 4 / 20px (Эфиопия, Колумбия, Бразилия, дрип).
|
- Строка «ДАТА ОБЖАРКИ»: min-height 72px, `align-items: center`, padding-y 12px; под ней сразу линейка цены (без зазора). Штамп `lg`-строкой шириной 136px; повороты −4°, 3°, −2°, 5° и сдвиги по X 0 / 12 / 4 / 20px (Эфиопия, Колумбия, Бразилия, дрип).
|
||||||
- Лент-наклеек нет (сняты в итерации критика 1).
|
- Лент-наклеек нет (сняты в итерации критика 1).
|
||||||
|
- На ≥768 карточки сортов делят строки сетки через `grid-template-rows: subgrid` (шапка / поля / цена): линейка под названием на одной высоте у всех; строка «дата обжарки» растягивается, если полей меньше.
|
||||||
|
- Значения полей — line-height 1.3; «•» между нотами склеен неразрывным пробелом со следующей нотой.
|
||||||
|
- Вес «250 г»: число и единица — отдельные span, отступ 0.25em через CSS вместо моноширинного пробела.
|
||||||
- Ноты склеены неразрывным пробелом внутри ноты («красное яблоко» не рвётся).
|
- Ноты склеены неразрывным пробелом внутри ноты («красное яблоко» не рвётся).
|
||||||
- **Дрип-пакеты:** вариант `Label variant="drip"`: на 1440 горизонтальный (вся ширина, высота ~180px; слева название «Дрип-пакеты» + «ассорти», в центре поля «10 ШТ», справа цена и кнопка), сверху перфорация вместо сплошной линии. На 390 — как обычная этикетка, но ниже (без поля нот). Лента та же.
|
- **Дрип-пакеты:** вариант `Label variant="drip"`: на 1440 горизонтальный (вся ширина, высота ~180px; слева название «Дрип-пакеты» + «ассорти», в центре поля «10 ШТ», справа цена и кнопка), сверху перфорация вместо сплошной линии. На 390 — как обычная этикетка, но ниже (без поля нот). Лента та же.
|
||||||
- Никаких фото внутри этикеток и никаких вложенных карточек.
|
- Никаких фото внутри этикеток и никаких вложенных карточек.
|
||||||
@@ -451,6 +454,7 @@ const martian = Martian_Mono({ subsets: ['cyrillic', 'latin'], variable: '--font
|
|||||||
- **Вид:** одна большая этикетка-абонемент на `kraft`, без ленты. Внутри поля: «СОСТАВ — 2 ПАЧКИ × 250 Г», «КАК ЧАСТО — РАЗ В МЕСЯЦ», «ДОСТАВКА — БЕСПЛАТНО»; справа/снизу PriceTag крупно: «54 BYN / мес» (price ×1.5 = 48px на 1440). SVG: два силуэта пачки (прямоугольник с клапаном, 1.5px ink, без заливки), во второй пачке маленький знак «7 штрихов».
|
- **Вид:** одна большая этикетка-абонемент на `kraft`, без ленты. Внутри поля: «СОСТАВ — 2 ПАЧКИ × 250 Г», «КАК ЧАСТО — РАЗ В МЕСЯЦ», «ДОСТАВКА — БЕСПЛАТНО»; справа/снизу PriceTag крупно: «54 BYN / мес» (price ×1.5 = 48px на 1440). SVG: два силуэта пачки (прямоугольник с клапаном, 1.5px ink, без заливки), во второй пачке маленький знак «7 штрихов».
|
||||||
- **390:** H2 → lead → этикетка (поля столбиком, цена внизу) → primary CTA во всю ширину.
|
- **390:** H2 → lead → этикетка (поля столбиком, цена внизу) → primary CTA во всю ширину.
|
||||||
- **1440:** H2 + lead в кол. 1–5; этикетка в кол. 6–12: слева поля, справа цена и primary CTA.
|
- **1440:** H2 + lead в кол. 1–5; этикетка в кол. 6–12: слева поля, справа цена и primary CTA.
|
||||||
|
- Цена без своей линейки: поля подписки замкнуты линейкой снизу, цена и CTA выровнены по низу полей; примечание — строкой под всей этикеткой.
|
||||||
- **CTA:** CtaButton `site.subscription.cta` (`subscription`, comment «Подписка: 2 пачки в месяц»).
|
- **CTA:** CtaButton `site.subscription.cta` (`subscription`, comment «Подписка: 2 пачки в месяц»).
|
||||||
|
|
||||||
### 5. Кто мы `#about`
|
### 5. Кто мы `#about`
|
||||||
@@ -464,6 +468,8 @@ const martian = Martian_Mono({ subsets: ['cyrillic', 'latin'], variable: '--font
|
|||||||
- **Фон:** `roast`, текст `cream`. Факты: доставка по Минску в пятницу, от 50 BYN бесплатно, дешевле — стоимость уточнит менеджер; самовывоз ул. Короля, 9; пн–пт 8:00–20:00, сб–вс 10:00–18:00.
|
- **Фон:** `roast`, текст `cream`. Факты: доставка по Минску в пятницу, от 50 BYN бесплатно, дешевле — стоимость уточнит менеджер; самовывоз ул. Короля, 9; пн–пт 8:00–20:00, сб–вс 10:00–18:00.
|
||||||
- **<768:** фото мешка скрыто (рядом уже охладитель), секция начинается с H2, padding-top 48px. **768–1023:** фото полосой сверху. **≥1024:** фото абсолютно слева на 42% ширины экрана, контент в колонках 7–12 контейнера 1280. **390:** 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.
|
- **<768:** фото мешка скрыто (рядом уже охладитель), секция начинается с H2, padding-top 48px. **768–1023:** фото полосой сверху. **≥1024:** фото абсолютно слева на 42% ширины экрана, контент в колонках 7–12 контейнера 1280. **390:** 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 снизу.
|
- **1440:** фото кол. 1–5 на всю высоту секции (bleed влево до края экрана), справа кол. 7–12: H2, два блока рядом (по 3 колонки), CTA снизу.
|
||||||
|
- Строки доставки — поля с tag-подписью `muted-on-dark` (`site.delivery.delivery.labels`); акцент `cherry-on-dark` только на слове `accent` («бесплатно»).
|
||||||
|
- Под сеткой ассортимента — строка small `site.catalog.deliveryNote` ссылкой на `#delivery`.
|
||||||
- **Фото:** obV_LM0KjxY (Tina Guina, мешок на тёмном). `sizes="(min-width: 1024px) 40vw, 100vw"`, `object-position: center`, lazy. Alt: «Открытый мешок с кофейным зерном».
|
- **Фото:** 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`.
|
- **CTA:** CtaButton secondary on-dark, `interest: 'question'`, без comment (подпись CTAS.question). Акценты (ПТ, «бесплатно») — `cherry-on-dark`.
|
||||||
- Карты-iframe нет (производительность).
|
- Карты-iframe нет (производительность).
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ function splitName(name: string): [string, string] {
|
|||||||
function Row({ label, tall = false, wide = false, children }: { label: string; tall?: boolean; wide?: boolean; children: React.ReactNode }) {
|
function Row({ label, tall = false, wide = false, children }: { label: string; tall?: boolean; wide?: boolean; children: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`flex items-center gap-3 border-t border-ink ${tall ? 'min-h-[72px] py-3' : 'min-h-10 py-2'} ${
|
className={`flex items-center gap-3 border-t border-ink ${tall ? 'min-h-[72px] py-3 md:flex-1' : 'min-h-10 py-2'} ${
|
||||||
wide ? 'lg:first:border-t-0' : ''
|
wide ? 'lg:first:border-t-0' : ''
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<span className="tag w-[6.25rem] flex-none text-muted lg:w-[7.5rem]">{label}</span>
|
<span className="tag w-[6.25rem] flex-none text-muted lg:w-[7.5rem]">{label}</span>
|
||||||
<span className="min-w-0">{children}</span>
|
<span className="min-w-0 leading-[1.3]">{children}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -49,10 +49,10 @@ export function Label({ item, roastDate, wide = false }: { item: Item; roastDate
|
|||||||
);
|
);
|
||||||
|
|
||||||
const fields = (
|
const fields = (
|
||||||
<div className={wide ? 'lg:px-8' : 'mt-auto'}>
|
<div className={wide ? 'lg:px-8' : 'flex flex-col'}>
|
||||||
{item.notes.length > 0 && (
|
{item.notes.length > 0 && (
|
||||||
<Row label={label.notes} wide={wide}>
|
<Row label={label.notes} wide={wide}>
|
||||||
<span className="tag text-ink">{item.notes.map(keep).join(' • ')}</span>
|
<span className="tag text-ink">{item.notes.map(keep).join(' •\u00A0')}</span>
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
{item.spec && (
|
{item.spec && (
|
||||||
@@ -95,7 +95,7 @@ export function Label({ item, roastDate, wide = false }: { item: Item; roastDate
|
|||||||
className={`relative border border-ink bg-kraft p-5 lg:p-6 ${
|
className={`relative border border-ink bg-kraft p-5 lg:p-6 ${
|
||||||
wide
|
wide
|
||||||
? 'flex flex-col lg:grid lg:grid-cols-[1fr_1.4fr_1fr] lg:items-center lg:py-8 lg:[&>*+*]:border-l lg:[&>*+*]:border-ink'
|
? 'flex flex-col lg:grid lg:grid-cols-[1fr_1.4fr_1fr] lg:items-center lg:py-8 lg:[&>*+*]:border-l lg:[&>*+*]:border-ink'
|
||||||
: 'flex flex-col'
|
: 'flex flex-col md:row-span-3 md:grid md:grid-rows-subgrid md:gap-0'
|
||||||
}`}
|
}`}
|
||||||
aria-labelledby={`label-${item.id}`}
|
aria-labelledby={`label-${item.id}`}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export function PriceTag({
|
|||||||
unit,
|
unit,
|
||||||
large = false,
|
large = false,
|
||||||
bareOnWide = false,
|
bareOnWide = false,
|
||||||
|
bare = false,
|
||||||
}: {
|
}: {
|
||||||
size?: string;
|
size?: string;
|
||||||
price: number;
|
price: number;
|
||||||
@@ -12,10 +13,12 @@ export function PriceTag({
|
|||||||
large?: boolean;
|
large?: boolean;
|
||||||
/** без верхней линейки на ≥1024 — у горизонтальной этикетки зоны делят вертикальные линейки */
|
/** без верхней линейки на ≥1024 — у горизонтальной этикетки зоны делят вертикальные линейки */
|
||||||
bareOnWide?: boolean;
|
bareOnWide?: boolean;
|
||||||
|
/** без верхней линейки совсем — когда цена стоит под своими полями (подписка) */
|
||||||
|
bare?: boolean;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<p className={`flex items-baseline justify-between gap-4 border-t border-ink pt-3 ${bareOnWide ? 'lg:border-t-0 lg:pt-0' : ''}`}>
|
<p className={`flex items-baseline justify-between gap-4 ${bare ? '' : 'border-t border-ink pt-3'} ${bareOnWide ? 'lg:border-t-0 lg:pt-0' : ''}`}>
|
||||||
{size ? <span className="mono text-base font-medium whitespace-nowrap">{size}</span> : <span />}
|
{size ? <Size value={size} /> : <span />}
|
||||||
<span className="whitespace-nowrap">
|
<span className="whitespace-nowrap">
|
||||||
<span className={`mono ${large ? 'text-[clamp(2.25rem,1.6rem+2vw,3rem)]' : 'text-price'} font-semibold tracking-[-0.02em]`}>
|
<span className={`mono ${large ? 'text-[clamp(2.25rem,1.6rem+2vw,3rem)]' : 'text-price'} font-semibold tracking-[-0.02em]`}>
|
||||||
{price}
|
{price}
|
||||||
@@ -26,3 +29,20 @@ export function PriceTag({
|
|||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** «250 г»: число и единица отдельными span — моноширинный пробел слишком широкий, отступ задаём сами. */
|
||||||
|
function Size({ value }: { value: string }) {
|
||||||
|
const m = /^(\S+)\s+(.+)$/.exec(value);
|
||||||
|
return (
|
||||||
|
<span className="mono text-base font-medium whitespace-nowrap">
|
||||||
|
{m ? (
|
||||||
|
<>
|
||||||
|
<span>{m[1]}</span>
|
||||||
|
<span className="ml-[0.25em]">{m[2]}</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
value
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export function About() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="lg:col-span-5 lg:col-start-8">
|
<div className="lg:col-span-5 lg:col-start-8">
|
||||||
<h2 id="about-title" className="text-headline font-extrabold">
|
<h2 id="about-title" className="text-headline font-extrabold">
|
||||||
{about.title}
|
{typo(about.title)}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="mt-5 flex max-w-[62ch] flex-col gap-4">
|
<div className="mt-5 flex max-w-[62ch] flex-col gap-4">
|
||||||
{about.text.map((p) => (
|
{about.text.map((p) => (
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ export function Catalog({ roastDate }: { roastDate: string }) {
|
|||||||
<section id="coffee" aria-labelledby="coffee-title" className="page-x section-y">
|
<section id="coffee" aria-labelledby="coffee-title" className="page-x section-y">
|
||||||
<div className="container-page border-t-2 border-ink pt-10 lg:pt-14">
|
<div className="container-page border-t-2 border-ink pt-10 lg:pt-14">
|
||||||
<h2 id="coffee-title" className="text-headline font-extrabold">
|
<h2 id="coffee-title" className="text-headline font-extrabold">
|
||||||
{catalog.title}
|
{typo(catalog.title)}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-4 max-w-[46ch] text-lead">{typo(catalog.subtitle)}</p>
|
<p className="mt-4 max-w-[46ch] text-lead">{typo(catalog.subtitle)}</p>
|
||||||
<div className="mt-10 grid gap-4 md:grid-cols-2 lg:mt-16 lg:grid-cols-3 lg:gap-6">
|
<div className="mt-10 grid gap-4 md:grid-cols-2 lg:mt-16 lg:grid-cols-3 lg:gap-x-6 lg:gap-y-6">
|
||||||
{packs.map((item) => (
|
{packs.map((item) => (
|
||||||
<Label key={item.id} item={item} roastDate={roastDate} />
|
<Label key={item.id} item={item} roastDate={roastDate} />
|
||||||
))}
|
))}
|
||||||
@@ -23,6 +23,11 @@ export function Catalog({ roastDate }: { roastDate: string }) {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<p className="mt-6 text-small">
|
||||||
|
<a href={catalog.deliveryNote.href} className="inline-flex min-h-11 items-center underline">
|
||||||
|
{typo(catalog.deliveryNote.text)}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -24,21 +24,24 @@ export function Delivery() {
|
|||||||
<div className="container-page lg:grid lg:grid-cols-12 lg:gap-x-6">
|
<div className="container-page lg:grid lg:grid-cols-12 lg:gap-x-6">
|
||||||
<div className="lg:col-span-6 lg:col-start-7">
|
<div className="lg:col-span-6 lg:col-start-7">
|
||||||
<h2 id="delivery-title" className="text-headline font-extrabold">
|
<h2 id="delivery-title" className="text-headline font-extrabold">
|
||||||
{delivery.title}
|
{typo(delivery.title)}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="mt-10 grid gap-10 md:grid-cols-2 md:gap-8 lg:mt-14">
|
<div className="mt-10 grid gap-10 md:grid-cols-2 md:gap-8 lg:mt-14">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-title font-extrabold">{delivery.delivery.title}</h3>
|
<h3 className="text-title font-extrabold">{typo(delivery.delivery.title)}</h3>
|
||||||
<ul className="mt-5">
|
<dl className="mt-5">
|
||||||
{delivery.delivery.lines.map((line) => (
|
{delivery.delivery.lines.map((line, i) => (
|
||||||
<li key={line} className="border-t border-muted-on-dark py-3 first:border-t-0 first:pt-0">
|
<div key={line} className="flex gap-3 border-t border-muted-on-dark py-3">
|
||||||
{typo(line)}
|
<dt className="tag w-[6.5rem] flex-none pt-1 text-muted-on-dark">{delivery.delivery.labels[i]}</dt>
|
||||||
</li>
|
<dd className="min-w-0 leading-[1.4]">
|
||||||
|
<Accent text={typo(line)} word={delivery.delivery.accent} />
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-title font-extrabold">{pickup.title}</h3>
|
<h3 className="text-title font-extrabold">{typo(pickup.title)}</h3>
|
||||||
<p className="mt-5">{pickup.address}</p>
|
<p className="mt-5">{pickup.address}</p>
|
||||||
<dl className="mt-3">
|
<dl className="mt-3">
|
||||||
{pickup.hours.map((row) => (
|
{pickup.hours.map((row) => (
|
||||||
@@ -70,3 +73,16 @@ export function Delivery() {
|
|||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Выделить одно слово акцентом на тёмном. */
|
||||||
|
function Accent({ text, word }: { text: string; word: string }) {
|
||||||
|
const i = text.indexOf(word);
|
||||||
|
if (i === -1) return <>{text}</>;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{text.slice(0, i)}
|
||||||
|
<span className="text-cherry-on-dark">{word}</span>
|
||||||
|
{text.slice(i + word.length)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export function Freshness({ week }: { week: RoastWeek }) {
|
|||||||
<section id="fresh" aria-labelledby="fresh-title" className="page-x section-y lg:pb-0">
|
<section id="fresh" aria-labelledby="fresh-title" className="page-x section-y lg:pb-0">
|
||||||
<div className="container-page border-t-2 border-ink pt-10 lg:pt-14">
|
<div className="container-page border-t-2 border-ink pt-10 lg:pt-14">
|
||||||
<h2 id="fresh-title" className="text-headline font-extrabold">
|
<h2 id="fresh-title" className="text-headline font-extrabold">
|
||||||
{freshness.title}
|
{typo(freshness.title)}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-4 max-w-[46ch] text-lead">{typo(freshness.why)}</p>
|
<p className="mt-4 max-w-[46ch] text-lead">{typo(freshness.why)}</p>
|
||||||
<div className="mt-10 lg:mt-16">
|
<div className="mt-10 lg:mt-16">
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export function LeadSection() {
|
|||||||
<div className="container-page grid gap-10 lg:grid-cols-12 lg:gap-6">
|
<div className="container-page grid gap-10 lg:grid-cols-12 lg:gap-6">
|
||||||
<div className="lg:col-span-5">
|
<div className="lg:col-span-5">
|
||||||
<h2 id="lead-title" className="text-headline font-extrabold">
|
<h2 id="lead-title" className="text-headline font-extrabold">
|
||||||
{lead.title}
|
{typo(lead.title)}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-4 max-w-[36ch] text-lead">{typo(lead.subtitle)}</p>
|
<p className="mt-4 max-w-[36ch] text-lead">{typo(lead.subtitle)}</p>
|
||||||
<p className="mt-8 text-small text-muted">{lead.call}</p>
|
<p className="mt-8 text-small text-muted">{lead.call}</p>
|
||||||
|
|||||||
@@ -11,29 +11,29 @@ export function Subscription() {
|
|||||||
<div className="container-page grid gap-10 border-t-2 border-ink pt-10 lg:grid-cols-12 lg:gap-6 lg:pt-14">
|
<div className="container-page grid gap-10 border-t-2 border-ink pt-10 lg:grid-cols-12 lg:gap-6 lg:pt-14">
|
||||||
<div className="lg:col-span-5">
|
<div className="lg:col-span-5">
|
||||||
<h2 id="subscription-title" className="text-headline font-extrabold">
|
<h2 id="subscription-title" className="text-headline font-extrabold">
|
||||||
{sub.title}
|
{typo(sub.title)}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-4 max-w-[40ch] text-lead">{typo(sub.text)}</p>
|
<p className="mt-4 max-w-[40ch] text-lead">{typo(sub.text)}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="border border-ink bg-kraft p-5 lg:col-span-7 lg:grid lg:grid-cols-[1.2fr_1fr] lg:gap-10 lg:p-8">
|
<div className="border border-ink bg-kraft p-5 lg:col-span-7 lg:grid lg:grid-cols-[1.2fr_1fr] lg:gap-x-10 lg:p-8">
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
<Packs />
|
<Packs />
|
||||||
<dl>
|
<dl>
|
||||||
{sub.fields.map((field) => (
|
{sub.fields.map((field) => (
|
||||||
<div key={field.label} className="flex min-h-10 items-center gap-3 border-t border-ink py-2">
|
<div key={field.label} className="flex min-h-10 items-center gap-3 border-t border-ink py-2 last:border-b">
|
||||||
<dt className="tag w-[6.25rem] flex-none text-muted lg:w-[7.5rem]">{field.label}</dt>
|
<dt className="tag w-[6.25rem] flex-none text-muted lg:w-[7.5rem]">{field.label}</dt>
|
||||||
<dd className="tag text-ink">{typo(field.value)}</dd>
|
<dd className="tag text-ink">{typo(field.value)}</dd>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4 flex flex-col justify-end gap-6 lg:mt-0">
|
<div className="mt-6 flex flex-col justify-end gap-6 lg:mt-0">
|
||||||
<PriceTag price={sub.price} unit={sub.priceUnit} large />
|
<PriceTag price={sub.price} unit={sub.priceUnit} large bare />
|
||||||
<CtaButton interest={sub.cta.interest} comment={sub.cta.comment} className="btn btn-primary w-full">
|
<CtaButton interest={sub.cta.interest} comment={sub.cta.comment} className="btn btn-primary w-full">
|
||||||
{sub.cta.label}
|
{sub.cta.label}
|
||||||
</CtaButton>
|
</CtaButton>
|
||||||
<p className="-mt-2 text-small text-muted">{typo(sub.note)}</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p className="mt-4 text-small text-muted lg:col-span-2 lg:mt-6">{typo(sub.note)}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ export const site = {
|
|||||||
title: 'Что обжариваем',
|
title: 'Что обжариваем',
|
||||||
subtitle: 'Три сорта в пачках по 250 г и дрип-пакеты.',
|
subtitle: 'Три сорта в пачках по 250 г и дрип-пакеты.',
|
||||||
label: { brand: 'Кварта · Минск', notes: 'Ноты' },
|
label: { brand: 'Кварта · Минск', notes: 'Ноты' },
|
||||||
|
// строка под сеткой (итерация критика 2, нужна вычитка автора)
|
||||||
|
deliveryNote: { text: 'Доставка по Минску от 50 BYN — бесплатно', href: '#delivery' },
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: 'ethiopia',
|
id: 'ethiopia',
|
||||||
@@ -141,6 +143,9 @@ export const site = {
|
|||||||
'От 50 BYN бесплатно.',
|
'От 50 BYN бесплатно.',
|
||||||
'Если заказ меньше, стоимость доставки уточнит менеджер.',
|
'Если заказ меньше, стоимость доставки уточнит менеджер.',
|
||||||
],
|
],
|
||||||
|
// подписи полей к строкам выше и слово-акцент (итерация критика 2, нужна вычитка автора)
|
||||||
|
labels: ['Когда', 'От 50 BYN', 'Дешевле'],
|
||||||
|
accent: 'бесплатно',
|
||||||
},
|
},
|
||||||
pickup: {
|
pickup: {
|
||||||
title: 'Самовывоз',
|
title: 'Самовывоз',
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ describe('typo', () => {
|
|||||||
it('неразрывный дефис в фамилии', () => {
|
it('неразрывный дефис в фамилии', () => {
|
||||||
expect(typo('Scott Soltys-Curry')).toBe('Scott Soltys‑Curry');
|
expect(typo('Scott Soltys-Curry')).toBe('Scott Soltys‑Curry');
|
||||||
});
|
});
|
||||||
|
it('слеш не уходит на новую строку', () => {
|
||||||
|
expect(typo('Curry / Unsplash')).toBe(`Curry${N}/ Unsplash`);
|
||||||
|
});
|
||||||
it('keep склеивает фразу', () => {
|
it('keep склеивает фразу', () => {
|
||||||
expect(keep('красное яблоко')).toBe(`красное${N}яблоко`);
|
expect(keep('красное яблоко')).toBe(`красное${N}яблоко`);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Типографика для вывода: неразрывные пробелы после коротких слов и между числом и единицей,
|
// Типографика для вывода: неразрывные пробелы после коротких слов и между числом и единицей,
|
||||||
// неразрывный дефис в двойных латинских фамилиях. Смысл текста не меняется.
|
// неразрывный дефис в двойных латинских фамилиях, неразрывный пробел перед «/». Смысл текста не меняется.
|
||||||
const NBSP = ' ';
|
const NBSP = ' ';
|
||||||
const SHORT = 'а|в|во|и|к|ко|на|не|но|о|об|от|до|по|с|со|у|за|из|для|как|или';
|
const SHORT = 'а|в|во|и|к|ко|на|не|но|о|об|от|до|по|с|со|у|за|из|для|как|или';
|
||||||
const SHORT_RE = new RegExp(`(^|[\\s(«—–-])(${SHORT}) `, 'giu');
|
const SHORT_RE = new RegExp(`(^|[\\s(«—–-])(${SHORT}) `, 'giu');
|
||||||
@@ -10,7 +10,10 @@ export function typo(text: string): string {
|
|||||||
// дважды: «и в Минске» — после замены первого слова второе стоит уже после NBSP
|
// дважды: «и в Минске» — после замены первого слова второе стоит уже после NBSP
|
||||||
let out = text.replace(SHORT_RE, `$1$2${NBSP}`);
|
let out = text.replace(SHORT_RE, `$1$2${NBSP}`);
|
||||||
out = out.replace(new RegExp(`(${NBSP})(${SHORT}) `, 'giu'), `$1$2${NBSP}`);
|
out = out.replace(new RegExp(`(${NBSP})(${SHORT}) `, 'giu'), `$1$2${NBSP}`);
|
||||||
return out.replace(UNIT_RE, `$1${NBSP}$2`).replace(NAME_HYPHEN_RE, '$1‑$2');
|
return out
|
||||||
|
.replace(UNIT_RE, `$1${NBSP}$2`)
|
||||||
|
.replace(NAME_HYPHEN_RE, '$1\u2011$2')
|
||||||
|
.replace(/ \//g, `${NBSP}/`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Склеить фразу целиком, чтобы она не рвалась: «красное яблоко». */
|
/** Склеить фразу целиком, чтобы она не рвалась: «красное яблоко». */
|
||||||
|
|||||||
Reference in New Issue
Block a user