Component Contract
Navigation
Global navigation component covering the site header (navbar), footer, and mobile menu for the CAAT Pension Plan digital properties.
Live Demo
Desktop Mega Menu Navbar
Rendered
Footer
Mobile Menu
Rendered
- Alberta Milk
- Angus Consulting Management Limited
- Catholic Charities of the Archdiocese of Toronto and Agencies
- Community Living Toronto
- Jewish Vocational Services (JVS) - Toronto
- Lutheran Church – Canada (LCC) Pension Plan
- MC LegalManagement Limited Partnership
- Pension Plan for Employees of Healthcare Excellence Canada (HEC)
- Pension Plan for Employees of The College of Family Physicians of Canada
- Postmedia Network Inc
- Royal Ontario Museum (ROM)
- Sanofi Pasteur
- St. John Ambulance
- The Canadian Press
- The George Hull Centre for Children and Families
- The Globe and Mail
- The Manitoba Teachers’ Society
- Torstar Group of Companies
- United Way of Greater Toronto
- Winnipeg Free Press and Canstar Community News
- Youth Services Bureau (YSB)
Nav Link States
L1 navigation link interaction states. Hover and focus are shown statically via is-* classes mirroring the live :hover / :focus rules; the current-page state uses .active.
Rendered
Code samples
Desktop Mega Menu Navbar HTML
<header class="caat-navbar" data-analytics-component="navigation">
<div class="utility-bar">
<div class="container d-flex justify-content-end gap-3">
<a href="/contact">Contact us</a>
<a href="#" class="lang-switch" aria-label="Switch language to French" lang="fr" hreflang="fr">Français</a>
</div>
</div>
<nav class="navbar navbar-expand navbar-main" aria-label="Main navigation">
<div class="container">
<a class="navbar-brand" href="/">
<img src="/assets/images/CAAT_RGB.png" alt="CAAT Pension Plan">
</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav me-auto">
<li class="nav-item dropdown">
<div class="nav-link-group">
<a class="nav-link" href="/pension-solutions">Pension solutions</a>
<button class="nav-link nav-link-toggle dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false" aria-label="Open Pension solutions menu"></button>
</div>
<div class="dropdown-menu mega-menu">
<div class="container">
<div class="mega-columns">
<div>
<!-- L2: clickable column header -->
<a class="mega-category-title" href="/pension-solutions/plans">
<i class="bi bi-shield-check"></i> Our Plans
</a>
<!-- L3: plain links — labels only, no descriptions, never L4 -->
<ul class="mega-links">
<li><a href="#">DBplus</a></li>
<li><a href="#">DBprime</a></li>
</ul>
</div>
<div class="mega-promo">
<p class="mega-promo-title">Ready to join?</p>
<p class="mega-promo-text">Learn more...</p>
<a href="#" class="btn btn-sm btn-primary">Get started</a>
</div>
</div>
</div>
</div>
</li>
<li class="nav-item">
<a class="nav-link" href="/about">About us</a>
</li>
</ul>
<div class="nav-actions">
<div class="nav-search-wrap">
<input type="search" class="nav-search-input"
placeholder="Search CAAT" aria-label="Search">
<i class="bi bi-search"></i>
</div>
<div class="caat-dropdown dropdown">
<button class="nav-login dropdown-toggle" type="button"
data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-person-circle" aria-hidden="true"></i> Login
</button>
<ul class="dropdown-menu dropdown-menu-end caat-dropdown__menu">
<li><a class="dropdown-item caat-dropdown__item" href="/member-portal">Member portal</a></li>
<li><a class="dropdown-item caat-dropdown__item" href="/employer-portal">Employer Portal</a></li>
</ul>
</div>
</div>
</div>
</div>
</nav>
</header>
Footer markup
The footer is a separate component — see its full markup and contract on the Footer page.
Mobile drill-down — live demo
Scotiabank-style panel drill-down: tap a parent to slide into its child panel (down to Level 4), Back returns one level, each panel offers a section-overview link. Try it:
Mobile Menu HTML
<div class="caat-mobile-nav-demo">
<div class="caat-mobile-topbar">
<a href="/">
<img src="/assets/images/caat-logo.jpg" alt="CAAT Pension Plan">
</a>
<button class="caat-menu-button" type="button"
aria-expanded="false" aria-controls="mobileOffcanvas"
aria-label="Open menu">
<i class="bi bi-list" aria-hidden="true"></i> Menu
</button>
</div>
<div class="offcanvas offcanvas-end" id="mobileOffcanvas"
tabindex="-1" aria-labelledby="mobileOffcanvasLabel">
<div class="offcanvas-header">
<h5 id="mobileOffcanvasLabel">Menu</h5>
<button type="button" class="btn-close"
data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body caat-drillnav" data-caat-drillnav>
<div class="caat-mobile-search">
<input type="search" class="form-control"
placeholder="Search CAAT" aria-label="Search CAAT">
<i class="bi bi-search" aria-hidden="true"></i>
</div>
<!-- ROOT panel: L1 — items with children use a drill trigger -->
<div class="caat-drillnav__panel">
<ul class="caat-drillnav__list">
<li>
<button class="caat-drillnav__next" type="button"
data-target="dn-pension" aria-expanded="false">
Pension solutions <i class="bi bi-chevron-right" aria-hidden="true"></i>
</button>
</li>
<li><a class="caat-drillnav__link" href="/about">About CAAT</a></li>
</ul>
</div>
<!-- L2 panel — Back + section overview link + children -->
<div class="caat-drillnav__panel" id="dn-pension">
<button class="caat-drillnav__back" type="button">
<i class="bi bi-chevron-left" aria-hidden="true"></i> Back
</button>
<a class="caat-drillnav__overview" href="/pension-solutions">Pension solutions</a>
<ul class="caat-drillnav__list">
<li>
<button class="caat-drillnav__next" type="button"
data-target="dn-dbplus" aria-expanded="false">
DBplus <i class="bi bi-chevron-right" aria-hidden="true"></i>
</button>
</li>
<li><a class="caat-drillnav__link" href="…">Why CAAT</a></li>
</ul>
</div>
<!-- L3 panel — its links are L4 pages (deepest level) -->
<div class="caat-drillnav__panel" id="dn-dbplus">
<button class="caat-drillnav__back" type="button">
<i class="bi bi-chevron-left" aria-hidden="true"></i> Back
</button>
<a class="caat-drillnav__overview" href="…">DBplus</a>
<ul class="caat-drillnav__list">
<li><a class="caat-drillnav__link" href="…">How DBplus works</a></li>
<li><a class="caat-drillnav__link" href="…" aria-current="page">Contribution rates</a></li>
</ul>
</div>
</div>
</div>
</div>
1. Component Summary
| Component name | Navigation |
|---|---|
| AEM component name | caat/components/navigation (covers header, footer, and mobile-nav as experience fragments) |
| Recommended implementation | AEM Experience Fragments for Header and Footer, referenced globally via template policies |
| Component group | CAAT Design System |
| Purpose | Provide consistent global navigation, wayfinding, and legal compliance across all pages |
| Existing AEM components replaced | Header (#39), Footer (#37) |
| AEM triage | Rebuild Rebuild to new brand |
Primary use cases
- Site header — desktop navbar with utility bar, 25% larger logo, mega-menu dropdowns (up to 4 columns, depth to L3), search, and a Login dropdown for the member and employer portals
- Mega menu panels — full-width dropdowns with categorised columns, nested links, optional promo cards, and L2-first keyboard tab order
- Site footer — link columns, legal links, social icons, and copyright
- Mobile navigation — offcanvas drill-down menu: one panel per level, forward to L4, Back returns a level (navigation.js)
- Utility navigation — secondary links (Contact, Support, Newsroom, Events, Careers)
- Language toggle — English / Français switcher with proper
langattribute
Non-goals
- Not for in-page section navigation (use Table of Contents component instead)
- Not for breadcrumbs (separate Breadcrumb component)
- Does not manage client-side routing or SPA behaviour
2. Design System Source / Token Dependencies
| Token | Value (reference) | Purpose |
|---|---|---|
--caat-blue-900 | #003750 | Primary link hover, mobile menu button border, CTA background |
--caat-blue-100 | #e7f4fb | Mobile panel gradient end, social icon hover background |
--caat-blue-300 | #9dd2ed | Mobile link hover border, search icon colour |
--caat-grey-200 | #dfe6ef | Borders on navbar, footer, mobile links |
--caat-lime | #95e35c | Footer heading indicator dot |
--caat-green | #55a546 | CTA alternate variant |
--caat-radius-lg | 1.25rem | Container border radius for nav sections |
--caat-shadow-sm | 0 1px 3px rgba(0,0,0,.08) | Subtle shadow on nav containers |
--caat-focus | 0 0 0 .25rem rgba(47,149,210,.35) | Focus ring on all interactive elements |
--caat-muted | #506273 | Search placeholder text |
--caat-ink | #102637 | Default text colour for mobile links |
--caat-font-primary | "Libre Franklin", Arial, sans-serif | Navigation typeface |
Visual variants
- Desktop header — full-width navbar with dark utility bar, enlarged logo, mega-menu dropdown items (L1–L3), search pill, and a Login dropdown linking to both portals
- Mega menu panel — full-width dropdown with up to 4 categorised columns containing L2 headings and L3 links, plus an optional promotional card
- Mobile header — compact topbar with logo and hamburger toggle, offcanvas drill-down panels (to L4, with Back)
- Full footer — 4-column link grid with legal row, social icons, copyright
- Campaign footer — simplified single-row footer for landing pages (legal links + copyright only)
3. Authoring Fields
Header tab
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
| Logo image | Image (DAM) | Yes | — | CAAT logo. Rendered as <img> with alt text. |
| Primary nav links | Multifield (nested) | Yes | — | Each L1 item: Label + URL + hasMegaMenu (toggle). If mega menu enabled, add L2 Categories multifield (title + URL + icon — the column header is a link) with an L3 Links multifield [label + URL only — no descriptions, no L4 sub-links]. Min 3, max 7 L1 items. |
| Mega menu promo card | Composite | No | — | Per L1 mega item: Title (text) + Description (textarea) + CTA label + CTA URL. Renders as a featured card in the last column of the mega panel. |
| Utility nav links | Multifield | No | — | Each item: Label (text) + URL (pathfield). Rendered above main row. |
| Search enabled | Toggle | No | On | Show/hide the search field in the navbar. |
| Member portal URL | Path browser | Yes | — | Destination for the “Member portal” item in the Login dropdown. |
| Employer Portal URL | Path browser | Yes | — | Destination for the “Employer Portal” item in the Login dropdown. |
| Language toggle URLs | Multifield | No | — | Each item: Language code + URL. Uses lang attribute. |
Footer tab
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
| Link columns | Multifield | Yes | — | Each column: Heading (text) + Links multifield (label + URL). Max 4 columns. |
| Legal links | Multifield | Yes | — | Each item: Label + URL. Must include Privacy, Terms of use, Accessibility. |
| Social links | Multifield | No | — | Each item: Platform (dropdown) + URL. Provides icon and aria-label automatically. |
| Logo image | Image (DAM) | Yes | — | Footer logo. Typically same as header. |
| Copyright text | Text field | Yes | — | e.g. "© 2026 CAAT Pension Plan. All rights reserved." |
Mobile tab
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
| Inherits from header | — | — | — | Primary links, utility links, search, portal Login dropdown, and language toggle are inherited from the Header tab. |
| Offcanvas position | Select | No | End (right) | start | end. Controls which side the offcanvas panel slides in from. |
4. Authoring Validation Rules
- Logo image is required for both header and footer. The dialog must not allow saving without it.
- Primary nav links must contain at least 3 items. Display a validation error if fewer are provided.
- Footer legal links must include entries for Privacy, Terms of use, and Accessibility. Warn if any are missing.
- Copyright text is required. The dialog must not allow saving without it.
- Social links must each have a valid URL and an
aria-labelgenerated from the selected platform name. - Both portal URLs are required so the Login dropdown always contains “Member portal” and “Employer Portal”.
- Primary nav link labels should not exceed 25 characters each. Display a warning above 20 characters.
- Mega menu columns: a menu may contain at most 4 link groups (columns). The dialog rejects a 5th group.
- Mega menu depth: entries go no deeper than Level 3 (column header = clickable L2 link, list links = L3 labels only — no descriptions). L4 pages never appear in the mega menu — they are reached via the Side Navigation, breadcrumb, and the mobile drill-down.
- Mega menu keyboard order: inside each open desktop panel, Tab reaches every clickable L2 column header first, then continues through L3 links and promo links.
- Mobile drill-down depth: panels mirror the full tree to Level 4; every drilled panel must include the Back control and a section-overview link.
5. Content Guidance
Good examples
- Navigation labels consistent with site map
- Short, scannable utility link labels
- Visible "Menu" text label on mobile hamburger button
- Footer columns match primary site sections
- Language toggle clearly identifies target language
- Split L1 items keep the text link navigable while the adjacent caret controls the mega panel
Avoid
- Hamburger icon without visible text label
- Removing legal links from campaign pages without approval
- Inconsistent nav labels between header and footer
- More than 7 primary navigation items
- Using abbreviations in navigation labels
- Making the L1 text link a dropdown-only trigger; it must remain a real landing-page link
Interaction expectations
- On desktop, hovering a mega-menu L1 item opens its panel after a short intent delay.
- The L1 text remains a normal link to the section landing page. Hover must not prevent click navigation.
- The adjacent caret button toggles the same panel for pointer and keyboard users and mirrors state with
aria-expanded. - Only one mega panel may be open at a time. Moving to another L1 opens that panel and closes the previous panel.
- Hover-open is desktop-only. Tablet and mobile use the offcanvas drill-down menu.
6. Expected Semantic HTML Output
Header — Bootstrap navbar with mega menu
<header class="caat-navbar">
<!-- Utility bar -->
<div class="utility-bar">
<div class="container d-flex justify-content-end gap-3">
<a href="#">Contact us</a>
<a href="#" class="lang-switch" aria-label="Switch language to French" lang="fr" hreflang="fr">Français</a>
</div>
</div>
<!-- Main navbar -->
<nav class="navbar navbar-expand navbar-main" aria-label="Main navigation">
<div class="container">
<a class="navbar-brand" href="/">
<img src="/logo.png" alt="CAAT Pension Plan">
</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav me-auto">
<li class="nav-item dropdown">
<div class="nav-link-group">
<a class="nav-link" href="/pension-solutions">Pension solutions</a>
<button class="nav-link nav-link-toggle dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false" aria-label="Open Pension solutions menu"></button>
</div>
<div class="dropdown-menu mega-menu">
<div class="container">
<div class="mega-columns">
<div>
<a class="mega-category-title" href="…">Our Plans</a> <!-- L2, clickable -->
<ul class="mega-links"> <!-- L3, labels only -->
<li><a href="#">DBplus</a></li>
</ul>
</div>
<div class="mega-promo">…</div>
</div>
</div>
</div>
</li>
<li class="nav-item"><a class="nav-link" href="#">About</a></li>
</ul>
<div class="nav-actions">
<div class="nav-search-wrap">
<input type="search" class="nav-search-input">
<i class="bi bi-search"></i>
</div>
<div class="caat-dropdown dropdown">
<button class="nav-login dropdown-toggle" type="button"
data-bs-toggle="dropdown" aria-expanded="false">Login</button>
<ul class="dropdown-menu dropdown-menu-end caat-dropdown__menu">
<li><a class="dropdown-item caat-dropdown__item" href="/member-portal">Member portal</a></li>
<li><a class="dropdown-item caat-dropdown__item" href="/employer-portal">Employer Portal</a></li>
</ul>
</div>
</div>
</div>
</div>
</nav>
</header>
Navigation hierarchy (IA is 4 levels; the mega menu shows L1–L3)
| Level | Element | CSS class | Example |
|---|---|---|---|
| L1 | <li class="nav-item dropdown"> | .nav-item | Pension solutions, Members |
| L2 | <a class="mega-category-title" href> — clickable column header linking to the L2 landing page | .mega-category-title | Why join, Life events |
| L3 | <ul class="mega-links"> <li><a> | .mega-links a | Success stories, Estimate your pension |
| L4 | Never rendered in the mega menu. L4 pages are reached via the Side Navigation, breadcrumb, and the mobile drill-down. | ||
Footer — semantic <footer> with <nav>
<footer class="caat-footer" role="contentinfo">
<div class="container">
<div class="caat-footer__grid">
<nav class="caat-footer__col" aria-labelledby="ft-quick">
<h2 class="caat-footer__heading" id="ft-quick">Quick links</h2>
<ul class="caat-footer__list">…</ul>
</nav>
</div>
<div class="caat-footer__bottom">…</div>
</div>
</footer>
Full contract on the Footer page.
Mobile menu — offcanvas with ARIA
<button class="caat-menu-button" type="button"
data-bs-toggle="offcanvas" data-bs-target="#mobileOffcanvas"
aria-expanded="false" aria-controls="mobileOffcanvas"
aria-label="Open menu">
<i class="bi bi-list" aria-hidden="true"></i> Menu
</button>
<div class="offcanvas offcanvas-end" id="mobileOffcanvas"
tabindex="-1" aria-labelledby="mobileOffcanvasLabel">
<div class="offcanvas-header">
<h5 id="mobileOffcanvasLabel">Menu</h5>
<button type="button" class="btn-close"
data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body caat-drillnav" data-caat-drillnav>
<div class="caat-drillnav__panel">…</div> <!-- root + one panel per node, see §6 -->
</div>
</div>
7. CSS Contract
The navigation component relies on the global token layer defined in /assets/css/tokens.css.
All component-specific styles live in
/assets/css/components/navigation.css.
Primary selectors
| Selector | Purpose |
|---|---|
.caat-navbar | Top-level navbar wrapper (<header>) |
.caat-navbar .utility-bar | Dark, right-aligned utility links bar (top row) |
.caat-navbar .navbar-main | Main nav row (Bootstrap .navbar) |
.caat-navbar .mega-menu | Full-width mega dropdown panel (extends .dropdown-menu) |
.mega-columns | CSS Grid container for mega menu columns |
.mega-category-title | Clickable L2 column header (<a> to the L2 landing page) — icon + lime underline |
.mega-links | L3 link list within a mega column |
.mega-promo | Optional promotional card inside mega panel |
.nav-actions | Right-side actions: search and the composed Dropdown component for portal login |
.nav-search-input | Pill-shaped search field (expands on focus) |
| (footer) | Moved to the standalone .caat-footer component |
.caat-mobile-nav-demo | Mobile navigation demo container |
.caat-drillnav [data-caat-drillnav] | Mobile drill-down container (offcanvas body) |
.caat-drillnav__panel | One panel per node with children; first = root (L1) |
.caat-drillnav__next / __back | Drill trigger (data-target, aria-expanded) / back control |
.caat-drillnav__overview / __link | Section landing link at panel top / leaf link |
var(--caat-blue-900))
so that theme changes propagate automatically.
8. Accessibility Requirements
| Requirement | Acceptance criteria |
|---|---|
| Skip link | A "Skip to main content" link must be the first focusable element on the page, targeting the #main landmark. |
| Keyboard navigation | All interactive elements (links, buttons, search input) must be reachable and operable via Tab, Enter, Space, and Escape (to close offcanvas). Inside desktop mega menus, Tab moves across all L2 category links first, then through the L3 links and promo links. |
| Visible focus states | A visible focus ring (var(--caat-focus)) must appear on :focus-visible for all interactive elements. The ring must meet a 3 : 1 contrast ratio. |
| Mobile toggle — aria-expanded | The mobile Menu button must toggle aria-expanded between true and false. It must reference the offcanvas panel via aria-controls. |
| Social link labels | Every social icon link must have an aria-label (e.g. "CAAT on LinkedIn"). The icon must have aria-hidden="true". |
| Language toggle | The language toggle link must spell out the language names and use the lang attribute matching the target language (e.g. lang="fr"). |
| Nav landmarks | Each <nav> must have a unique aria-label (e.g. "Main navigation", "Footer navigation", "Legal links"). |
| Colour contrast | All text and interactive elements must meet WCAG 2.1 AA (4.5 : 1 normal text, 3 : 1 large text) in all states. |
Behaviour contract
Bootstrap 5 handles offcanvas lifecycle and the Login dropdown. navigation.js handles the split-link desktop mega menus, mobile drill-down panels, and desktop mega-menu tab order. Verify the behaviours below survive any AEM integration.
| Behaviour | Mechanism & acceptance criteria |
|---|---|
| Mobile menu | Below lg (992 px) the bar collapses behind a Menu button that opens the offcanvas (aria-expanded + aria-controls). Above it, the full bar renders and the toggler is hidden. |
| Mobile drill-down (navigation.js) | Inside the offcanvas: .caat-drillnav__next reveals the child panel (to L4) and focuses its Back control; .caat-drillnav__back returns one level and restores focus to the trigger; Escape in a sub-panel goes back one level (closes the offcanvas only from the root); closing the offcanvas resets to the root panel. Hidden panels use the hidden attribute; triggers mirror state via aria-expanded. |
| Desktop hover-open | On viewports at or above lg (992 px), pointer hover on a mega-menu L1 item opens its panel after a short intent delay. The panel remains open while the pointer is over the L1 item or its mega panel, then closes after a short delay when the pointer leaves both. Hover must not apply below lg. |
| Mega menu caret toggle | L1 text links navigate to the landing page. The adjacent caret button toggles the same mega panel on click/Enter/Space and mirrors state with aria-expanded. navigation.js closes the panel on Escape with focus returned to the caret, closes on outside click, and keeps only one panel open at a time. |
| Keyboard inside menus | Inside open desktop mega menus, navigation.js intercepts Tab so focus traverses all .mega-category-title L2 links across the panel first, then continues through L3 links and promo links. Shift+Tab reverses that order. |
| Sticky header | sticky-top (z-index 1020 = --caat-z-sticky). Anchor targets stay visible beneath it via the global scroll-padding-top: 5rem (caat-base.css). |
| Current page | Set aria-current="page" + the .active class on the current L1 link (server-rendered, not JS-derived). |
9. SEO Requirements
- All navigation links must be rendered as crawlable
<a>elements with validhrefattributes. No JavaScript-only navigation. - Link text must be descriptive and unique within the page. Avoid generic phrases like "Click here".
- Maintain a canonical navigation structure — the same primary links should appear on all pages for consistent internal linking.
- Footer links contribute to the site's internal link graph. Ensure all footer URLs resolve correctly (no broken links).
- The logo link should point to the site root (
/) to reinforce the homepage's authority.
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 Header (#39) and Footer (#37) in the existing AEM component library.
AEM triage
Rebuild Rebuild to new brand standards. The existing header and footer components do not meet current accessibility, design-token, or responsive requirements.
Experience Fragment approach
Implement the header and footer as AEM Experience Fragments. Each fragment is authored once and referenced globally via template policies, ensuring consistent navigation across all pages without per-page authoring.
- Header XF: contains the desktop mega-menu navbar (Bootstrap 5.3
navbar-expandwith.dropdown-menu.mega-menupanels). Mobile nav uses Bootstrap offcanvas + the drill-down panels;navigation.jsalso normalizes desktop mega-menu tab order. - Footer XF: contains the full footer; campaign pages may reference a simplified campaign-footer XF variant.
Client library structure
- Category:
caat.components.navigation - Dependencies:
caat.base(tokens + base styles),caat.components.button, andcaat.components.dropdownfor the portal Login control - CSS:
navigation.css - JS:
navigation.js(Bootstrap 5.3 bundle handles dropdown/offcanvas show-hide natively; navigation.js normalizes desktop mega-menu tab order and implements the mobile drill-down — panel stack, Back, focus management, reset on close)
Suggested file structure
experience-fragments/
├── header/
│ ├── master/
│ │ ├── _cq_dialog/
│ │ │ └── .content.xml # Header XF dialog
│ │ ├── .content.xml # XF node definition
│ │ └── header.html # HTL template
│ └── variations/
│ └── campaign-header/
│ └── .content.xml
├── footer/
│ ├── master/
│ │ ├── _cq_dialog/
│ │ │ └── .content.xml # Footer XF dialog
│ │ ├── .content.xml
│ │ └── footer.html
│ └── variations/
│ └── campaign-footer/
│ └── .content.xml
└── clientlibs/
├── .content.xml
├── css/
│ └── navigation.css
└── js/
└── navigation.js
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/experience-fragments/header — 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:
# ── Header tab ───────────────────────────────
header:
jcr:title: Header
items:
logoImage:
sling:resourceType: granite/ui/components/coral/foundation/form/pathfield
name: ./logoImage
fieldLabel: Logo image
required: true
rootPath: /content/dam
primaryNavLinks:
sling:resourceType: granite/ui/components/coral/foundation/form/multifield
name: ./primaryNavLinks
fieldLabel: Primary nav links
required: true
composite: true
items:
label:
sling:resourceType: granite/ui/components/coral/foundation/form/textfield
name: ./label
fieldLabel: Label
required: true
maxlength: 25
url:
sling:resourceType: granite/ui/components/coral/foundation/form/pathfield
name: ./url
fieldLabel: URL
required: true
utilityNavLinks:
sling:resourceType: granite/ui/components/coral/foundation/form/multifield
name: ./utilityNavLinks
fieldLabel: Utility nav links
composite: true
items:
label:
sling:resourceType: granite/ui/components/coral/foundation/form/textfield
name: ./label
fieldLabel: Label
url:
sling:resourceType: granite/ui/components/coral/foundation/form/pathfield
name: ./url
fieldLabel: URL
searchEnabled:
sling:resourceType: granite/ui/components/coral/foundation/form/switch
name: ./searchEnabled
fieldLabel: Search enabled
checked: true
memberPortalURL:
sling:resourceType: granite/ui/components/coral/foundation/form/pathfield
name: ./memberPortalURL
fieldLabel: Member portal URL
required: true
employerPortalURL:
sling:resourceType: granite/ui/components/coral/foundation/form/pathfield
name: ./employerPortalURL
fieldLabel: Employer Portal URL
required: true
languageToggleURLs:
sling:resourceType: granite/ui/components/coral/foundation/form/multifield
name: ./languageToggleURLs
fieldLabel: Language toggle URLs
composite: true
items:
langCode:
sling:resourceType: granite/ui/components/coral/foundation/form/textfield
name: ./langCode
fieldLabel: Language code
url:
sling:resourceType: granite/ui/components/coral/foundation/form/pathfield
name: ./url
fieldLabel: URL
# ── Mobile tab ───────────────────────────────
mobile:
jcr:title: Mobile
items:
offcanvasPosition:
sling:resourceType: granite/ui/components/coral/foundation/form/select
name: ./offcanvasPosition
fieldLabel: Offcanvas position
items:
- { text: End (right), value: end }
- { text: Start (left), value: start }
---
# caat/experience-fragments/footer — 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:
# ── Footer tab ──────────────────────────────
footer:
jcr:title: Footer
items:
linkColumns:
sling:resourceType: granite/ui/components/coral/foundation/form/multifield
name: ./linkColumns
fieldLabel: Link columns
required: true
composite: true
items:
heading:
sling:resourceType: granite/ui/components/coral/foundation/form/textfield
name: ./heading
fieldLabel: Column heading
required: true
links:
sling:resourceType: granite/ui/components/coral/foundation/form/multifield
name: ./links
fieldLabel: Links
composite: true
items:
label:
sling:resourceType: granite/ui/components/coral/foundation/form/textfield
name: ./label
fieldLabel: Label
url:
sling:resourceType: granite/ui/components/coral/foundation/form/pathfield
name: ./url
fieldLabel: URL
legalLinks:
sling:resourceType: granite/ui/components/coral/foundation/form/multifield
name: ./legalLinks
fieldLabel: Legal links
required: true
composite: true
items:
label:
sling:resourceType: granite/ui/components/coral/foundation/form/textfield
name: ./label
fieldLabel: Label
required: true
url:
sling:resourceType: granite/ui/components/coral/foundation/form/pathfield
name: ./url
fieldLabel: URL
required: true
socialLinks:
sling:resourceType: granite/ui/components/coral/foundation/form/multifield
name: ./socialLinks
fieldLabel: Social links
composite: true
items:
platform:
sling:resourceType: granite/ui/components/coral/foundation/form/select
name: ./platform
fieldLabel: Platform
items:
- { text: LinkedIn, value: linkedin }
- { text: YouTube, value: youtube }
- { text: Twitter / X, value: twitter }
- { text: Facebook, value: facebook }
- { text: Instagram, value: instagram }
url:
sling:resourceType: granite/ui/components/coral/foundation/form/textfield
name: ./url
fieldLabel: URL
footerLogo:
sling:resourceType: granite/ui/components/coral/foundation/form/pathfield
name: ./footerLogo
fieldLabel: Logo image
required: true
rootPath: /content/dam
copyrightText:
sling:resourceType: granite/ui/components/coral/foundation/form/textfield
name: ./copyrightText
fieldLabel: Copyright text
required: true
13. QA Acceptance Checklist
- Desktop navbar renders correctly: dark right-aligned utility bar, enlarged logo, split L1 navigation links, search pill, Login dropdown, and language toggle are visible and properly aligned; no Join DBplus button renders.
- Login uses the Dropdown component and contains exactly two links: “Member portal” and “Employer Portal”.
- Desktop mega menu panels open on L1 hover and on caret click/keyboard activation. The L1 text link remains clickable and navigates to its landing page.
- Mega menu panels display categorised columns (L2 headings and L3 links only) and an optional promo card.
- Mega menu closes on pointer leave, outside click, Escape key, or opening another L1 item. Only one panel is open at a time.
- Mobile menu opens via hamburger toggle; the search field appears at the top of the drawer; drill-down panels reach L4 with a working Back control, focus moves into each revealed panel, and the menu resets to root on close.
- Full footer renders all 4 link columns, legal links (including Privacy, Terms of use, Accessibility), social icons, logo, and copyright.
- Responsive behaviour: navbar wraps gracefully at tablet and mobile breakpoints; footer collapses to 2-column and then 1-column; mobile nav is hidden on desktop.
- Keyboard navigation works: all links and buttons reachable via Tab; desktop mega menu focus moves across L2 category links before L3 links; offcanvas closes with Escape; focus is trapped inside offcanvas when open.
- All links resolve correctly — no broken links in header, footer, or mobile menu. External links open in new tab with
rel="noopener". - Legal compliance: Privacy, Terms of use, and Accessibility links are present in the footer on all page templates including campaign pages.
- Language toggle switches to the correct locale URL and uses the
langattribute on the toggle element.
14. Definition of Done
| Area | Done means |
|---|---|
| Design | Figma component matches this contract. Desktop navbar, mobile menu, full footer, and campaign footer variants are documented. All tokens are used — no hard-coded colour values. |
| Development | AEM Experience Fragments created for header and footer. HTL templates emit semantic HTML per Section 6. CSS uses design tokens. JS wires analytics events and mobile offcanvas. Unit tests pass. |
| QA | All items in the QA Acceptance Checklist (Section 13) pass, including desktop hover-open, caret toggle, L1 link navigation, L2-first mega-menu keyboard order, mobile drill-down focus restoration, and breadcrumb/side-nav handoff checks. Manual accessibility audit completed. Cross-browser tested (Chrome, Firefox, Safari, Edge). Responsive tested at 320px, 768px, 1024px, 1440px. |
| Launch | Experience Fragments deployed to AEM production. Template policies configured to reference header/footer globally. Content authors trained. Analytics dashboards confirmed receiving nav-click, footer-click, and mobile-menu-open events. |