Component Contract

Accordion

A progressive-disclosure component for FAQ, plan details, and help content in CAAT digital properties.

Live Demo

Standard Accordion

Rendered

The CAAT DBplus pension plan is a defined benefit pension plan that provides a predictable, lifetime retirement income based on your contributions and years of service. It is available to employees of participating employers across Canada.

Contributions are automatically deducted from your pay. Both you and your employer contribute a percentage of your earnings. The contribution rates are set by the plan and reviewed periodically to ensure the plan remains fully funded.

You can start receiving an unreduced pension as early as age 60, or a reduced pension from age 55. The exact amount depends on your years of service and average earnings. Use the pension estimator tool to see your projected benefits.

Flush Variant

Rendered

Vesting means you have earned the right to receive a pension from the plan. In CAAT DBplus, you are immediately vested — your pension benefit is yours from day one of membership.

If your new employer also participates in CAAT DBplus, your pension continues to grow seamlessly. If they don't, your earned pension stays with CAAT and will be paid to you at retirement.

Always-Open Variant

Rendered

If you pass away, your spouse or beneficiary may be entitled to receive a survivor pension or a lump-sum death benefit, depending on your membership status and elections.

Yes. CAAT DBplus provides conditional inflation protection. The plan's board reviews the funding status each year and may grant increases to help your pension keep pace with the cost of living.

States

Follows the Interaction States matrix. The accordion button implements collapsed (default), expanded (selected), and focus; hover is subtle and disabled does not apply.

Rendered

The CAAT DBplus pension plan is a defined benefit pension plan that provides a predictable, lifetime retirement income based on your contributions and years of service.

Code sample

HTML

<!-- Standard accordion -->
<div class="accordion caat-accordion" id="faqAccordion"
     data-analytics-component="accordion">
  <div class="accordion-item">
    <h3 class="accordion-header" id="faqHeading1">
      <button class="accordion-button" type="button"
              data-bs-toggle="collapse"
              data-bs-target="#faqCollapse1"
              aria-expanded="true"
              aria-controls="faqCollapse1"
              data-analytics-label="question-1">
        What is the CAAT DBplus pension plan?
      </button>
    </h3>
    <div id="faqCollapse1" class="accordion-collapse collapse show"
         aria-labelledby="faqHeading1"
         data-bs-parent="#faqAccordion">
      <div class="accordion-body">
        Your answer content here.
      </div>
    </div>
  </div>
</div>

<!-- Flush variant — add .accordion-flush -->
<div class="accordion accordion-flush caat-accordion" id="flushAccordion">
  <!-- items … -->
</div>

<!-- Always-open — omit data-bs-parent -->
<div class="accordion caat-accordion" id="openAccordion">
  <div class="accordion-item">
    <h3 class="accordion-header" id="openHeading1">
      <button class="accordion-button" type="button"
              data-bs-toggle="collapse"
              data-bs-target="#openCollapse1"
              aria-expanded="true"
              aria-controls="openCollapse1">
        Question text
      </button>
    </h3>
    <div id="openCollapse1" class="accordion-collapse collapse show"
         aria-labelledby="openHeading1">
      <div class="accordion-body">Answer text</div>
    </div>
  </div>
</div>

1. Component Summary

Component nameAccordion
AEM component namecaat/components/accordion
Recommended implementationExtend the AEM Core Accordion component (core/wcm/components/accordion)
Component groupCAAT Design System
PurposeProgressive disclosure for FAQ, plan details, and help content
Existing AEM components replaced CAAT Accordion (#3), CAAT Vue Accordion (#19), CAAT Estimators Help (#14), CAAT Estimators Help Vue (#15). Consolidates 4 existing components into 1.

Primary use cases

  1. FAQ pages — question-and-answer pairs with optional FAQ schema markup
  2. Plan detail explanations — progressive disclosure of pension plan features
  3. Help and guidance sections — contextual help within tools and calculators
  4. Terms and conditions — collapsible legal sections
  5. Estimator help content — replacing the existing Vue-based help accordions

Non-goals

  1. This component is not for navigation — do not use it as a menu or nav structure
  2. It is not for sequential wizards or stepped processes (use a Stepper component)
  3. It must not be used to hide essential legal disclosures that members are required to see

2. Design System Source / Token Dependencies

TokenValue (reference)Purpose
--caat-blue-900#003750Accordion button text colour, active indicator
--caat-blue-100#e7f4fbExpanded header background tint
--caat-blue-300#9dd2edAccordion border accent
--caat-grey-200#dfe6efItem divider border colour
--caat-focus0 0 0 .25rem rgba(47,149,210,.35)Focus ring on accordion buttons
--caat-font-primary"Libre Franklin", Arial, sans-serifAccordion typeface

Visual variants

  • Standard — bordered accordion with background tint on expanded header
  • Flush (.accordion-flush) — no outer borders or rounded corners, sits flush with surrounding content
  • Always-open — multiple items can be expanded simultaneously (no data-bs-parent)
  • Nested / FAQ with schema — standard accordion with itemscope and itemprop attributes for FAQ structured data

3. Authoring Fields

Content tab

FieldTypeRequiredDefaultNotes
Heading text (per item)Text fieldYesVisible label for the accordion trigger button.
Body content (per item)Rich text editorYesSupports paragraphs, lists, links, images, and embedded components.
Heading levelDropdownNoh3h2 | h3 | h4 | h5. Sets the semantic heading wrapping each accordion button.
Expanded by defaultCheckbox (per item)NoOffWhen checked, the item renders in the expanded state on page load.

Style tab

FieldTypeRequiredDefaultNotes
VariantDropdownYesStandardstandard | flush | always-open
Single item expandToggleNoOnWhen on, only one item can be open at a time (sets data-bs-parent). Disabled when variant is "always-open".

Behaviour tab

FieldTypeRequiredDefaultNotes
FAQ schema outputCheckboxNoOffWhen checked, renders FAQ structured data (itemscope/itemprop) for eligible pages.
Analytics trackingToggleNoOnWhen on, emits data-analytics-* attributes and wires expand/collapse events.

4. Authoring Validation Rules

  1. At least one accordion item is required. The dialog must not allow saving an empty accordion.
  2. Heading text is required for every item. The dialog must not allow saving an item without a heading.
  3. Authors must never hide essential legal disclosures solely within collapsed accordion panels — such content must be visible by default or presented outside the accordion.
  4. FAQ schema output should only be enabled when the content is genuinely FAQ-eligible (question-and-answer pairs on a page primarily about answering questions).

5. Content Guidance

Good examples

  • Clear, question-style headings: "How do I update my beneficiary?"
  • Direct answer first, then supporting detail
  • Concise answers — 1–2 sentences when possible
  • Logically grouped items under a clear section heading
  • Use for genuinely optional detail that supports a primary narrative

Avoid

  • Vague headings: "More information", "Details", "Click here"
  • Nesting accordions more than 1 level deep
  • Hiding required action items inside collapsed panels
  • Using accordion as the only way to read critical legal content
  • Overly long body content — consider splitting into multiple items

6. Expected Semantic HTML Output

Standard accordion

<div class="accordion caat-accordion" id="faqAccordion">
  <div class="accordion-item">
    <h3 class="accordion-header" id="faqHeading1">
      <button class="accordion-button" type="button"
              data-bs-toggle="collapse"
              data-bs-target="#faqCollapse1"
              aria-expanded="true"
              aria-controls="faqCollapse1">
        Question heading text
      </button>
    </h3>
    <div id="faqCollapse1"
         class="accordion-collapse collapse show"
         aria-labelledby="faqHeading1"
         data-bs-parent="#faqAccordion">
      <div class="accordion-body">
        <p>Answer content here.</p>
      </div>
    </div>
  </div>
</div>

FAQ schema variant

<div class="accordion caat-accordion"
     itemscope itemtype="https://schema.org/FAQPage">
  <div class="accordion-item"
       itemscope itemprop="mainEntity"
       itemtype="https://schema.org/Question">
    <h3 class="accordion-header">
      <button class="accordion-button" type="button"
              data-bs-toggle="collapse"
              data-bs-target="#schemaCollapse1"
              aria-expanded="true"
              aria-controls="schemaCollapse1">
        <span itemprop="name">What is CAAT DBplus?</span>
      </button>
    </h3>
    <div id="schemaCollapse1" class="accordion-collapse collapse show">
      <div class="accordion-body"
           itemscope itemprop="acceptedAnswer"
           itemtype="https://schema.org/Answer">
        <div itemprop="text">
          <p>CAAT DBplus is a defined benefit pension plan…</p>
        </div>
      </div>
    </div>
  </div>
</div>

Flush variant

<div class="accordion accordion-flush caat-accordion" id="flushAccordion">
  <!-- Same inner structure, no outer borders -->
</div>

7. CSS Contract

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

Base styles

.caat-accordion {
  font-family: var(--caat-font-primary, "Libre Franklin", Arial, sans-serif);
}

.caat-accordion .accordion-item {
  border-color: var(--caat-grey-200, #e9ecef);
}

.caat-accordion .accordion-button {
  font-weight: 600;
  color: var(--caat-blue-900, #003750);
  background-color: transparent;
}

.caat-accordion .accordion-button:not(.collapsed) {
  background-color: var(--caat-blue-100, #e6f0f7);
  color: var(--caat-blue-900, #003750);
  box-shadow: inset 0 -1px 0 var(--caat-grey-200, #e9ecef);
}

/* Focus ring */
.caat-accordion .accordion-button:focus-visible {
  box-shadow: var(--caat-focus);
  outline: 0;
}

/* Custom chevron colour */
.caat-accordion .accordion-button::after {
  filter: brightness(0) saturate(100%);
}
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 access Accordion buttons must be reachable via Tab and toggled with Enter or Space. Arrow-key navigation between headers is optional but recommended.
Focus state A visible focus ring (var(--caat-focus)) must appear on :focus-visible. The ring must meet a 3 : 1 contrast ratio against the background.
ARIA attributes Each accordion button must have aria-expanded (true/false) and aria-controls pointing to its collapse panel id. The panel must have aria-labelledby pointing back to its header.
Heading level The heading level wrapping the accordion button must be configurable (h2–h5) so it fits the page's document outline.
Content visibility When a panel is expanded, its content must be fully accessible to screen readers. Collapsed content should be hidden from assistive technology via the Bootstrap collapse mechanism.
Colour contrast Text-to-background contrast must meet WCAG 2.1 AA (4.5 : 1 for normal text) in both collapsed and expanded states.

9. SEO Requirements

  1. Use real heading elements (h2–h5) for accordion headers — do not rely on ARIA roles alone.
  2. Enable FAQ structured data (itemscope/itemprop) only on pages with genuinely eligible FAQ content. Do not apply to every accordion.
  3. Keep answers visible to crawlers — Google can render collapsed Bootstrap accordion content, but ensure key content is in the initial HTML response.

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 consolidates and replaces:

  • CAAT Accordion (#3) — standard server-rendered accordion
  • CAAT Vue Accordion (#19) — Vue.js-based accordion
  • CAAT Estimators Help (#14) — help accordion in estimator tools
  • CAAT Estimators Help Vue (#15) — Vue.js version of estimator help

AEM triage

Rebuild The Vue-based components (#19, #14, #15) should be fully replaced with a standard AEM / Bootstrap accordion. The existing server-rendered accordion (#3) should be rebuilt to use design tokens and meet current accessibility requirements.

Proxy / extend guidance

Create a proxy component at /apps/caat/components/accordion that extends the AEM Core Accordion component (core/wcm/components/accordion) where feasible. Override the HTL template to emit the CAAT class names, data attributes, and optional FAQ schema markup.

Client library structure

  • Category: caat.components.accordion
  • Dependencies: caat.base (tokens + base styles)
  • CSS: accordion.css
  • JS: accordion.js (analytics wiring, expand/collapse event tracking)

Suggested file structure

accordion/
├── _cq_dialog/
│   └── .content.xml          # Touch UI dialog
├── _cq_editConfig.xml         # Edit configuration
├── .content.xml                # Component node definition
├── accordion.html              # HTL template
├── clientlibs/
│   ├── .content.xml
│   ├── css/
│   │   └── accordion.css
│   └── js/
│       └── accordion.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/accordion — 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:
              headingLevel:
                sling:resourceType: granite/ui/components/coral/foundation/form/select
                name: ./headingLevel
                fieldLabel: Heading level
                items:
                  - { text: H2, value: h2 }
                  - { text: H3, value: h3 }
                  - { text: H4, value: h4 }
                  - { text: H5, value: h5 }
                defaultValue: h3
              accordionItems:
                sling:resourceType: granite/ui/components/coral/foundation/form/multifield
                name: ./accordionItems
                fieldLabel: Accordion items
                composite: true
                required: true
                items:
                  field:
                    sling:resourceType: granite/ui/components/coral/foundation/container
                    items:
                      headingText:
                        sling:resourceType: granite/ui/components/coral/foundation/form/textfield
                        name: ./headingText
                        fieldLabel: Heading text
                        required: true
                      bodyContent:
                        sling:resourceType: granite/ui/components/coral/foundation/form/richtext
                        name: ./bodyContent
                        fieldLabel: Body content
                        required: true
                      expandedByDefault:
                        sling:resourceType: granite/ui/components/coral/foundation/form/checkbox
                        name: ./expandedByDefault
                        text: Expanded by default

          # ── 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: Flush,       value: flush }
                  - { text: Always open, value: always-open }
              singleItemExpand:
                sling:resourceType: granite/ui/components/coral/foundation/form/switch
                name: ./singleItemExpand
                fieldLabel: Single item expand
                checked: true

          # ── Behaviour tab ────────────────────────────
          behaviour:
            jcr:title: Behaviour
            items:
              faqSchema:
                sling:resourceType: granite/ui/components/coral/foundation/form/checkbox
                name: ./faqSchema
                text: Enable FAQ schema output
              analyticsTracking:
                sling:resourceType: granite/ui/components/coral/foundation/form/switch
                name: ./analyticsTracking
                fieldLabel: Analytics tracking
                checked: true

13. QA Acceptance Checklist

  • All three variants (standard, flush, always-open) render correctly with expected styles and spacing.
  • Expand and collapse behaviour works correctly — single-expand mode closes siblings; always-open mode allows multiple panels.
  • Keyboard navigation works: Tab to focus accordion buttons, Enter/Space to toggle.
  • aria-expanded, aria-controls, and aria-labelledby attributes are correct in all states.
  • FAQ schema markup (itemscope/itemprop) renders correctly when enabled and validates in Google's Rich Results Test.
  • Analytics data attributes are present and expand/collapse events fire correctly to the data layer.

14. Definition of Done

AreaDone means
Design Figma component matches this contract. All variants (standard, flush, always-open, FAQ schema) are documented. Tokens are used — no hard-coded colour values.
Development AEM proxy component created extending Core Accordion. HTL template emits semantic HTML per Section 6. CSS uses design tokens. JS wires analytics expand/collapse events. All 4 legacy components (#3, #19, #14, #15) are replaced. Unit tests pass.
QA All items in the QA Acceptance Checklist (Section 13) pass. Manual accessibility audit completed. Cross-browser tested (Chrome, Firefox, Safari, Edge). FAQ schema validated.
Launch Component deployed to AEM production. Legacy accordion components deprecated and migration plan communicated. Content authors trained. Analytics dashboards confirmed receiving events.