Loading…
Loading…
A prominent section typically at the top of a page with a headline, description, and CTA.
The Hero section (also known as a hero banner, jumbotron, or above-the-fold banner) is a prominent visual component typically placed at the top of a page to immediately communicate the page's primary message, value proposition, or call-to-action. It is the first thing a user sees and sets the visual tone for the entire experience.
Hero sections combine typography, imagery (or color/gradient backgrounds), and calls-to-action into a single high-impact composition. They are used across marketing pages, landing pages, product pages, blog headers, and application dashboards.
When to use a Hero:
When NOT to use a Hero:
Use the Gradient Generator to create background gradients for your hero. Set type scales with the Font Scale Generator. Verify text-over-image contrast with the Contrast Checker.
| Variant | Description | Best For |
|---|---|---|
| Centered | All content centered horizontally and vertically. Single column. | Simple landing pages, announcement pages, brand pages |
| Split | Content on one side, image/media on the other (50/50 or 60/40). | Product pages, SaaS landing pages, feature showcases |
| Full-image | Full-bleed background image with overlaid text. | Visual-heavy brands, photography, travel, lifestyle |
| Video background | Looping video behind text content. | Modern SaaS, creative agencies, event pages |
| Gradient | Gradient background (solid or mesh) with text overlay. | Tech products, apps, where photography isn't available |
| Minimal | Text-only, no background image, relying on typography and whitespace. | Content-focused sites, blogs, documentation |
| Illustrated | Custom illustration (SVG/animation) alongside or behind text. | Playful brands, fintech, developer tools |
| Card hero | Content contained within a card element on a subtle background. | Dashboard welcomes, nested hero sections |
| Size | Height | Viewport Coverage | Use Case |
|---|---|---|---|
| Full viewport | 100vh | Entire screen | Brand-focused landing pages, single-page sites |
| Large | 70–80vh | Majority of viewport | Product pages, marketing pages |
| Medium | 50vh or 400–500px | Half viewport | Blog headers, secondary pages |
| Small | 200–300px | Compact | Section headers, dashboard banners |
| Treatment | Description | Tool |
|---|---|---|
| Solid color | Single brand color | Contrast Checker |
| Linear gradient | Two or more colors in a directional gradient | Gradient Generator |
| Radial/mesh gradient | Complex multi-point gradients for depth | Gradient Generator |
| Image with overlay | Photo with semi-transparent color overlay for text readability | Contrast Checker |
| Image with scrim | Gradient overlay (transparent to dark) from text area toward image | Contrast Checker |
| Pattern/texture | SVG pattern or subtle texture behind content | — |
| Property | Type | Default | Description |
|---|---|---|---|
variant | 'centered' | 'split' | 'fullImage' | 'minimal' | 'gradient' | 'centered' | Layout variant |
size | 'sm' | 'md' | 'lg' | 'full' | 'lg' | Height preset |
height | string | — | Custom height (overrides size) |
minHeight | string | — | Minimum height |
backgroundImage | string | — | URL for background image |
backgroundColor | string | — | Background color or CSS gradient |
overlay | boolean | true | Applies a semi-transparent overlay for text readability |
overlayColor | string | 'rgba(0,0,0,0.4)' | Overlay color and opacity |
overlayGradient | string | — | CSS gradient used as overlay (e.g., scrim) |
title | ReactNode | — | Primary heading content |
subtitle | ReactNode | — | Secondary text or description |
actions | ReactNode | — | CTA buttons or action elements |
media | ReactNode | — | Image, video, or illustration for split layout |
mediaPosition | 'left' | 'right' | 'right' | Side of the media in split variant |
textAlign | 'left' | 'center' | 'right' | 'center' | Text alignment within the content area |
maxWidth | string | '720px' | Maximum width of the text content area |
verticalAlign | 'top' | 'center' | 'bottom' | 'center' | Vertical alignment of content within the hero |
breadcrumb | ReactNode | — | Breadcrumb navigation displayed above the title |
badge | ReactNode | — | Badge or label above the title (e.g., "New", "Beta") |
className | string | — | Custom CSS class |
| Custom Property | Description | Default |
|---|---|---|
--hero-height | Section height | 80vh |
--hero-min-height | Minimum height | 400px |
--hero-bg | Background color/gradient | transparent |
--hero-overlay | Overlay color | rgba(0,0,0,0.4) |
--hero-content-max-width | Content max-width | 720px |
--hero-padding-x | Horizontal padding | 24px |
--hero-padding-y | Vertical padding | 48px |
--hero-title-size | Title font size | clamp(2rem, 5vw, 4rem) |
--hero-subtitle-size | Subtitle font size | clamp(1rem, 2vw, 1.5rem) |
| Token | Role | Typical Value |
|---|---|---|
color.bg.brand | Brand-colored hero background | Brand primary |
color.bg.surface | Minimal hero background | #ffffff / #1a1a1a |
color.text.on-brand | Text color on brand background | #ffffff |
color.text.on-dark | Text on dark overlays | #ffffff |
color.text.primary | Text on light/minimal heroes | #111827 |
color.text.secondary | Subtitle text on light heroes | #6b7280 |
font.size.display-lg | Hero title (large variant) | clamp(2.5rem, 5vw, 4.5rem) |
font.size.display-md | Hero title (medium variant) | clamp(2rem, 4vw, 3rem) |
font.size.xl | Hero subtitle | clamp(1.125rem, 2vw, 1.5rem) |
font.weight.bold | Title font weight | 700 |
font.weight.normal | Subtitle font weight | 400 |
line-height.tight | Title line height | 1.1–1.2 |
line-height.relaxed | Subtitle line height | 1.5–1.6 |
space.6 | Gap between title and subtitle | 24px |
space.8 | Gap between subtitle and CTA | 32px |
space.12 – space.16 | Hero vertical padding | 48–64px |
radius.lg – radius.xl | Card hero border radius | 16–24px |
Configure your typography scale with the Font Scale Generator. Preview gradient backgrounds in the Gradient Generator. Verify text contrast with the Contrast Checker.
Hero is primarily a static presentational component, but it has important viewport-driven and content-driven states:
| State | Trigger | Effect |
|---|---|---|
| Default | Page load | Full hero renders with background, text, and CTAs |
| Responsive mobile | Viewport < 768px | Split heroes stack vertically, font sizes reduce via clamp(), padding decreases |
| Background loading | Large background image loading | Show a solid-color fallback or gradient while the image loads |
| Background loaded | Image fully loaded | Cross-fade or instant swap to the background image |
| Video playing | Autoplay video background | Video loops silently; falls back to poster image if autoplay is blocked |
| Reduced motion | prefers-reduced-motion: reduce | Video backgrounds pause (show poster), animations are disabled, parallax is removed |
| High contrast | prefers-contrast: more | Overlay opacity increases, text borders/shadows added for clarity |
| Print media | Background images hidden, text displayed in print-friendly colors |
Hero typography should use fluid sizing with clamp() to scale smoothly between viewport sizes:
.hero__title {
font-size: clamp(2rem, 5vw, 4rem);
line-height: 1.1;
}
.hero__subtitle {
font-size: clamp(1rem, 2vw, 1.5rem);
line-height: 1.5;
}
This eliminates the need for breakpoint-specific font-size media queries. Configure your fluid type scale with the Font Scale Generator.
Always provide a background-color fallback that closely matches the dominant color of the hero image. This prevents a jarring white-to-image flash:
.hero {
background-color: #1a1a2e; /* dominant image color */
background-image: url('hero.webp');
background-size: cover;
background-position: center;
}
Hero sections present significant accessibility challenges due to their reliance on visual impact, background imagery, and often low contrast ratios between text and backgrounds.
WCAG Success Criteria:
background-image (which is inherently hidden from AT) rather than <img>.prefers-reduced-motion: reduce, pause animations automatically.Text-over-Image Strategies:
| Strategy | Contrast Guarantee | Performance |
|---|---|---|
Solid overlay (rgba(0,0,0,0.5)) | Strong, uniform | Simple, reliable |
| Gradient scrim (transparent → dark) | Strong in text area, image visible elsewhere | More visually sophisticated |
Text shadow (0 2px 4px rgba(0,0,0,0.8)) | Moderate, depends on image | Lightweight but less reliable |
| Background on text (semi-transparent pill) | Strong, localized | Can look dated |
| Dark image selection (curate only dark images) | Unreliable, varies by image | Fragile — avoid |
The recommended approach is a gradient scrim: a CSS gradient overlay that transitions from near-opaque behind the text to transparent toward the image focal point. This provides reliable contrast while preserving image visibility.
Do:
clamp() for responsive hero titles — configure with the Font Scale Generatorbackground-color fallback that approximates the hero image's dominant colorprefers-reduced-motion handling for video/animated heroesloading="eager" and fetchpriority="high" for hero images — they are the LCP elementDon't:
Performance Considerations:
| Optimization | Impact |
|---|---|
Use <img> with srcset/sizes for responsive image loading | Serves appropriately sized images per viewport |
| Use WebP/AVIF formats with JPEG fallback | 25–50% smaller file sizes |
Add fetchpriority="high" to the hero image | Browser prioritizes LCP image download |
| Inline critical CSS for the hero | Prevents FOUC (flash of unstyled content) |
Use aspect-ratio to prevent layout shift | Reserves space before image loads (CLS improvement) |
Hero Copy Hierarchy:
| Element | Character Limit | Typography |
|---|---|---|
| Badge/label | 15–20 chars | Small caps or tag style |
| Headline | 40–60 chars | Display font, bold |
| Subheadline | 80–150 chars | Body font, regular weight |
| Primary CTA | 15–25 chars | Button, primary variant |
| Secondary CTA | 15–25 chars | Button, ghost/outline variant |
<!-- Centered hero with gradient background -->
<section class="hero hero--centered" style="
display: flex;
align-items: center;
justify-content: center;
min-height: 80vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #ffffff;
padding: 48px 24px;
text-align: center;
">
<div class="hero__content" style="max-width: 720px;">
<span class="hero__badge" style="
display: inline-block;
padding: 4px 12px;
background: rgba(255,255,255,0.2);
border-radius: 9999px;
font-size: 0.875rem;
margin-bottom: 16px;
">Now in Beta</span>
<h1 class="hero__title" style="
font-size: clamp(2rem, 5vw, 4rem);
line-height: 1.1;
margin: 0 0 16px;
font-weight: 700;
">Build beautiful interfaces faster</h1>
<p class="hero__subtitle" style="
font-size: clamp(1rem, 2vw, 1.5rem);
line-height: 1.5;
margin: 0 0 32px;
opacity: 0.9;
">A complete design system with accessible components, design tokens, and interactive tools.</p>
<div class="hero__actions" style="display: flex; gap: 12px; justify-content: center; flex-wrap: wrap;">
<a href="/get-started" class="btn btn--primary btn--lg">Get Started</a>
<a href="/docs" class="btn btn--ghost btn--lg" style="color: #fff; border: 1px solid rgba(255,255,255,0.3);">Documentation</a>
</div>
</div>
</section>
<!-- Split hero with image -->
<section class="hero hero--split" style="
display: grid;
grid-template-columns: 1fr 1fr;
min-height: 70vh;
align-items: center;
">
<div class="hero__content" style="padding: 48px;">
<h1 style="font-size: clamp(2rem, 4vw, 3.5rem); line-height: 1.1; margin: 0 0 16px;">
Your product headline
</h1>
<p style="font-size: 1.25rem; color: #6b7280; margin: 0 0 32px; line-height: 1.5;">
A compelling description of what your product does and why users should care.
</p>
<div style="display: flex; gap: 12px;">
<a href="/signup" class="btn btn--primary btn--lg">Start Free Trial</a>
</div>
</div>
<div class="hero__media">
<img src="hero-image.webp" alt="" loading="eager" fetchpriority="high"
style="width: 100%; height: 100%; object-fit: cover;" />
</div>
</section>
<style>
@media (max-width: 768px) {
.hero--split {
grid-template-columns: 1fr !important;
}
.hero--split .hero__content { order: 1; }
.hero--split .hero__media { order: 0; max-height: 300px; overflow: hidden; }
}
</style>import React from 'react';
interface HeroProps {
variant?: 'centered' | 'split' | 'fullImage' | 'minimal' | 'gradient';
size?: 'sm' | 'md' | 'lg' | 'full';
backgroundImage?: string;
backgroundColor?: string;
overlay?: boolean;
overlayColor?: string;
title: React.ReactNode;
subtitle?: React.ReactNode;
actions?: React.ReactNode;
media?: React.ReactNode;
mediaPosition?: 'left' | 'right';
badge?: React.ReactNode;
textAlign?: 'left' | 'center' | 'right';
maxWidth?: string;
className?: string;
}
const sizeMap = { sm: '300px', md: '50vh', lg: '80vh', full: '100vh' };
function Hero({
variant = 'centered',
size = 'lg',
backgroundImage,
backgroundColor = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
overlay = true,
overlayColor = 'rgba(0,0,0,0.4)',
title,
subtitle,
actions,
media,
mediaPosition = 'right',
badge,
textAlign = variant === 'split' ? 'left' : 'center',
maxWidth = '720px',
className,
}: HeroProps) {
if (variant === 'split') {
return (
<section
className={className}
style={{
display: 'grid',
gridTemplateColumns: mediaPosition === 'right' ? '1fr 1fr' : '1fr 1fr',
minHeight: sizeMap[size],
alignItems: 'center',
}}
>
<div style={{ padding: 48, order: mediaPosition === 'left' ? 1 : 0 }}>
{badge && <div style={{ marginBottom: 16 }}>{badge}</div>}
<h1 style={{ fontSize: 'clamp(2rem, 4vw, 3.5rem)', lineHeight: 1.1, margin: '0 0 16px' }}>
{title}
</h1>
{subtitle && (
<p style={{ fontSize: '1.25rem', color: '#6b7280', margin: '0 0 32px', lineHeight: 1.5 }}>
{subtitle}
</p>
)}
{actions && <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>{actions}</div>}
</div>
<div style={{ order: mediaPosition === 'left' ? 0 : 1, overflow: 'hidden', height: '100%' }}>
{media}
</div>
</section>
);
}
return (
<section
className={className}
style={{
position: 'relative',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
minHeight: sizeMap[size],
background: backgroundImage ? undefined : backgroundColor,
backgroundImage: backgroundImage ? `url(${backgroundImage})` : undefined,
backgroundSize: 'cover',
backgroundPosition: 'center',
color: '#ffffff',
padding: '48px 24px',
textAlign,
}}
>
{overlay && backgroundImage && (
<div
aria-hidden="true"
style={{
position: 'absolute',
inset: 0,
background: overlayColor,
}}
/>
)}
<div style={{ position: 'relative', maxWidth, zIndex: 1 }}>
{badge && <div style={{ marginBottom: 16 }}>{badge}</div>}
<h1 style={{ fontSize: 'clamp(2rem, 5vw, 4rem)', lineHeight: 1.1, margin: '0 0 16px', fontWeight: 700 }}>
{title}
</h1>
{subtitle && (
<p style={{ fontSize: 'clamp(1rem, 2vw, 1.5rem)', lineHeight: 1.5, margin: '0 0 32px', opacity: 0.9 }}>
{subtitle}
</p>
)}
{actions && (
<div style={{ display: 'flex', gap: 12, justifyContent: textAlign === 'center' ? 'center' : 'flex-start', flexWrap: 'wrap' }}>
{actions}
</div>
)}
</div>
</section>
);
}
// Usage
function LandingPage() {
return (
<Hero
variant="centered"
size="lg"
backgroundColor="linear-gradient(135deg, #667eea 0%, #764ba2 100%)"
badge={<span className="badge badge--outline">Now in Beta</span>}
title="Build beautiful interfaces faster"
subtitle="A complete design system with accessible components, design tokens, and interactive tools."
actions={
<>
<a href="/start" className="btn btn--primary btn--lg">Get Started</a>
<a href="/docs" className="btn btn--ghost btn--lg">Documentation</a>
</>
}
/>
);
}Hero sections are typically application-level compositions rather than standardized design system primitives. Most design systems provide the building blocks (typography, spacing, buttons, layout) and leave hero composition to the application layer. However, some systems include hero-specific components:
Bootstrap provides the Jumbotron (deprecated in v5) which was a simple padded container with a larger heading. In Bootstrap 5, the recommended approach is composing a hero from utility classes: py-5 text-center bg-body-tertiary with responsive grid columns. Bootstrap's Examples page includes several hero templates (centered, split, image background) as copy-paste starting points.
Material Design 3 does not include a "Hero" component. MUI's approach is to compose heroes from <Box>, <Container>, <Typography>, <Stack>, and <Button>. The Material Design spec does not define a hero section pattern, as it is more of a marketing/editorial pattern than an application UI pattern.
Chakra UI does not provide a Hero component. Chakra's documentation includes hero examples built from <Container>, <Heading>, <Text>, <Stack>, and <Button>. The philosophy is that heroes are too variable in layout and content to standardize into a single component.
Tailwind UI (the commercial component library for Tailwind CSS) provides numerous hero section templates in their "Marketing > Hero Sections" category. These include: simple centered, split with image, split with screenshot, angled background, with app screenshot, with phone mockup, and dark variants. Tailwind UI represents the most comprehensive hero pattern library available, though it is template-based rather than component-based.
Ant Design does not provide a hero component. Landing page patterns are available in Ant Design Pro and Ant Design Landing (a separate page builder tool) which includes hero section templates with animated text and scroll-triggered effects.
Shadcn/ui does not include a hero component but provides all building blocks. Community-contributed hero patterns are available through the Shadcn registry and typically combine the Card, Button, and typography primitives with Tailwind utilities.
The consensus across design systems is that heroes are composed, not componentized. The building blocks that matter most for heroes are: Stack for content layout, Grid for split layouts, typography components for display headings, and Button for CTAs.