Blocks / Marketing
Call to action band
A closing call to action: a big headline, one line, two actions and a row of supplier delivery tickets.
Start with tomorrow's delivery.
Natoora06:40
12 lines checked
All inSigned in by Sam W.
Neal's Yard Dairy07:15
6 lines checked
Butter 2kg shortSigned in by Sam W.
Flock & Herd08:05
9 lines checked
All inSigned in by Tom O.
Shellseekers09:30
4 lines checked
All inSigned in by Tom O.
Wildfarmed10:10
3 lines checked
All inSigned in by Kasia N.
Install
Writes the block to components/blocks/cta-band.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:cta-bandBuilt from 4 parts
Notes
- The ticket row is one role=img with a sentence for a label; the tickets inside are inert, so nothing in it takes focus.
- On a narrow container the tickets run off the panel's edge under a fade; from 60rem they share the row.
- The actions are link Buttons with fragment hrefs; point them at your sign-up and booking pages.
Source
components/blocks/cta-band.tsx79 lines
import { Badge } from "@/components/equip/badge";
import { Button } from "@/components/equip/button";
import { Heading } from "@/components/equip/heading";
import { Text } from "@/components/equip/text";
import "./cta-band.css";
const tickets = [
{ supplier: "Natoora", time: "06:40", lines: "12 lines checked", status: "All in", tone: "ok", by: "Sam W." },
{ supplier: "Neal's Yard Dairy", time: "07:15", lines: "6 lines checked", status: "Butter 2kg short", tone: "warn", by: "Sam W." },
{ supplier: "Flock & Herd", time: "08:05", lines: "9 lines checked", status: "All in", tone: "ok", by: "Tom O." },
{ supplier: "Shellseekers", time: "09:30", lines: "4 lines checked", status: "All in", tone: "ok", by: "Tom O." },
{ supplier: "Wildfarmed", time: "10:10", lines: "3 lines checked", status: "All in", tone: "ok", by: "Kasia N." },
] as const;
/*
* The closing call to action of a landing page: one big line, two ways in, and a morning's
* deliveries pinned up like tickets on the pass.
*/
export default function CtaBand() {
return (
<section className="blk-cta-band" aria-labelledby="cta-band-title">
<div className="blk-cta-band-inner">
<div className="blk-cta-band-panel">
<div className="blk-cta-band-copy">
<Heading level={2} id="cta-band-title" className="blk-cta-band-title">
Start with tomorrow's delivery.
</Heading>
<div className="blk-cta-band-side">
<Text size="l" tone="quiet" className="blk-cta-band-line">
Import your stock sheet tonight, connect the till, and check the 6am drop against what you ordered.
</Text>
<div className="blk-cta-band-actions">
<Button href="#start" variant="accent" size="l">
Start a free kitchen
</Button>
<Button href="#walkthrough" variant="outline" size="l">
Book a walkthrough
</Button>
</div>
<Text size="s" tone="faint">
Free for 30 days, no card. Bring your spreadsheet and we will tidy it.
</Text>
</div>
</div>
<div
className="blk-cta-band-tickets"
role="img"
aria-label="A Tuesday morning's five deliveries checked in, from Natoora at 06:40 to Wildfarmed at 10:10, with Neal's Yard Dairy 2kg short on butter"
>
<div className="blk-cta-band-row" inert>
{tickets.map((ticket) => (
<div key={ticket.supplier} className="blk-cta-band-ticket">
<div className="blk-cta-band-ticket-head">
<Text as="span" size="s" weight="strong">
{ticket.supplier}
</Text>
<span className="blk-cta-band-ticket-time">{ticket.time}</span>
</div>
<div className="blk-cta-band-ticket-body">
<Text size="s" tone="quiet">
{ticket.lines}
</Text>
<Badge tone={ticket.tone} dot>
{ticket.status}
</Badge>
</div>
<Text size="s" tone="faint" className="blk-cta-band-ticket-sign">
Signed in by {ticket.by}
</Text>
</div>
))}
</div>
</div>
</div>
</div>
</section>
);
}components/blocks/cta-band.css155 lines
@layer blocks {
.blk-cta-band {
container: blk-cta-band / inline-size;
background: var(--eq-bg);
color: var(--eq-ink);
}
.blk-cta-band-inner {
max-width: 76rem;
margin-inline: auto;
padding: calc(var(--eq-space-6) * 1.5) var(--eq-space-4);
}
.blk-cta-band-panel {
overflow: clip;
border: 1px solid var(--eq-line);
border-radius: var(--eq-radius-l);
background: var(--eq-surface);
}
.blk-cta-band-copy {
display: grid;
gap: var(--eq-space-5);
padding: calc(var(--eq-space-6) * 1.25) var(--eq-space-4) var(--eq-space-6);
}
.blk-cta-band-copy :is(h2, p) {
margin: 0;
}
.blk-cta-band-title {
max-width: 14ch;
font-size: clamp(2.25rem, 9cqi, 4.5rem);
line-height: 0.98;
letter-spacing: -0.035em;
text-wrap: balance;
}
.blk-cta-band-side {
display: grid;
justify-items: start;
gap: var(--eq-space-4);
max-width: 30rem;
}
.blk-cta-band-line {
text-wrap: pretty;
}
.blk-cta-band-actions {
display: flex;
flex-wrap: wrap;
gap: var(--eq-space-2);
}
/* The pass: a morning of delivery tickets pinned in a row, running off the edge of the panel. */
.blk-cta-band-tickets {
border-top: 1px solid var(--eq-line);
background:
radial-gradient(circle at 1px 1px, var(--eq-line-strong) 1px, transparent 0) 0 0 / 18px 18px,
color-mix(in oklab, var(--eq-tint) 60%, var(--eq-surface));
}
.blk-cta-band-row {
display: flex;
gap: var(--eq-space-3);
padding: var(--eq-space-5) var(--eq-space-4) calc(var(--eq-space-5) + 4px);
overflow: clip;
mask-image: linear-gradient(to right, currentColor 78%, transparent);
}
.blk-cta-band-ticket {
flex: 0 0 11.5rem;
display: grid;
gap: var(--eq-space-2);
padding: var(--eq-space-3);
border: 1px dashed var(--eq-line-strong);
border-radius: var(--eq-radius-m);
background: var(--eq-raised);
rotate: -1.25deg;
}
.blk-cta-band-ticket:nth-child(even) {
rotate: 1deg;
translate: 0 4px;
}
.blk-cta-band-ticket:nth-child(3n) {
rotate: -0.5deg;
}
.blk-cta-band-ticket p {
margin: 0;
}
.blk-cta-band-ticket-head {
display: flex;
flex-wrap: wrap;
align-items: baseline;
justify-content: space-between;
gap: 0 var(--eq-space-2);
min-width: 0;
}
.blk-cta-band-ticket-time {
flex: none;
color: var(--eq-ink-3);
font-family: var(--eq-font-mono);
font-variation-settings: var(--eq-font-mono-settings);
font-size: var(--eq-text-xs);
}
/* The tear line: where the driver's copy comes off. */
.blk-cta-band-ticket-body {
display: grid;
justify-items: start;
gap: var(--eq-space-2);
padding-top: var(--eq-space-2);
border-top: 1px dashed var(--eq-line-strong);
}
.blk-cta-band-ticket-sign {
font-size: var(--eq-text-xs);
}
@container blk-cta-band (width >= 40rem) {
.blk-cta-band-inner {
padding-inline: var(--eq-space-5);
}
.blk-cta-band-copy,
.blk-cta-band-row {
padding-inline: var(--eq-space-6);
}
}
@container blk-cta-band (width >= 60rem) {
.blk-cta-band-copy {
grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
align-items: end;
gap: calc(var(--eq-space-6) * 1.5);
padding-block: calc(var(--eq-space-6) * 2) calc(var(--eq-space-6) * 1.25);
}
/* Wide enough for the whole morning: the tickets share the row and the fade goes. */
.blk-cta-band-row {
mask-image: none;
}
.blk-cta-band-ticket {
flex: 1 1 0;
min-width: 0;
}
}
}