Component Contract
A high-impact, single-message landing component that orients visitors and presents one clear CTA on key landing pages.
Rendered
Pension Solutions
Helping employers offer retirement security with flexible plan designs and expert administration.
Explore solutionsRendered
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>
| Component name | Hero |
|---|---|
| AEM component name | caat/components/hero |
| Recommended implementation | Custom component — no Core Component equivalent exists for this design |
| Component group | CAAT Design System |
| Purpose | Orient visitors, present one clear message and CTA on key landing pages |
| Existing AEM component replaced | CAAT Carousel (#5) — consolidate rotating hero into single-message hero |
| Token | Value (reference) | Purpose |
|---|---|---|
--caat-blue-900 | #003750 | Hero card background, stat strip background |
--caat-blue-700 | #0b5a80 | Card hover state background |
--caat-blue-100 | #e7f4fb | Light tint in gradient backgrounds |
--caat-green | #55a546 | Accent rule colour |
--caat-radius-lg | 1.25rem | Hero frame and media border radius |
--caat-shadow | 0 .75rem 2rem rgba(0,55,80,.12) | Card and media box shadow |
--caat-focus | 0 0 0 .25rem rgba(47,149,210,.35) | Focus ring on CTA |
--caat-font-primary | "Libre Franklin", Arial, sans-serif | All hero typography |
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.
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.
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
| Eyebrow text | Text field | No | — | Short category label above the heading. Max 3 words. |
| Heading | Text field | Yes | — | Primary hero message. Concise and action-oriented. |
| Heading level | Dropdown | No | h1 | h1 | h2 | h3. Use h1 only when the hero is the page hero. |
| Summary | Text area | No | — | Supporting description. Max 35 words recommended. |
| CTA label | Text field | Conditional | — | Required if CTA URL is provided. |
| CTA URL | Path browser | Conditional | — | Required if CTA label is provided. |
| CTA target | Checkbox | No | Same tab | If checked, opens in new tab with rel="noopener". |
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
| Image | Image picker | No | Gradient fallback | Recommended 1800 × 1000 px minimum. Supports DAM assets. |
| Image alt text | Text field | Conditional | — | Required unless "Decorative image" is checked. |
| Image position | Dropdown | No | Center | top | center | bottom | left | right. Sets background-position. |
| Use gradient instead | Checkbox | No | Off | Replaces the image with the default branded gradient. |
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
| Stats strip | Checkbox | No | Off | When enabled, renders a stats bar below the hero image |
| Card background colour | Colour picker | No | #003750 | Override the default card background. Must meet WCAG contrast on white text. |
| Full-width toggle | Checkbox | No | Off | Hero breaks out of container to span full viewport width. |
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
| Analytics label | Text field | No | hero-primary-cta | Custom label for analytics reporting. |
| Decorative image | Checkbox | No | Off | If checked, image is treated as decorative (aria-label removed, role="presentation" set). |
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.
| Element | Guideline |
|---|---|
| Eyebrow | 1–3 words |
| Headline | 6–12 words, ideally under two lines on desktop |
| Summary | 18–35 words |
| CTA label | 2–5 words, starting with a verb |
"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.
"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.
<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>
<div class="caat-hero-media">
<img src="/content/dam/caat/hero-image.jpg" alt="" role="presentation">
</div>
<!-- When hero is NOT the page hero, use h2 -->
<h2 id="hero-heading">Secondary hero heading</h2>
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.
.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 */
.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;
}
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.
The hero is static — no JavaScript. Everything below is CSS composition; verify it survives AEM integration.
.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..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..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..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.:has(.stretched-link:focus-visible) pattern; CTAs inside the panel use the Button contract (incl. --outline-white on the glass surface).prefers-reduced-motion rule in caat-base.css — no per-component handling needed.var(--caat-blue-900))
so that theme changes propagate automatically.
| Requirement | Acceptance 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. |
<h1> only when this hero is the page-level hero. Interior sections should use <h2> or <h3>.<a> element with a crawlable href. Do not use JavaScript-only navigation.<img> element or use role="img" with aria-label so the content is accessible to search engines and assistive tech.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.
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.
New component No direct AEM Core Component equivalent exists. Build as a custom component with HTL/Sightly.
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.
caat.components.herocaat.base (tokens + base styles)hero.csshero.js (analytics wiring, impression observer)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.
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
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
cta_click event fires correctly on CTA interaction.| Area | Done 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. |