Jumbotron
The full-width opening statement of a page: one heading, one sentence, one action, and nothing else competing.
Also called Hero, Hero Banner, Splash, Page Header — in this system all of them are Jumbotron.
Everything below is the real component. Change the controls, tab through it, and turn on Inspector Mode to read any value off the screen.
Deploy to twenty-four regions in one command.
No build servers to manage, no YAML to write, and a rollback that finishes in eight seconds when it goes wrong.
One action, not two
A filled button and a text button read as a recommendation and an alternative. Two filled buttons read as two equally important decisions, which is no decision at all.
Large type needs a short measure
A 48px headline across a full-width container is unreadable. Twenty characters per line for the heading, fifty-five for the body.
As an empty state
The same anatomy at a smaller scale. A new user with no projects needs the same three things: what this is, why it matters, and one way in.
Left or centred
Centred suits a short heading and a single action. Left-aligned scans faster and is the only option once there is media beside the text.
Deploy to twenty-four regions in one command.
No build servers to manage, no YAML to write, and a rollback that finishes in eight seconds when it goes wrong.
Deploy to twenty-four regions in one command.
No build servers to manage, no YAML to write, and a rollback that finishes in eight seconds when it goes wrong.
Interaction states
Every state a user can put this component into, rendered side by side. If a state is missing here, it is missing in production too.
Every part, every measurement, and the reason it is that number.
Deploy to twenty-four regions in one command.
No build servers to manage, no YAML to write, and a rollback that finishes in eight seconds when it goes wrong.
Eyebrow, one heading, one sentence, one filled action and one text alternative — in that order, with nothing else competing.
- Vertical padding48px (32px compact)
The space is the emphasis. A hero with card-level padding reads as a card, and the reader treats it as one item among several.
- EyebrowBadge or overline
Optional, and it must earn its line. "New in 2026.3" is context; "Introducing" is a word that says nothing.
- Headingclamp(2rem, 5vw, 3.5rem)
Fluid, so it never wraps awkwardly between breakpoints. Leading tightens to about 1.06 and tracking goes negative — large type needs both.
- Heading measure~20 characters
Much shorter than body copy. A 48px headline across 1440px is unreadable exactly where readability matters most.
- Body17px, ~55 characters
One sentence. It expands the heading rather than repeating it, and it is the first thing to cut when the section feels crowded.
- ActionsOne filled, one text
Large size, 12px apart. Two filled buttons is two primaries, which is none.
- Media scrimGradient over the image
Contrast against an arbitrary photograph cannot be asserted. The scrim is what makes the headline survive an image swap.
Values are read live from the running stylesheet, so this table can never drift from the code. Click any value to copy it.
Color
| Token | Value | Used for |
|---|---|---|
| --ds-surface | — | Section background |
| --ds-fg | — | The heading |
| --ds-fg-secondary | — | The body sentence |
| --ds-accent | — | Ambient gradient and the primary action |
| --ds-accent-subtle | — | Eyebrow badge fill |
| --ds-surface-inset | — | A media panel beside the text |
Spacing
| Token | Value | Used for |
|---|---|---|
| --space-12 | Vertical padding | |
| --space-4 | Gap between heading, body and actions |
Radius
| Token | Value | Used for |
|---|---|---|
| --radius-2xl | Section corners when it is a contained block |
Typography
| Token | Value | Used for |
|---|---|---|
| --text-h1 | The heading | |
| tracking | Large type needs negative tracking |
Pick a size from this table. Do not invent a new one — a fourth height is how a design system starts dying.
| Size | Height | Label gap | Type | Max width | When to use |
|---|---|---|---|---|---|
| Compact | 32px padding | — | h2 heading | — | Empty states and section openers inside an application. |
| Default | 48px padding | — | h1 heading | — | The top of a landing page or a feature announcement. |
| Full-bleed | 64px+ padding | — | h1 heading | — | A dedicated landing page where the hero is the whole first screen. |
| Heading measure | — | — | — | 20ch | Much shorter than body copy. Large type wraps badly at any wider measure. |
| Body measure | — | — | — | 55ch | One sentence. Two is usually one too many. |
| Actions | 44px | 12px | — | — | Large buttons. One filled, one text. |
<h1 class="ds-hero__heading">…</h1>Our platform delivers unparalleled deployment velocity through a distributed edge network spanning twenty-four regions. Built by engineers who have operated systems at scale, it removes the operational burden of build servers, configuration management and rollback orchestration. Teams ship faster, with fewer incidents and lower total cost of ownership.
Not a checklist to run at the end. These are the requirements the component was built from.
Contrast
- Text over any image or gradient needs a scrim. Contrast against an arbitrary photograph is not something you can assert or test once.
- The eyebrow badge is content and owes 4.5:1, not the lower bar decoration gets.
- Large text may use 3:1 under AA, but a headline is the thing you most want read — hold it to 4.5:1 anyway.
- An ambient background gradient must not reduce the heading below its ratio at any viewport width, including the ones where it lands differently.
Keyboard
| Tab | Reaches the primary action first. It is first in the DOM as well as visually. |
| Tab | Then the secondary action. Decorative media is never focusable. |
Screen readers
- The heading is the first thing announced after the landmark. It has to work as a standalone sentence, because that is how it will be heard.
- The eyebrow is read before the heading. Keep it short, or every visitor hears "Introducing, comma, new in twenty twenty-six point three" before the message.
- Background media must be aria-hidden. A decorative video announced as an unlabelled region is pure noise at the top of the page.
Focus & touch
- The primary action is the first focusable element on the page after any skip link. Nothing decorative — no gradient, no background video — is ever in the tab order.
- Vertical padding halves on a phone — 48px becomes 24px, or the hero consumes the entire first screen and the content below is invisible. Actions stack full-width with the primary on top. Fluid heading sizing with clamp() is what stops a headline wrapping into five lines at 375px.
| Attribute | Applied to | Notes |
|---|---|---|
| <h1> | The heading | One per page, and it belongs here. A hero heading styled as a div leaves the page with nothing to jump to. |
| aria-hidden | Decorative gradients and background media | They carry no information and must not be announced. |
| alt | A meaningful hero image | If the image carries information the text does not, it needs alt text. If it is decoration, alt="". |
| <section> | The container | With aria-labelledby pointing at the heading, so the region has a name in the landmark list. |
Example usage
1import { Jumbotron } from '@/ui/Surface'23<Jumbotron4 eyebrow={<Badge tone="accent" dot>New in 2026.3</Badge>}5 heading="Deploy to twenty-four regions in one command."6 body="No build servers to manage, no YAML to write, and a rollback that finishes in eight seconds."7 action={<Button size="lg" endIcon={<ArrowRight />}>Start deploying</Button>}8 // One filled, one text. Two filled buttons is two primaries, which is none.9 secondaryAction={<Button size="lg" variant="text">Watch the demo</Button>}10/>1112// The heading is the top of the document outline — a real h1, not a styled div.13<h1 className="ds-hero__heading">{heading}</h1>1415// Over media, the scrim is not optional: contrast against an arbitrary16// photograph cannot be asserted, and the asset will be swapped eventually.17<div className="relative">18 <img src={hero} alt="" className="absolute inset-0 h-full w-full object-cover" />19 <div aria-hidden className="absolute inset-0 bg-gradient-to-r from-black/70 to-black/20" />20 <div className="relative">{content}</div>21</div>2223// Fluid type stops the headline wrapping into five lines at 375px.24// h1 { font-size: clamp(2rem, 5vw, 3.5rem) }Framework-free HTML
<section class="ds-hero" aria-labelledby="hero-heading">
<!-- Decorative: carries no information, never announced. -->
<span class="ds-hero__glow" aria-hidden="true"></span>
<p class="ds-hero__eyebrow">
<span class="ds-badge">New in 2026.3</span>
</p>
<!-- The top of the document outline. -->
<h1 id="hero-heading" class="ds-hero__heading">
Deploy to twenty-four regions in one command.
</h1>
<p class="ds-hero__body">
No build servers to manage, no YAML to write, and a rollback that
finishes in eight seconds.
</p>
<div class="ds-hero__actions">
<a class="ds-btn ds-btn--filled" href="/start">Start deploying</a>
<a class="ds-btn ds-btn--text" href="/demo">Watch the demo</a>
</div>
</section>CSS
.ds-hero {
position: relative;
overflow: hidden;
/* The space IS the emphasis. Card-level padding makes a hero read as a
card, and the reader treats it as one item among several. */
padding-block: var(--space-12);
padding-inline: var(--space-8);
}
.ds-hero__heading {
/* Fluid, so it never wraps awkwardly between breakpoints. */
font-size: clamp(2rem, 5vw, 3.5rem);
/* Large type needs tight leading and negative tracking; body values look
loose and unfinished at this size. */
line-height: 1.06;
letter-spacing: -0.028em;
/* Much shorter than body copy. 48px across 1440px is unreadable. */
max-inline-size: 20ch;
text-wrap: balance;
}
.ds-hero__body {
font-size: 17px;
line-height: 1.6;
max-inline-size: 55ch;
color: var(--ds-fg-secondary);
}
.ds-hero__actions {
display: flex;
gap: 12px;
margin-block-start: var(--space-2);
}
/* Contrast against an arbitrary photograph is not assertable. */
.ds-hero--media::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(to right, rgb(0 0 0 / 0.7), rgb(0 0 0 / 0.2));
}
@media (max-width: 640px) {
/* Halved, or the hero eats the whole first screen. */
.ds-hero { padding-block: var(--space-6); padding-inline: var(--space-4); }
.ds-hero__actions { flex-direction: column; }
.ds-hero__actions > * { inline-size: 100%; }
}Component API
Jumbotron
| Prop | Type | Default | Description |
|---|---|---|---|
| heading* | ReactNode | — | One sentence saying what this is. Rendered as an h1 by default. |
| body | ReactNode | — | One sentence expanding the heading. Two is usually one too many. |
| eyebrow | ReactNode | — | Optional context above the heading. It must earn its line. |
| action | ReactNode | — | The single filled action. It is the reason the section exists. |
| secondaryAction | ReactNode | — | A text button. Never a second filled one. |
| media | ReactNode | — | A panel beside the text, or a background image. Backgrounds are automatically scrimmed. |
| align | 'left' | 'center' | 'left' | Centred suits a short heading and one action; left scans faster and is required with side media. |
| as | 'h1' | 'h2' | 'h1' | h2 when the hero is not the top of the page — the outline must stay correct. |
Professional tips
- Write the heading before designing anything. If it cannot be said in twenty words, the problem is the message, not the layout.
- Cut the body sentence first when the section feels crowded. The heading and the action do almost all of the work.
- Use text-wrap: balance on the heading. It costs one line of CSS and prevents the single orphaned word that makes a hero look unfinished.
- Test the heading at 375px early. A headline that wraps into five lines on a phone is the most common hero failure, and it is invisible on a desktop.
- For an empty state, keep the same anatomy at a smaller scale — the user needs exactly the same three things.
Performance
- The hero image is the largest contentful paint on most landing pages. Preload it, size it correctly, and never lazy-load it.
- Serve modern formats with a fallback, and give the image an explicit aspect ratio so the heading does not shift when it loads.
- Background video is rarely worth its cost: several megabytes, no autoplay on low-power mode, and a decode that competes with the text render. A poster image is usually better.
- Fluid type with clamp() removes the layout shift that breakpoint-based font sizes cause at the moment of resize.
Common mistakes
- Two filled buttons, so neither reads as the recommendation.
- A heading measure as wide as the container, making large type hard to read.
- Text on an unscrimmed image, with contrast dependent on the asset.
- A styled div instead of an h1, leaving the page with no outline entry.
- Three paragraphs nobody reads at hero scale.
- Full desktop padding on mobile, so the hero fills the entire first screen.
- A hero on an application screen, spending 200px on selling something the user already bought.
- Body-copy leading and tracking on a 48px headline, which looks loose and unfinished.
Real-world recommendations
- The heading is the highest-leverage sentence on a landing page and it is usually written last. Reverse that: it should be the first thing agreed and the last thing changed.
- Specific beats aspirational every time. "Deploy in eight seconds" outperforms "Ship with confidence" because one of them is a claim the reader can evaluate.
- Above-the-fold anxiety causes most hero bloat. Users scroll — the hero does not need to contain the whole pitch, only enough to earn the scroll.
- Rotating heroes and video backgrounds both test worse than a single strong message with a still image, consistently enough that the burden of proof sits with anyone proposing them.