See late loads before customers call, with depot and driver status on one screen.
homepage · html, react
Homepage
Copy this page into your project. Recolour it with tokens. It is yours from that moment.
Fleet operations
Keep every route on time from depot to door.
Live ETAs, driver handoffs and depot boards in one place your ops team already trusts. No second kit. No redesign before the first customer sees it.
Pass jobs between shifts without lost notes or duplicate calls.
Flag bay delays and capacity risks before the afternoon rush.
Trusted on the road by
One board
Depot, driver and customer see the same status.
Recolour the brand; the layout stays finished. Copy this page for HTML, React or Django on the same bw-* contract.
18%
Fewer late arrivals
6 min
Average handoff time
1 board
Shared across every craft
Customer proof
We stopped rebuilding the ops screen every quarter. The defaults were already the product.
Ready when the next depot goes live.
Copy this page. Change the brand tokens. Ship.
Paste into a static page. Link @bwui/css (and @bwui/js for behaviours).
<!DOCTYPE html>
<html lang="en-GB" data-bw-brand="northline">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Northline: fleet operations that stay on time</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Source+Sans+3:wght@400;500;600;700&family=Source+Serif+4:opsz,wght@8..60,500;8..60,650;8..60,700&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="/static/product_templates/assets/brickwork.css" />
<script defer src="/static/product_templates/assets/behaviours.js"></script>
<style>
/* Northline brand pack (fictional): cool paper, teal accent, display serif. */
:root,
[data-bw-brand="northline"] {
--bw-color-surface: oklch(0.985 0.004 240);
--bw-color-fg: oklch(0.26 0.02 250);
--bw-color-border: oklch(0.90 0.01 240);
--bw-color-accent: oklch(0.42 0.05 230);
--bw-color-danger: oklch(0.55 0.18 25);
--bw-color-success: oklch(0.56 0.13 155);
--bw-color-warning: oklch(0.70 0.14 75);
--bw-color-fg-on-accent: oklch(0.99 0 0);
--bw-font-family-sans: "Source Sans 3", system-ui, sans-serif;
--bw-font-family-display: "Source Serif 4", Georgia, serif;
--nl-ink: #0c1c24;
--nl-deep: #1b3a4b;
--nl-mist: #2f5a6c;
--nl-paper: #f4f6f7;
}
body {
margin: 0;
font-family: var(--bw-font-family-sans);
background: var(--bw-color-surface);
color: var(--bw-color-fg);
}
.nl-header {
display: flex;
align-items: center;
gap: var(--bw-space-5);
padding: var(--bw-space-4) var(--bw-space-6);
border-bottom: 1px solid var(--bw-color-border);
background: color-mix(in oklab, var(--bw-color-surface) 94%, white);
}
.nl-header__brand {
display: inline-flex;
align-items: center;
gap: var(--bw-space-2);
margin-right: auto;
font-weight: 700;
letter-spacing: -0.03em;
color: var(--bw-color-fg);
text-decoration: none;
}
.nl-header__mark {
width: 1.125rem;
height: 1.125rem;
border-radius: 5px;
background: var(--nl-deep);
}
.nl-header__nav {
display: flex;
align-items: center;
gap: var(--bw-space-5);
}
.nl-header__nav a {
color: var(--bw-color-fg-muted);
text-decoration: none;
font-size: var(--bw-font-size-sm);
font-weight: 500;
}
.nl-header__nav a:hover {
color: var(--bw-color-fg);
}
.nl-header__cta {
display: inline-flex;
align-items: center;
min-height: 2.5rem;
padding: 0 var(--bw-space-4);
border-radius: var(--bw-radius-md);
background: var(--nl-deep);
color: #fff;
font-size: var(--bw-font-size-sm);
font-weight: 600;
text-decoration: none;
}
/* Full-bleed hero: same atmosphere Home sells in the fold. */
.nl-hero {
position: relative;
display: grid;
gap: var(--bw-space-10);
padding: clamp(3rem, 8vw, 6rem) clamp(1.25rem, 4vw, 3rem);
color: var(--nl-paper);
background:
linear-gradient(115deg, rgba(12, 28, 36, 0.55) 0%, rgba(12, 28, 36, 0.15) 48%, transparent 72%),
linear-gradient(165deg, var(--nl-ink) 0%, var(--nl-deep) 52%, var(--nl-mist) 100%);
overflow: hidden;
}
.nl-hero::after {
content: "";
position: absolute;
inset: auto -8% -40% 35%;
height: 140%;
background:
radial-gradient(ellipse at 40% 40%, rgba(255, 255, 255, 0.14) 0%, transparent 55%),
radial-gradient(ellipse at 70% 60%, color-mix(in oklab, #7eb8c9 40%, transparent) 0%, transparent 50%);
pointer-events: none;
}
@media (min-width: 64rem) {
.nl-hero {
grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
align-items: center;
}
}
.nl-hero__copy,
.nl-hero__media {
position: relative;
z-index: 1;
}
.nl-hero__eyebrow {
margin: 0 0 var(--bw-space-3);
font-size: var(--bw-font-size-xs);
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: rgba(244, 246, 247, 0.72);
}
.nl-hero__heading {
margin: 0 0 var(--bw-space-4);
font-family: var(--bw-font-family-display);
font-weight: 650;
font-size: clamp(2.25rem, 5vw, 3.75rem);
letter-spacing: -0.04em;
line-height: 1.05;
max-width: 14ch;
color: var(--nl-paper);
}
.nl-hero__lede {
margin: 0 0 var(--bw-space-6);
font-size: clamp(1rem, 1.4vw, 1.125rem);
line-height: 1.45;
color: rgba(244, 246, 247, 0.78);
max-width: 38ch;
}
.nl-hero__actions {
display: flex;
flex-wrap: wrap;
gap: var(--bw-space-3);
margin-bottom: var(--bw-space-4);
}
.nl-btn {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 2.75rem;
padding: 0 var(--bw-space-5);
border-radius: var(--bw-radius-md);
border: 1px solid transparent;
font-size: var(--bw-font-size-sm);
font-weight: 600;
text-decoration: none;
}
.nl-btn--solid {
background: var(--nl-paper);
color: var(--nl-ink);
}
.nl-btn--ghost {
background: transparent;
color: var(--nl-paper);
border-color: rgba(244, 246, 247, 0.35);
}
.nl-hero__meta {
font-size: var(--bw-font-size-sm);
color: rgba(244, 246, 247, 0.62);
}
.nl-chrome {
border-radius: 1rem;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, 0.12);
background: color-mix(in oklab, var(--nl-ink) 70%, black);
box-shadow:
0 1.5rem 3rem rgba(0, 0, 0, 0.28),
0 0 0 1px rgba(255, 255, 255, 0.04);
}
.nl-chrome__bar {
display: flex;
align-items: center;
gap: 0.4rem;
padding: 0.75rem 1rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
background: rgba(255, 255, 255, 0.03);
}
.nl-chrome__dot {
width: 0.55rem;
height: 0.55rem;
border-radius: 999px;
background: rgba(244, 246, 247, 0.28);
}
.nl-chrome__title {
margin: 0 0 0 0.5rem;
font-size: 0.75rem;
font-weight: 500;
color: rgba(244, 246, 247, 0.7);
}
.nl-chrome__stage {
padding: 1rem;
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.03), transparent 40%),
#132833;
}
.nl-chrome__stage svg {
display: block;
width: 100%;
height: auto;
}
.nl-bands {
display: grid;
grid-template-columns: 1.25fr 1fr 1fr;
border-bottom: 1px solid var(--bw-color-border);
background: var(--bw-color-surface);
}
.nl-band {
padding: var(--bw-space-6) var(--bw-space-5);
border-right: 1px solid var(--bw-color-border);
}
.nl-band:last-child {
border-right: 0;
}
.nl-band--lead {
background: color-mix(in oklab, var(--bw-color-surface) 88%, var(--nl-deep));
}
.nl-band strong {
display: block;
margin-bottom: var(--bw-space-2);
font-size: 1rem;
letter-spacing: -0.02em;
}
.nl-band p {
margin: 0;
color: var(--bw-color-fg-muted);
line-height: 1.45;
font-size: var(--bw-font-size-sm);
}
.nl-trust {
padding: var(--bw-space-10) var(--bw-space-6);
text-align: center;
}
.nl-trust__label {
margin: 0 0 var(--bw-space-5);
font-size: var(--bw-font-size-xs);
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--bw-color-fg-muted);
}
.nl-trust__logos {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: var(--bw-space-8);
list-style: none;
margin: 0;
padding: 0;
}
.nl-trust__logos img {
height: 1.75rem;
width: auto;
opacity: 0.72;
filter: grayscale(1);
}
.nl-depth {
display: grid;
gap: var(--bw-space-8);
padding: clamp(3rem, 7vw, 5rem) clamp(1.25rem, 4vw, 3rem);
background: color-mix(in oklab, var(--bw-color-surface) 92%, var(--nl-deep));
}
@media (min-width: 64rem) {
.nl-depth {
grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
align-items: center;
}
}
.nl-depth__eyebrow {
margin: 0 0 var(--bw-space-2);
font-size: var(--bw-font-size-xs);
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--nl-deep);
}
.nl-depth__heading {
margin: 0 0 var(--bw-space-4);
font-family: var(--bw-font-family-display);
font-size: clamp(1.75rem, 3vw, 2.5rem);
letter-spacing: -0.03em;
line-height: 1.1;
max-width: 16ch;
}
.nl-depth__body {
margin: 0;
max-width: 42ch;
color: var(--bw-color-fg-muted);
line-height: 1.5;
}
.nl-depth__panel {
border-radius: 1rem;
border: 1px solid var(--bw-color-border);
background: var(--bw-color-surface);
padding: var(--bw-space-5);
box-shadow: 0 1rem 2.5rem color-mix(in oklab, var(--nl-ink) 8%, transparent);
}
.nl-depth__panel svg {
display: block;
width: 100%;
height: auto;
}
.nl-stats {
display: grid;
gap: var(--bw-space-6);
padding: clamp(3rem, 6vw, 4.5rem) clamp(1.25rem, 4vw, 3rem);
border-block: 1px solid var(--bw-color-border);
}
@media (min-width: 48rem) {
.nl-stats {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
.nl-stat__value {
margin: 0 0 var(--bw-space-2);
font-family: var(--bw-font-family-display);
font-size: clamp(2rem, 4vw, 3rem);
letter-spacing: -0.04em;
line-height: 1;
color: var(--nl-deep);
}
.nl-stat__label {
margin: 0;
color: var(--bw-color-fg-muted);
}
.nl-quote {
padding: clamp(3rem, 7vw, 5rem) clamp(1.25rem, 4vw, 3rem);
max-width: 48rem;
}
.nl-quote blockquote {
margin: 0 0 var(--bw-space-5);
font-family: var(--bw-font-family-display);
font-size: clamp(1.5rem, 2.6vw, 2rem);
letter-spacing: -0.03em;
line-height: 1.25;
}
.nl-quote figcaption {
color: var(--bw-color-fg-muted);
font-size: var(--bw-font-size-sm);
}
.nl-quote strong {
color: var(--bw-color-fg);
}
.nl-close {
padding: clamp(3rem, 7vw, 5rem) clamp(1.25rem, 4vw, 3rem);
background: var(--nl-ink);
color: var(--nl-paper);
}
.nl-close__inner {
display: flex;
flex-wrap: wrap;
gap: var(--bw-space-6);
align-items: end;
justify-content: space-between;
max-width: 72rem;
}
.nl-close h2 {
margin: 0 0 var(--bw-space-3);
font-family: var(--bw-font-family-display);
font-size: clamp(1.75rem, 3vw, 2.5rem);
letter-spacing: -0.03em;
max-width: 16ch;
}
.nl-close p {
margin: 0;
max-width: 40ch;
color: rgba(244, 246, 247, 0.72);
line-height: 1.45;
}
.nl-footer {
display: flex;
flex-wrap: wrap;
gap: var(--bw-space-4);
justify-content: space-between;
padding: var(--bw-space-6);
border-top: 1px solid var(--bw-color-border);
color: var(--bw-color-fg-muted);
font-size: var(--bw-font-size-sm);
}
.nl-footer a {
color: inherit;
}
@media (max-width: 48rem) {
.nl-header__nav {
display: none;
}
.nl-bands {
grid-template-columns: 1fr;
}
.nl-band {
border-right: 0;
border-bottom: 1px solid var(--bw-color-border);
}
.nl-band:last-child {
border-bottom: 0;
}
}
</style>
</head>
<body data-theme="light">
<a class="bw-skip-link" href="#main">Skip to content</a>
<header class="nl-header">
<a class="nl-header__brand" href="#">
<span class="nl-header__mark" aria-hidden="true"></span>
Northline
</a>
<nav class="nl-header__nav" aria-label="Primary">
<a href="#product">Product</a>
<a href="#proof">Customers</a>
<a href="/templates/pricing/full/">Pricing</a>
</nav>
<a class="nl-header__cta" href="/templates/contact/full/">Book a walkthrough</a>
</header>
<main id="main">
<section class="nl-hero" aria-labelledby="nl-hero-heading">
<div class="nl-hero__copy">
<p class="nl-hero__eyebrow">Fleet operations</p>
<h1 id="nl-hero-heading" class="nl-hero__heading">Keep every route on time from depot to door.</h1>
<p class="nl-hero__lede">
Live ETAs, driver handoffs and depot boards in one place your ops team already trusts.
No second kit. No redesign before the first customer sees it.
</p>
<div class="nl-hero__actions">
<a class="nl-btn nl-btn--solid" href="/templates/contact/full/">Start free trial</a>
<a class="nl-btn nl-btn--ghost" href="#product">See the product</a>
</div>
<p class="nl-hero__meta">No card for fourteen days · Cancel in one click</p>
</div>
<div class="nl-hero__media">
<div class="nl-chrome" aria-hidden="true">
<div class="nl-chrome__bar">
<span class="nl-chrome__dot"></span>
<span class="nl-chrome__dot"></span>
<span class="nl-chrome__dot"></span>
<p class="nl-chrome__title">Depot board · Mersey hub</p>
</div>
<div class="nl-chrome__stage">
<svg viewBox="0 0 520 300" role="presentation">
<rect width="520" height="300" fill="#0f222b" />
<rect x="28" y="28" width="180" height="16" rx="4" fill="#f4f6f7" opacity="0.88" />
<rect x="28" y="56" width="110" height="9" rx="3" fill="#f4f6f7" opacity="0.28" />
<rect x="28" y="92" width="464" height="52" rx="12" fill="#173441" stroke="rgba(244,246,247,0.08)" />
<circle cx="56" cy="118" r="10" fill="#7eb8c9" />
<rect x="78" y="106" width="168" height="11" rx="3" fill="#f4f6f7" opacity="0.78" />
<rect x="78" y="124" width="104" height="9" rx="3" fill="#f4f6f7" opacity="0.28" />
<rect x="380" y="108" width="88" height="28" rx="14" fill="#7eb8c9" opacity="0.22" />
<rect x="28" y="156" width="464" height="52" rx="12" fill="#173441" stroke="rgba(244,246,247,0.08)" />
<circle cx="56" cy="182" r="10" fill="#f4f6f7" opacity="0.22" />
<rect x="78" y="170" width="148" height="11" rx="3" fill="#f4f6f7" opacity="0.7" />
<rect x="78" y="188" width="128" height="9" rx="3" fill="#f4f6f7" opacity="0.24" />
<rect x="380" y="172" width="88" height="28" rx="14" fill="#f4f6f7" opacity="0.1" />
<rect x="28" y="220" width="464" height="52" rx="12" fill="#1b3a4b" />
<rect x="48" y="238" width="220" height="14" rx="4" fill="#f4f6f7" opacity="0.9" />
<rect x="380" y="236" width="88" height="28" rx="14" fill="#f4f6f7" />
</svg>
</div>
</div>
</div>
</section>
<section id="product" class="nl-bands" aria-label="Product highlights">
<article class="nl-band nl-band--lead">
<strong>Live route boards</strong>
<p>See late loads before customers call, with depot and driver status on one screen.</p>
</article>
<article class="nl-band">
<strong>Driver handoffs</strong>
<p>Pass jobs between shifts without lost notes or duplicate calls.</p>
</article>
<article class="nl-band">
<strong>Depot alerts</strong>
<p>Flag bay delays and capacity risks before the afternoon rush.</p>
</article>
</section>
<section class="nl-trust" aria-labelledby="trust-heading">
<p id="trust-heading" class="nl-trust__label">Trusted on the road by</p>
<ul class="nl-trust__logos">
<li><img src="/static/img/example-logos/cairn-logistics.svg" alt="Cairn Logistics" /></li>
<li><img src="/static/img/example-logos/alder-ledger.svg" alt="Alder Ledger" /></li>
<li><img src="/static/img/example-logos/bramble-studio.svg" alt="Bramble Studio" /></li>
<li><img src="/static/img/example-logos/dovetail-works.svg" alt="Dovetail Works" /></li>
</ul>
</section>
<section class="nl-depth" aria-labelledby="depth-heading">
<div>
<p class="nl-depth__eyebrow">One board</p>
<h2 id="depth-heading" class="nl-depth__heading">Depot, driver and customer see the same status.</h2>
<p class="nl-depth__body">
Recolour the brand; the layout stays finished. Copy this page for HTML, React or Django on the same bw-* contract.
</p>
</div>
<div class="nl-depth__panel" aria-hidden="true">
<svg viewBox="0 0 480 260" role="presentation">
<rect width="480" height="260" rx="12" fill="var(--bw-color-surface)" />
<rect x="24" y="24" width="140" height="12" rx="3" fill="var(--nl-deep)" opacity="0.85" />
<rect x="24" y="52" width="90" height="8" rx="2" fill="currentColor" opacity="0.18" />
<rect x="24" y="88" width="432" height="48" rx="10" fill="color-mix(in oklab, var(--nl-deep) 8%, white)" stroke="var(--bw-color-border)" />
<circle cx="52" cy="112" r="9" fill="var(--nl-deep)" />
<rect x="74" y="100" width="150" height="10" rx="2" fill="currentColor" opacity="0.35" />
<rect x="74" y="118" width="100" height="8" rx="2" fill="currentColor" opacity="0.16" />
<rect x="24" y="152" width="432" height="48" rx="10" fill="var(--bw-color-surface)" stroke="var(--bw-color-border)" />
<circle cx="52" cy="176" r="9" fill="currentColor" opacity="0.2" />
<rect x="74" y="164" width="130" height="10" rx="2" fill="currentColor" opacity="0.3" />
<rect x="24" y="216" width="432" height="24" rx="8" fill="color-mix(in oklab, var(--nl-deep) 14%, white)" />
</svg>
</div>
</section>
<section class="nl-stats" aria-label="Outcomes">
<div>
<p class="nl-stat__value">18%</p>
<p class="nl-stat__label">Fewer late arrivals</p>
</div>
<div>
<p class="nl-stat__value">6 min</p>
<p class="nl-stat__label">Average handoff time</p>
</div>
<div>
<p class="nl-stat__value">1 board</p>
<p class="nl-stat__label">Shared across every craft</p>
</div>
</section>
<section id="proof" class="nl-quote" aria-labelledby="quote-heading">
<h2 id="quote-heading" class="visually-hidden">Customer proof</h2>
<figure>
<blockquote>
We stopped rebuilding the ops screen every quarter. The defaults were already the product.
</blockquote>
<figcaption>
<strong>Priya Raman</strong> · Head of Operations, Harbour Freight
</figcaption>
</figure>
</section>
<section class="nl-close" aria-labelledby="close-heading">
<div class="nl-close__inner">
<div>
<h2 id="close-heading">Ready when the next depot goes live.</h2>
<p>Copy this page. Change the brand tokens. Ship.</p>
</div>
<a class="nl-btn nl-btn--solid" href="/templates/contact/full/">Start free trial</a>
</div>
</section>
</main>
<footer class="nl-footer">
<p>Northline gallery page. Copy and own.</p>
<p><a href="/templates/landing/full/">Landing</a> · <a href="/templates/contact/full/">Contact</a></p>
</footer>
</body>
</html>
Named export from @bwui/react. Do not invent other page components.
import "@bwui/css";
import { Homepage } from "@bwui/react";
export default function Page() {
return (
<Homepage
brand="Your brand"
title="Beautiful interfaces, out of the box"
description="Install Brickwork, copy this page, recolour it, own it."
/>
);
}