Blocks / Marketing
Hero
The top of a landing page: a headline, a supporting line, two actions and a product picture made of parts.
New: price alerts from your suppliers
Know what is on the shelf before service starts.
Larder counts stock as deliveries come in and dishes go out, so an independent kitchen orders what it needs and bins less of what it bought.
Free for one kitchen for 30 days. No card, and your stock counts stay yours if you leave.
Tonight, Friday dinner
Counted at 17:40 by Ines
On the shelf£4,820
Waste this week3.1%Down 1.3from 4.4
Running low before Saturday
Delivery checked in
Hodmedod's, 14 lines, 1 short
Install
Writes the block to components/blocks/hero.tsx and its .css, and every part it uses to components/equip/, with the parts those are built from.
npx https://equip.studio311.app/equip.tgz add block:heroBuilt from 9 parts
Notes
- The product picture is one role=img with a sentence for a label; the parts inside it are inert, so nothing in it takes focus.
- The headline is the page's h1; use it once per page.
- The calls to action are Buttons given an href, so they are real links that look like buttons.
Source
components/blocks/hero.tsx87 lines
import { Badge } from "@/components/equip/badge";
import { BarChart } from "@/components/equip/bar-chart";
import { Button } from "@/components/equip/button";
import { Card, CardBody, CardHeader } from "@/components/equip/card";
import { Heading } from "@/components/equip/heading";
import { Icon } from "@/components/equip/icon";
import { Stat } from "@/components/equip/stat";
import { StatGroup } from "@/components/equip/stat-group";
import { Text } from "@/components/equip/text";
import "./hero.css";
/*
* The top of a landing page: one promise, one line that backs it up, two ways in,
* and a picture of the product built from the product's own parts.
*/
export default function Hero() {
return (
<section className="blk-hero" aria-labelledby="hero-title">
<div className="blk-hero-inner">
<div className="blk-hero-copy">
<Badge tone="ok" dot>
New: price alerts from your suppliers
</Badge>
<Heading level={1} id="hero-title" className="blk-hero-title">
Know what is on the shelf before service starts.
</Heading>
<Text size="l" tone="quiet" className="blk-hero-lede">
Larder counts stock as deliveries come in and dishes go out, so an independent kitchen orders what it
needs and bins less of what it bought.
</Text>
<div className="blk-hero-actions">
<Button variant="accent" size="l" href="#start">
Start a free kitchen
</Button>
<Button variant="outline" size="l" href="#tour" lead={<Icon name="play" />}>
Watch the tour
</Button>
</div>
<Text size="s" tone="faint">
Free for one kitchen for 30 days. No card, and your stock counts stay yours if you leave.
</Text>
</div>
<div className="blk-hero-visual" role="img" aria-label="Larder on a Friday: stock worth £4,820, waste down to 3.1%, butter running out tonight">
<div className="blk-hero-stage" inert>
<Card tone="raised" className="blk-hero-panel">
<CardHeader title="Tonight, Friday dinner" description="Counted at 17:40 by Ines" />
<CardBody>
<StatGroup size="s" label="Stock tonight">
<Stat label="On the shelf" value={4820} format={{ style: "currency", currency: "GBP", maximumFractionDigits: 0 }} locale="en-GB" />
<Stat label="Waste this week" value={3.1} unit="%" previous={4.4} change="absolute" goodWhen="down" locale="en-GB" />
</StatGroup>
<div className="blk-hero-low">
<Text size="s" weight="strong">Running low before Saturday</Text>
<BarChart
label="Days of stock left"
orientation="horizontal"
locale="en-GB"
format={{ style: "unit", unit: "day", maximumFractionDigits: 1 }}
data={[
{ label: "Cultured butter", value: 0.6, tone: "danger" },
{ label: "Banana shallots", value: 1.2, tone: "warn" },
{ label: "Double cream", value: 1.5, tone: "warn" },
]}
/>
</div>
</CardBody>
</Card>
<Card tone="raised" padding="s" className="blk-hero-note">
<div className="blk-hero-note-row">
<span className="blk-hero-note-icon">
<Icon name="truck" />
</span>
<div>
<Text size="s" weight="strong">Delivery checked in</Text>
<Text size="s" tone="quiet">Hodmedod's, 14 lines, 1 short</Text>
</div>
<Badge tone="warn">Short</Badge>
</div>
</Card>
</div>
</div>
</div>
</section>
);
}components/blocks/hero.css131 lines
@layer blocks {
.blk-hero {
container: blk-hero / inline-size;
position: relative;
overflow: clip;
background: var(--eq-bg);
color: var(--eq-ink);
}
.blk-hero-inner {
display: grid;
/* minmax(0, ...) so a long unbreakable line (a nowrap button) cannot widen the column past the hero. */
grid-template-columns: minmax(0, 1fr);
gap: calc(var(--eq-space-6) * 1.5);
max-width: 76rem;
margin-inline: auto;
padding: calc(var(--eq-space-6) * 1.5) var(--eq-space-4);
}
.blk-hero-copy {
display: grid;
grid-template-columns: minmax(0, 1fr);
justify-items: start;
gap: var(--eq-space-4);
max-width: 36rem;
}
.blk-hero-copy > * {
margin: 0;
}
.blk-hero-title {
font-size: clamp(2rem, 7cqi, 3.5rem);
line-height: 1.04;
letter-spacing: -0.025em;
text-wrap: balance;
}
.blk-hero-lede {
max-width: 34rem;
text-wrap: pretty;
}
.blk-hero-actions {
display: flex;
flex-wrap: wrap;
gap: var(--eq-space-2);
padding-block: var(--eq-space-2);
}
/* The product picture: a counted shelf, with a delivery note pinned over its corner. */
.blk-hero-visual {
position: relative;
padding: var(--eq-space-5) var(--eq-space-3);
border-radius: var(--eq-radius-l);
background:
radial-gradient(circle at 1px 1px, var(--eq-line-strong) 1px, transparent 0) 0 0 / 18px 18px,
color-mix(in oklab, var(--eq-surface) 70%, var(--eq-bg));
border: 1px solid var(--eq-line);
}
.blk-hero-stage {
max-width: 30rem;
margin-inline: auto;
}
.blk-hero-panel {
max-width: 30rem;
margin-inline: auto;
}
.blk-hero-low {
display: grid;
gap: var(--eq-space-2);
margin-top: var(--eq-space-4);
padding-top: var(--eq-space-3);
border-top: 1px solid var(--eq-line);
}
.blk-hero-low > p {
margin: 0;
}
/* Narrow: the note sits under the panel, just catching its edge, so it never covers the bars. */
.blk-hero-note {
position: relative;
width: min(19rem, calc(100% - var(--eq-space-5)));
margin: calc(var(--eq-space-3) * -1) 0 0 auto;
}
.blk-hero-note-row {
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
align-items: center;
gap: var(--eq-space-3);
}
.blk-hero-note-row p {
margin: 0;
}
.blk-hero-note-icon {
display: grid;
place-items: center;
width: var(--eq-h-m);
height: var(--eq-h-m);
border-radius: var(--eq-radius-m);
background: var(--eq-tint-strong);
}
@container blk-hero (width >= 56rem) {
.blk-hero-inner {
grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
align-items: center;
padding-block: calc(var(--eq-space-6) * 2.5);
}
.blk-hero-visual {
padding: calc(var(--eq-space-6) * 1.25) var(--eq-space-6) calc(var(--eq-space-6) * 2.5);
}
/* Wide: there is room below the panel, so the note is pinned over its corner. */
.blk-hero-note {
position: absolute;
inset-inline-start: calc(var(--eq-space-6) * -0.75);
bottom: var(--eq-space-5);
width: min(19rem, calc(100% - var(--eq-space-6)));
margin: 0;
}
}
}