Component Contract

Hero

A high-impact, single-message landing component that orients visitors and presents one clear CTA on key landing pages.

Live Demo

Slight-overlap hero

Rendered

Diverse group of professionals walking together outside a modern office building

Pension Solutions

A modern defined benefit pension solution

Helping employers offer retirement security with flexible plan designs and expert administration.

Explore solutions
125,000 Members
800 Employers across 20 industries
$23.3 billion Net assets

Stats strip (standalone)

Rendered

125,000 Members
800 Employers across 20 industries
$23.3 billion Net assets

Code sample

HTML

<!-- Slight-overlap hero with image -->
<section class="caat-composable-hero caat-hero-slight-overlap"
         aria-labelledby="hero-heading"
         data-analytics-component="hero">
  <div class="caat-hero-frame">
    <div class="caat-hero-media">
      <img src="/content/dam/caat/hero-image.jpg"
           alt="Descriptive alt text for the hero image">
    </div>
    <div class="caat-hero-card">
      <p class="eyebrow">Pension Solutions</p>
      <h3 id="hero-heading">A modern defined benefit pension solution</h3>
      <div class="accent-rule"></div>
      <p>Summary text — max 35 words.</p>
      <a href="/en/solutions" class="btn btn-outline-light"
         data-analytics-event="cta_click"
         data-analytics-label="hero-primary-cta"
         data-analytics-destination="/en/solutions">
        Explore solutions
      </a>
    </div>
  </div>
</section>

<!-- Stats strip -->
<div class="caat-stat-strip">
  <div class="caat-stat-item">
    <span class="caat-stat__icon" aria-hidden="true"><i class="bi bi-people-fill"></i></span>
    <strong>125,000+</strong>
    <span>Members</span>
  </div>
  <div class="caat-stat-item">
    <span class="caat-stat__icon" aria-hidden="true"><i class="bi bi-building"></i></span>
    <strong>800+</strong>
    <span>Employers</span>
  </div>
  <div class="caat-stat-item">
    <span class="caat-stat__icon" aria-hidden="true"><i class="bi bi-piggy-bank-fill"></i></span>
    <strong>$24B+</strong>
    <span>Assets</span>
  </div>
  <div class="caat-stat-item">
    <span class="caat-stat__icon" aria-hidden="true"><i class="bi bi-shield-check"></i></span>
    <strong>116%</strong>
    <span>Funded ratio</span>
  </div>
</div>

1. Component Summary

Component nameHero
AEM component namecaat/components/hero
Recommended implementationCustom component — no Core Component equivalent exists for this design
Component groupCAAT Design System
PurposeOrient visitors, present one clear message and CTA on key landing pages
Existing AEM component replacedCAAT Carousel (#5) — consolidate rotating hero into single-message hero

Primary use cases

  1. Homepage hero — primary brand statement and CTA
  2. Campaign landing pages — single focused message
  3. Pension solutions pages — orient visitors to key offering
  4. Employer acquisition pages — value proposition for prospective employers
  5. Event registration pages — promote upcoming events

Non-goals

  1. Not for every page — use breadcrumb/title pattern instead for interior pages
  2. Not for multiple competing CTAs — one primary CTA only
  3. Not for embedding forms — use a dedicated form component below the hero
  4. Not for logo placement — the hero is for messaging, not branding

2. Design System Source / Token Dependencies

TokenValue (reference)Purpose
--caat-blue-900#003750Hero card background, stat strip background
--caat-blue-700#0b5a80Card hover state background
--caat-blue-100#e7f4fbLight tint in gradient backgrounds
--caat-green#55a546Accent rule colour
--caat-radius-lg1.25remHero frame and media border radius
--caat-shadow0 .75rem 2rem rgba(0,55,80,.12)Card and media box shadow
--caat-focus0 0 0 .25rem rgba(47,149,210,.35)Focus ring on CTA
--caat-font-primary"Libre Franklin", Arial, sans-serifAll hero typography

Hero card overhang

The card overhang (the amount the card extends below the hero media) must never exceed 75 px. This is enforced via the CSS custom property --caat-hero-card-overhang: 75px.

Visual variants

  • Standard — full-size hero with card overlaid on media
  • With stats strip — standard hero with an attached stat bar below the image
  • Full-width breakout — hero breaks out of container to span full viewport width

Photography support

Photography backgrounds use a subtle gradient overlay to ensure text readability. Apply the image via the --hero-image custom property or use the pre-built .gradient-demo class for abstract gradients.

3. Authoring Fields

Content tab

FieldTypeRequiredDefaultNotes
Eyebrow textText fieldNoShort category label above the heading. Max 3 words.
HeadingText fieldYesPrimary hero message. Concise and action-oriented.
Heading levelDropdownNoh1h1 | h2 | h3. Use h1 only when the hero is the page hero.
SummaryText areaNoSupporting description. Max 35 words recommended.
CTA labelText fieldConditionalRequired if CTA URL is provided.
CTA URLPath browserConditionalRequired if CTA label is provided.
CTA targetCheckboxNoSame tabIf checked, opens in new tab with rel="noopener".

Media tab

FieldTypeRequiredDefaultNotes
ImageImage pickerNoGradient fallbackRecommended 1800 × 1000 px minimum. Supports DAM assets.
Image alt textText fieldConditionalRequired unless "Decorative image" is checked.
Image positionDropdownNoCentertop | center | bottom | left | right. Sets background-position.
Use gradient insteadCheckboxNoOffReplaces the image with the default branded gradient.

Style tab

FieldTypeRequiredDefaultNotes
Stats stripCheckboxNoOffWhen enabled, renders a stats bar below the hero image
Card background colourColour pickerNo#003750Override the default card background. Must meet WCAG contrast on white text.
Full-width toggleCheckboxNoOffHero breaks out of container to span full viewport width.

Behaviour tab

FieldTypeRequiredDefaultNotes
Analytics labelText fieldNohero-primary-ctaCustom label for analytics reporting.
Decorative imageCheckboxNoOffIf checked, image is treated as decorative (aria-label removed, role="presentation" set).

4. Authoring Validation Rules

  1. Heading is required. The dialog must not allow saving without it.
  2. CTA label and CTA URL are required together — if one is provided, the other must be as well.
  3. Image alt text is required unless the "Decorative image" checkbox is enabled.
  4. Eyebrow text must not exceed 3 words. Display a validation warning if exceeded.
  5. Summary must not exceed 35 words. Display a character/word count indicator.
  6. Only one primary CTA is permitted. The dialog does not allow adding multiple CTA buttons.

5. Content Guidance

Heroes orient visitors quickly, preserve the image's focal point, and keep the CTA usable on mobile. Use them for page introductions, campaign landing pages, and major story moments — not for every content block.

Recommended text length

ElementGuideline
Eyebrow1–3 words
Headline6–12 words, ideally under two lines on desktop
Summary18–35 words
CTA label2–5 words, starting with a verb

Image safe zones

  • Keep faces, logos, text, and key details out of the overlay zone (left third of image).
  • Use centre-right crops when the content card sits left.
  • Test desktop, tablet, and mobile before publishing.

Mobile behaviour

  • Stack the card below the image on narrow viewports.
  • Reduce image height before reducing legibility.
  • Avoid placing small text inside the image itself.

Busy images or people

  • Add a subtle gradient overlay only when needed for readability.
  • Choose images with quiet negative space for the card area.
  • Never cover a person's face, logo, or meaningful action with the headline or CTA.

Strong hero usage

"A retirement solution where everyone wins"

Why it works: concise message, one audience need, calm visual field, clear next step, and no meaningful image detail hidden under the card.

Weak hero usage

"Learn more about all of our services and programs today"

Why it fails: vague promise, no audience, too many possible actions, and a busy image that competes with the message.

6. Expected Semantic HTML Output

Standard hero

<section class="caat-composable-hero"
         aria-labelledby="hero-heading"
         data-analytics-component="hero">
  <div class="caat-hero-frame">
    <div class="caat-hero-media">
      <img src="/content/dam/caat/hero-image.jpg"
           alt="Descriptive alt text for the hero image">
    </div>
    <div class="caat-hero-card">
      <p class="eyebrow">Eyebrow Label</p>
      <h1 id="hero-heading">Primary hero heading</h1>
      <div class="accent-rule"></div>
      <p>Supporting summary text, max 35 words.</p>
      <a href="/en/target-page"
         class="btn btn-outline-light"
         data-analytics-event="cta_click"
         data-analytics-label="hero-primary-cta"
         data-analytics-destination="/en/target-page">
        Call to action
      </a>
    </div>
  </div>
</section>

Decorative image variant

<div class="caat-hero-media">
  <img src="/content/dam/caat/hero-image.jpg" alt="" role="presentation">
</div>

Configurable heading level

<!-- When hero is NOT the page hero, use h2 -->
<h2 id="hero-heading">Secondary hero heading</h2>

7. CSS Contract

The hero component relies on the global token layer defined in /assets/css/tokens.css. All component-specific styles live in /assets/css/components/hero.css.

Core classes

.caat-composable-hero   /* Root wrapper */
.caat-hero-frame        /* Positioning context for card overlay */
.caat-hero-media        /* Background image / gradient area */
.caat-hero-card         /* Overlay card — LAYOUT (size/position) */
.caat-hero-panel        /* Overlay card — SKIN (canonical, caat-base.css);
                           compose BOTH classes on the card element */
.accent-rule            /* Green divider bar */

/* Variants */
.caat-hero-slight-overlap   /* Card positioned at bottom of image */

/* Stat strip */
.caat-stat-strip           /* Stats row; background can bleed via --bleed modifier */
.caat-stat-item            /* Individual stat */

75 px overhang rule

.caat-hero-slight-overlap {
  --caat-hero-card-overhang: 75px;
}
.caat-hero-slight-overlap .caat-hero-frame {
  padding-bottom: var(--caat-hero-card-overhang);
  overflow: visible;
}

Responsive stacking

At 992 px and below, the hero card unstacks from the overlay position and sits below the media area. The overhang is reduced to 56 px on tablet breakpoints.

Composition & behaviour notes

The hero is static — no JavaScript. Everything below is CSS composition; verify it survives AEM integration.

  • Panel skin vs layout: the card element composes two classes — .caat-hero-card (size/position, hero.css) + .caat-hero-panel (canonical glass skin incl. hover lift, caat-base.css). Omitting the panel class yields an unstyled box; omitting the card class loses positioning.
  • Media: the image fills .caat-hero-media with object-fit: cover; min-heights step down 22 → 18 → 14 rem across breakpoints. Author images for the worst crop (centre-weighted focal point) — there is no focal-point control.
  • Legibility scrim: .caat-hero-media::after applies var(--caat-overlay-side). Never remove it when text or the panel sits over a photo; it is the contrast guarantee.
  • Stat strip: composes the Stat/KPI component (.caat-stat-strip / .caat-stat-item, stat.css). Use .caat-stat-strip--bleed when the band background should span the viewport while the stats stay aligned to the container. Author stats through the Stat component contract.
  • Interactivity: the panel lifts on hover (decorative). If the whole card is made clickable, use a stretched link inside it — the panel then shows the focus ring via the Card/panel :has(.stretched-link:focus-visible) pattern; CTAs inside the panel use the Button contract (incl. --outline-white on the glass surface).
  • Reduced motion: the lift/transition is neutralized globally by the prefers-reduced-motion rule in caat-base.css — no per-component handling needed.
Note: Do not duplicate token values inside the component CSS. Always reference the custom property (e.g. var(--caat-blue-900)) so that theme changes propagate automatically.

8. Accessibility Requirements

RequirementAcceptance criteria
Keyboard focus on CTA The CTA link must be reachable via Tab and activatable with Enter. A visible focus ring (var(--caat-focus)) must appear on :focus-visible.
Meaningful image alt Content images must have descriptive aria-label on the .caat-hero-media element with role="img".
Decorative image handling When the image is decorative, set role="presentation" and aria-hidden="true". Do not output an empty aria-label.
Heading level configurable Authors must be able to set the heading level (h1–h3) to maintain correct document outline. Use h1 only when this is the page hero.
Sufficient contrast on card overlay White text on the card background must meet WCAG 2.1 AA (4.5 : 1 for normal text, 3 : 1 for large text). The default #003750 background passes.
No auto-playing carousel This component deliberately replaces the carousel. It must not auto-rotate, auto-advance, or include multiple slides.

9. SEO Requirements

  1. Heading level is configurable — use <h1> only when this hero is the page-level hero. Interior sections should use <h2> or <h3>.
  2. Descriptive CTA text — avoid generic labels like "Learn more". Use action-oriented, context-rich text (e.g. "Explore pension solutions").
  3. Real href on CTA link — the CTA must be a real <a> element with a crawlable href. Do not use JavaScript-only navigation.
  4. Don't use background-image for content images — if the image conveys meaning, render it as an <img> element or use role="img" with aria-label so the content is accessible to search engines and assistive tech.

10. Analytics Requirements

Analytics are captured through the shared data-analytics-component attribute on the component root (emitted automatically) and reported to the central data layer. Event names and payloads follow the organisation's standard analytics schema — defined centrally with the analytics team, not invented per component — so no bespoke tracking is specified here.

11. AEM Implementation Notes

Existing component mapping

This component replaces CAAT Carousel (component #5) in the existing AEM component library. The recommendation is to consolidate the rotating hero carousel into a single-message hero for better usability, accessibility, and performance.

AEM triage

New component No direct AEM Core Component equivalent exists. Build as a custom component with HTL/Sightly.

Proxy / extend guidance

There is no Core Component to proxy. Create a custom component at /apps/caat/components/hero. Build the HTL template (hero.html) to emit the semantic HTML defined in Section 6. Use data-sly-attribute for dynamic class and data-attribute output.

Client library structure

  • Category: caat.components.hero
  • Dependencies: caat.base (tokens + base styles)
  • CSS: hero.css
  • JS: hero.js (analytics wiring, impression observer)

Stats strip

The stats strip can be implemented as either a child component within the hero or as a separate stand-alone component (caat/components/stat-strip) placed immediately below the hero in the page template. The recommended approach is a separate component for maximum reusability. Optional icons may appear above each stat on desktop only; hide them at tablet and mobile widths so the stacked layout stays compact.

Suggested file structure

hero/
├── _cq_dialog/
│   └── .content.xml          # Touch UI dialog
├── _cq_editConfig.xml         # Edit configuration
├── .content.xml                # Component node definition
├── hero.html                   # HTL template
├── clientlibs/
│   ├── .content.xml
│   ├── css/
│   │   └── hero.css
│   └── js/
│       └── hero.js
└── README.md                   # Dev notes

12. Dialog Field Model

Simplified model — shows field intent and Granite UI resource types, not the literal cq:dialog node tree (e.g. select options are authored as child items nodes).

# caat/components/hero — Touch UI Dialog
# _cq_dialog/.content.xml (YAML representation)

dialog:
  jcr:primaryType: nt:unstructured
  sling:resourceType: cq/gui/components/authoring/dialog
  content:
    jcr:primaryType: nt:unstructured
    sling:resourceType: granite/ui/components/coral/foundation/container
    items:
      tabs:
        jcr:primaryType: nt:unstructured
        sling:resourceType: granite/ui/components/coral/foundation/tabs
        items:

          # ── Content tab ──────────────────────────────
          content:
            jcr:title: Content
            items:
              eyebrowText:
                sling:resourceType: granite/ui/components/coral/foundation/form/textfield
                name: ./eyebrowText
                fieldLabel: Eyebrow text
                fieldDescription: Short category label (max 3 words)
              heading:
                sling:resourceType: granite/ui/components/coral/foundation/form/textfield
                name: ./heading
                fieldLabel: Heading
                required: true
              headingLevel:
                sling:resourceType: granite/ui/components/coral/foundation/form/select
                name: ./headingLevel
                fieldLabel: Heading level
                items:
                  - { text: H1, value: h1 }
                  - { text: H2, value: h2 }
                  - { text: H3, value: h3 }
              summary:
                sling:resourceType: granite/ui/components/coral/foundation/form/textarea
                name: ./summary
                fieldLabel: Summary
                fieldDescription: Max 35 words recommended
              ctaLabel:
                sling:resourceType: granite/ui/components/coral/foundation/form/textfield
                name: ./ctaLabel
                fieldLabel: CTA label
              ctaURL:
                sling:resourceType: granite/ui/components/coral/foundation/form/pathfield
                name: ./ctaURL
                fieldLabel: CTA URL
                rootPath: /content
              ctaTarget:
                sling:resourceType: granite/ui/components/coral/foundation/form/checkbox
                name: ./ctaTarget
                text: Open in new tab
                value: _blank

          # ── Media tab ────────────────────────────────
          media:
            jcr:title: Media
            items:
              image:
                sling:resourceType: granite/ui/components/coral/foundation/form/pathfield
                name: ./image
                fieldLabel: Image
                rootPath: /content/dam
              imageAlt:
                sling:resourceType: granite/ui/components/coral/foundation/form/textfield
                name: ./imageAlt
                fieldLabel: Image alt text
              imagePosition:
                sling:resourceType: granite/ui/components/coral/foundation/form/select
                name: ./imagePosition
                fieldLabel: Image position
                items:
                  - { text: Top,    value: top }
                  - { text: Center, value: center }
                  - { text: Bottom, value: bottom }
                  - { text: Left,   value: left }
                  - { text: Right,  value: right }
              useGradient:
                sling:resourceType: granite/ui/components/coral/foundation/form/checkbox
                name: ./useGradient
                text: Use gradient instead of image

          # ── Style tab ────────────────────────────────
          style:
            jcr:title: Style
            items:
              variant:
                sling:resourceType: granite/ui/components/coral/foundation/form/select
                name: ./variant
                fieldLabel: Variant
                required: true
                items:
                  - { text: Standard,       value: standard }
                  - { text: Full width,     value: full-width }
              cardBackground:
                sling:resourceType: granite/ui/components/coral/foundation/form/colorfield
                name: ./cardBackground
                fieldLabel: Card background colour
                value: "#003750"
              fullWidth:
                sling:resourceType: granite/ui/components/coral/foundation/form/checkbox
                name: ./fullWidth
                text: Full-width hero

          # ── Behaviour tab ────────────────────────────
          behaviour:
            jcr:title: Behaviour
            items:
              analyticsLabel:
                sling:resourceType: granite/ui/components/coral/foundation/form/textfield
                name: ./analyticsLabel
                fieldLabel: Analytics label
                value: hero-primary-cta
              decorativeImage:
                sling:resourceType: granite/ui/components/coral/foundation/form/checkbox
                name: ./decorativeImage
                text: Image is decorative

13. QA Acceptance Checklist

  • The hero card overhang does not exceed 75 px in the slight-overlap variant at any viewport width.
  • The hero card stacks below the media image at 992 px and below (responsive stacking).
  • Image safe zone testing: text on the card remains legible with various photography crops and positions.
  • CTA focus ring is visible and meets WCAG 3 : 1 contrast ratio against the card background.
  • The hero renders correctly at desktop, tablet, and mobile viewports with expected spacing and sizing.
  • Analytics data attributes are present in the DOM and the cta_click event fires correctly on CTA interaction.

14. Definition of Done

AreaDone means
Design Figma component matches this contract. Hero with and without stats strip is documented. Tokens are used — no hard-coded colour values.
Development Custom AEM component created at /apps/caat/components/hero. HTL template emits semantic HTML per Section 6. CSS uses design tokens. JS wires analytics events. Stats strip implemented as companion component.
QA All items in the QA Acceptance Checklist (Section 13) pass. Manual accessibility audit completed. Cross-browser tested (Chrome, Firefox, Safari, Edge). 75 px overhang validated.
Launch Component deployed to AEM production. Content authors trained. Carousel component (#5) deprecated and migration plan in place. Analytics dashboards confirmed receiving events.