Skip to content
Last updated

HeaderHeroPrimaryMedia Pattern

A page-level hero pattern featuring a headline, subtitle, call-to-action buttons, and a primary media element. Supports images, videos, or custom React elements with enforced aspect ratios and object-fit constraints.

Overview

The HeaderHeroPrimaryMedia component provides a structured hero section with:

  • Responsive headline and subtitle layout
  • Primary and optional secondary call-to-action buttons
  • Media element (image, video, or custom) with responsive aspect ratios
  • Development-time validation warnings

Basic Usage

import HeaderHeroPrimaryMedia from "shared/sections/HeaderHeroPrimaryMedia/HeaderHeroPrimaryMedia";

function MyPage() {
  return (
    <HeaderHeroPrimaryMedia
      headline="Build on XRPL"
      subtitle="Start developing today with our comprehensive developer tools."
      links={[{ label: "Get Started", href: "/docs" }]}
      media={{
        type: "image",
        src: "/img/hero.png",
        alt: "XRPL Development",
      }}
    />
  );
}

Props

PropTypeRequiredDescription
headlineReact.ReactNodeYesHero headline text (display-md typography)
subtitleReact.ReactNodeYesHero subtitle text (label-l typography)
linksDesignConstrainedLink[]NoArray of { label, href } for ButtonGroup
mediaHeaderHeroMediaYesMedia element (image, video, or custom)
stackedbooleanNoStack headline, subtitle, and buttons in one column. Defaults to false. See Layout
classNamestringNoAdditional CSS classes for the header element
...restHTMLHeaderElement attributesNoAny other HTML header attributes

The links prop accepts an array of { label, href } objects for consistent ButtonGroup rendering; variant and color are set by the component:

  • First link: variant="primary", color="green"
  • Second link: variant="tertiary", color="green"
  • Max 2 links supported (ButtonGroup validation)

Layout

stackedLayout
false (default)Two columns: headline on the left (5 of 12 on lg), subtitle and buttons on the right. The headline bottom-aligns against the CTA column.
trueOne column (full width at base, 7 of 8 at md, 9 of 12 at lg — note this grid is 4/8/12 columns by breakpoint, not 12 throughout): headline, then subtitle, then buttons, with 16px between the headline and subtitle. The subtitle-to-buttons gap keeps the standard 24px (40px on lg).
<HeaderHeroPrimaryMedia
  stacked
  headline="Build on the XRP Ledger"
  subtitle="Everything you need to get started."
  media={{ type: "image", src: "/hero.jpg", alt: "Hero" }}
/>

Media placement is unchanged — it stays full-width below the text in both layouts.

Media Types

The media prop accepts a discriminated union of three types:

Image Media

media={{
  type: "image",
  src: string,        // Required
  alt: string,        // Required
  // ... all native <img> props except className and style
}}

Example:

media={{
  type: "image",
  src: "/img/hero.png",
  alt: "Hero image",
  loading: "lazy",
  decoding: "async"
}}

Video Media

media={{
  type: "video",
  src: string,        // Required
  alt?: string,       // Optional but recommended
  // ... all native <video> props except className and style
}}

Example:

media={{
  type: "video",
  src: "/video/intro.mp4",
  alt: "Introduction video",
  autoPlay: true,
  loop: true,
  muted: true,
  playsInline: true
}}

Custom Element Media

media={{
  type: "custom",
  element: React.ReactElement  // Required
}}

Example:

media={{
  type: "custom",
  element: <MyAnimationComponent />
}}

Examples

With Secondary CTA

<HeaderHeroPrimaryMedia
  headline="Real-world asset tokenization"
  subtitle="Learn how to issue crypto tokens and build solutions."
  links={[
    { label: "Get Started", href: "/docs" },
    { label: "Learn More", href: "/about" },
  ]}
  media={{
    type: "image",
    src: "/img/tokenization.png",
    alt: "Tokenization",
  }}
/>

Video Media

<HeaderHeroPrimaryMedia
  headline="Watch and Learn"
  subtitle="Explore our video tutorials."
  links={[{ label: "Watch Tutorials", href: "/tutorials" }]}
  media={{
    type: "video",
    src: "/video/intro.mp4",
    alt: "Introduction video",
    autoPlay: true,
    loop: true,
    muted: true,
  }}
/>

Custom Element

<HeaderHeroPrimaryMedia
  headline="Interactive Experience"
  subtitle="Engage with custom media."
  links={[{ label: "Explore", href: "/interactive" }]}
  media={{
    type: "custom",
    element: <MyAnimationComponent />,
  }}
/>

Design Constraints

The component enforces specific design requirements:

  • Aspect Ratios: Media maintains responsive aspect ratios:
    • Base: 16:9
    • Medium (md+): 2:1
    • Large (lg+): 3:1
  • Object Fit: All media uses object-fit: cover to fill the container
  • Type Safety: TypeScript discriminated unions ensure type-safe media selection

Validation

The component includes development-time validation that logs warnings to the console when required props are missing:

  • Missing headline: Component returns null (error logged)
  • Missing subtitle, links, or media: Warning logged, component still renders

CSS Classes

The component generates the following CSS classes:

  • bds-header-hero-primary-media - Root header element
  • bds-header-hero-primary-media--stacked - Root modifier, present when stacked is set
  • bds-header-hero-primary-media__stack - Single-column wrapper, only rendered when stacked is set
  • bds-header-hero-primary-media__headline - Headline container
  • bds-header-hero-primary-media__subtitle - Subtitle element
  • bds-header-hero-primary-media__cta-container - CTA container
  • bds-header-hero-primary-media__cta-buttons - CTA buttons wrapper
  • bds-header-hero-primary-media__media-container - Media container
  • bds-header-hero-primary-media__media-element - Media element

Best Practices

  1. Media Selection: Choose media that works well with the responsive aspect ratios (16:9 base, 2:1 md+, 3:1 lg+)
  2. Alt Text: Always provide meaningful alt text for images and videos
  3. Performance: Use loading="lazy" for images below the fold
  4. CTAs: Keep CTA text concise and action-oriented
  5. Headlines: Keep headlines concise and impactful