Component Contract

Side Navigation

Left-hand navigation showing only the current Level 3 branch’s Level 4 pages. Users move to higher levels through the mega navigation or breadcrumb (see the Navigation Model), so L2 and L3 links are not repeated here.

Live Demo

How it fits the navigation flow

StepSurfaceWhat the user sees
1Mega navOpens an L1 panel, clicks an L3 link (the deepest level the mega nav shows) — e.g. Pension solutions → Why join → What members get
2L3 pageLands on the L3 page: the breadcrumb shows the trail and the side nav lists only that page’s L4 children
3Side navClicks an L4 link (e.g. Secure lifetime retirement income)
4L4 pageThe same flat L4 list persists and the current page is marked aria-current="page"; the breadcrumb extends to the L4 page

L4 page (current branch only)

An L4 page (Secure lifetime retirement income) inside Pension solutions → Why join → What members get. The side nav lists only the L4 pages beneath What members get. Below 992 px it collapses behind a prominent “In this section” disclosure toggle.

Secure lifetime retirement income

Page content for the current L4 page. The side nav includes its L4 siblings and marks only the page itself with aria-current="page". Higher-level items are available through the mega navigation and breadcrumb.

L3 page (its L4 children)

What members get

An L3 page with children shows its L4 destinations as a flat list. No item is current until the user opens an L4 page. If an L3 page has no visible L4 children, the component does not render.

States

Link interaction states per the Interaction States foundation. Hover and focus are shown statically via .is-* mirrors of the live :hover / :focus-visible rules; the current L4 page is carried by aria-current="page" (weight + rail + tint — never colour alone).

1. Summary

The Side Navigation orients users within the current L3 branch by listing only its visible L4 children. It appears on an L3 page when that page has L4 children and persists on those L4 pages. It does not repeat L2 or L3 destinations; users move to those levels through the mega navigation or breadcrumb.

StatusReady
TypeNavigation / wayfinding
JSside-nav.js — mobile disclosure toggle only (progressive enhancement: without JS the nav renders fully expanded). Desktop is pure CSS.
Used withBreadcrumb and Navigation — those surfaces handle L1–L3 movement; the side nav is the focused hand-off from the current L3 page to its L4 children

2. Design Tokens

TokenRole
--caat-color-text / --caat-color-text-headingLink / title colour
--caat-color-state-hover-bgRow hover background
--caat-color-bg-brand-subtleCurrent-item background
--caat-color-action-primaryCurrent-item left rail
--caat-color-border · --caat-border-2Branch-label divider and current-page rail
--caat-color-focus-ringKeyboard focus
--caat-font-size-xs / --caat-font-size-sm / --caat-font-size-md · --caat-weight-semibold / --caat-weight-bold / --caat-weight-extrabold · --caat-space-1 / --caat-space-2 / --caat-space-3Type and rhythm

3. Authoring Fields

Not authored per page — the component derives its content from the current L3 page or, on an L4 page, its L3 parent. The only authorable options:

FieldTypeRequiredDefaultNotes
StickyCheckboxNoOnSticky below the header on desktop (--sticky)
Hide pagesPathfield multiNoExclude specific child pages (mirrors "Hide in navigation")

4. Validation Rules

RuleBehaviour
Current L3 branch onlyRender only the visible L4 children of the current L3 page or the current L4 page’s L3 parent
Hide empty navigationDo not render the component when an L3 page has no visible L4 children
No L5The IA stops at L4; deeper pages are a content-architecture error and are not rendered
Current markingOn L4 pages, exactly one link has aria-current="page"; no ancestor link is rendered

5. Content Guidance

  • Labels are page titles (or their short navigation titles) — never rewritten copy.
  • Keep L4 lists scannable: prefer 4–8 items; if a branch needs substantially more, review the IA.
  • The current L3 label provides context as a heading, not a link. The breadcrumb provides the link back to that page.

6. Semantic HTML

<nav class="caat-side-nav caat-side-nav--sticky" aria-label="Section navigation">
  <!-- Mobile disclosure toggle (hidden on desktop; side-nav.js wires it) -->
  <button class="caat-side-nav__toggle" type="button"
          aria-expanded="false" aria-controls="sideNavBody">
    <span>In this section: What members get</span> <i class="bi bi-chevron-down" aria-hidden="true"></i>
  </button>
  <div class="caat-side-nav__body" id="sideNavBody">
  <h2 class="caat-side-nav__heading">What members get</h2> <!-- current L3 label, not a link -->
  <ul class="caat-side-nav__list">
    <li><a class="caat-side-nav__link" href="…">Value</a></li>
    <li><a class="caat-side-nav__link" href="…" aria-current="page">Secure lifetime retirement income</a></li>
    <li><a class="caat-side-nav__link" href="…">Early retirement options</a></li>
  </ul>
  </div>
</nav>

Place inside the page grid: col-lg-3 for the nav, col-lg-9 for content. Below 992 px the grid stacks the nav above the content (Responsive foundation). Each rendered instance must use a unique aria-controls / body id pair.

7. CSS Contract

All styles in /assets/css/components/side-nav.css.

ClassPurpose
.caat-side-navRoot <nav>
.caat-side-nav--stickyModifier: sticky below header on desktop
.caat-side-nav__headingCurrent L3 branch label; contextual heading, not a link
.caat-side-nav__listFlat list containing only the current branch’s L4 pages
.caat-side-nav__linkNav link; [aria-current] styles the active rail
.caat-side-nav__toggle / __bodyMobile disclosure: prominent “In this section” button (hidden ≥ 992 px) and the collapsible wrapper it controls
.caat-side-nav--collapsible / .is-openAdded by side-nav.js; only --collapsible allows CSS to collapse the body (no-JS fallback = expanded)

8. Accessibility

RequirementImplementation
Landmark<nav aria-label="Section navigation"> — unique label, distinct from "Main navigation" and "Breadcrumb"
Current pagearia-current="page" on the current L4 link; colour is never the only cue (weight + rail)
KeyboardPlain links — Tab order follows the visual order; visible focus ring per Interaction States
StructureA heading identifies the current L3 branch and a single <ul> contains its L4 links; no ARIA tree role is needed
Mobile disclosureThe toggle is a real <button> with aria-expanded + aria-controls; it names the current L3 branch (“In this section: What members get”), is ≥ 44 px tall, uses a unique controlled body id, and is deliberately prominent

9. SEO

Server-rendered crawlable links connect the sibling L4 pages within the current L3 branch. Higher-level internal links remain in the breadcrumb and global navigation.

10. Analytics

data-analytics-component="side-nav"
data-analytics-label="{link text}"     <!-- per link, kebab-case -->
data-analytics-level="4"

11. AEM Implementation Notes

  • Resource type: caat/components/side-nav; include in an L3 page template when visible L4 children exist and on its L4 page template — not authorable into other parsys positions.
  • Tree is computed from the current page path: use the current page when it is L3, otherwise ascend from the current L4 page to its L3 parent; list only that L3 page’s visible children.
  • Navigation titles (jcr:title / nav title override) supply labels.
  • Include the side-nav.js clientlib only on templates where the side nav can render; it adds the mobile disclosure behavior while preserving the expanded no-JS fallback.

12. Dialog Model

Minimal — content is tree-derived, not authored.

{
  "sticky":    { "sling:resourceType": "granite/ui/components/coral/foundation/form/checkbox", "name": "./sticky", "text": "Stick below header on desktop", "checked": true },
  "hidePages": { "sling:resourceType": "granite/ui/components/coral/foundation/form/multifield", "name": "./hidePages", "fieldLabel": "Hide pages" }
}

13. QA Acceptance Checklist

  • Renders on an L3 page only when it has visible L4 children, and on those L4 pages; absent elsewhere.
  • Exactly one aria-current="page" on an L4 page; no L2 or L3 navigation links render.
  • Only the current L3 branch’s L4 items appear in one flat list.
  • Below 992 px: collapses behind the “In this section” toggle (unique aria-controls target, aria-expanded reflects state); without JS the nav renders expanded; sticky on desktop when enabled.
  • Focus ring visible on every link; hover/current styles match Interaction States and never rely on colour alone.
  • Flow verified: mega nav L3 link → L3 page shows only its L4 children → L4 page keeps the same flat list and marks the current page.
  • Labels match page navigation titles; hidden pages excluded.

14. Definition of Done

  • Tree derivation verified against the content architecture (current L3 branch’s visible L4 children only).
  • Breadcrumb + side nav present together where the side nav is eligible; mega navigation unchanged.
  • Mobile disclosure and no-JS expanded fallback verified at the responsive breakpoint.
  • QA checklist passes on an L3 page with children, an L3 page without children, an L4 page, and a hidden-page case.