Component Contract

Tables

Present structured data — contribution rates, plan comparisons, fee schedules — in accessible tabular format for CAAT digital properties.

Live Demo

Standard Data Table — Contribution Rates

Rendered

DBplus Contribution Rates by Year
Year Member Rate (% of earnings) Employer Rate (% of earnings) Total Contribution
2024 4.0% 4.0% 8.0%
2025 4.2% 4.2% 8.4%
2026 4.4% 4.4% 8.8%
2027 4.5% 4.5% 9.0%

Responsive Stacked Table — Plan Comparison

Rendered

CAAT Pension Plan Comparison
Feature DBplus DBprime
Pension Type Defined Benefit Defined Benefit
Employer Eligibility All participating employers Select employers
Early Retirement Age 55 (reduced) / Age 60 (unreduced) Age 55 (reduced) / Age 62 (unreduced)
Inflation Protection Conditional — up to 100% CPI 75% of CPI guaranteed
Survivor Benefits 60% of lifetime pension 66.7% of lifetime pension

Sortable Table — Fee Schedule

Rendered

Investment Management Fee Schedule
Canadian Equities 18 20% S&P/TSX Composite
Global Equities 32 30% MSCI World (CAD)
Fixed Income 8 25% FTSE Canada Universe Bond
Real Assets 45 15% CPI + 4%
Private Credit 55 10% CDOR + 3%

Row states

Rendered

Row hover state
Year Member Rate Employer Rate
2024 4.0% 4.0%
2025 4.2% 4.2%
2026 4.4% 4.4%

1. Summary

Component namecaat/components/table
Status New
PurposePresent structured data (contribution rates, plan comparisons, fee schedules) in accessible tabular format with responsive behaviour and optional sorting.
VariantsStandard (striped/hover), Responsive stacked (cards on mobile), Sortable
ReplacesN/A — new component

2. Design Tokens

Tables consume these semantic tokens — no component-specific tokens are defined.

TokenRole
--caat-color-borderCell borders
--caat-color-bg-brandHeader row background (dark variant)
--caat-color-text-headingHeader text colour
--caat-color-bg-subtleStriped (alternating) row background
--caat-color-state-hover-bgRow hover background
--caat-color-text / -text-inverseCell text on light / dark header
--caat-blue-700Sortable column header / sort control
--caat-radiusWrapper border radius
--caat-font-primaryTable font family

3. Authoring Fields

FieldTypeRequiredNotes
CaptionTextfieldYesVisible table caption describing the data
Header RowMultifield (cells)YesColumn headers — minimum 2 columns
Data RowsMultifieldYesEach row contains a cells multifield matching header count
Responsive ModeDropdownYesOptions: scroll (horizontal overflow), stack (cards on mobile), collapse (hide columns)
SortableToggleNoEnables client-side column sorting
Striped RowsToggleNoAlternating row background colours

4. Validation

RuleConditionError Message
Caption requiredCaption field is empty"Please provide a table caption for accessibility."
Minimum columnsHeader row has fewer than 2 cells"A table must have at least 2 columns."
Minimum rowsData rows has fewer than 1 entry"A table must have at least 1 data row."
Cell count matchAny data row cell count ≠ header count"Row [n] has a different number of cells than the header row."

5. Content Guidance

  • Caption: Write a concise, descriptive caption such as "DBplus Contribution Rates 2024–2027". Avoid generic labels like "Table 1".
  • Headers: Keep column headers short (1–3 words). Include units in parentheses where applicable, e.g., "Fee Rate (bps)".
  • Data cells: Use consistent formatting — align numbers to the right, use the same decimal precision within a column.
  • Responsive mode: Use "stack" for comparison tables (≤ 4 columns). Use "scroll" for wide data tables (5+ columns).
  • Sortable: Only enable sorting on columns with comparable data (numbers, dates). Avoid on free-text columns.

6. Semantic HTML

<div class="caat-table__wrapper" data-analytics-component="table">
  <table class="caat-table caat-table--striped caat-table--hover">
    <caption class="caat-table__caption">{caption}</caption>
    <thead>
      <tr>
        <th scope="col">{header1}</th>
        <th scope="col">{header2}</th>
        <!-- additional headers -->
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>{cell1}</td>
        <td>{cell2}</td>
      </tr>
      <!-- additional rows -->
    </tbody>
  </table>
</div>

<!-- Sortable variant -->
<th scope="col">
  <button class="caat-table__sort-btn" aria-sort="none"
          data-analytics-action="table-sort"
          data-analytics-label="{column-id}">
    {header} <i class="bi bi-arrow-down-up"></i>
  </button>
</th>

<!-- Stacked responsive variant (adds data-label for mobile) -->
<td data-label="{header}">{cell}</td>

7. CSS Contract

SelectorPurpose
.caat-tableBase table styles — width, border-collapse, font
.caat-table__wrapperOverflow-x container for horizontal scrolling
.caat-table__captionCaption styling — positioned above table, smaller text
.caat-table--stripedAlternating row background via :nth-child(even)
.caat-table--hoverRow highlight on hover
.caat-table--responsiveEnables responsive behaviour based on chosen mode
.caat-table__sort-btnUnstyled button inside <th> for sort interaction
.caat-table--stackedMobile layout — collapses rows into card-like blocks using data-label attributes

8. Accessibility

RequirementImplementation
Table identificationEvery table must have a <caption> or be linked via aria-labelledby to a visible heading.
Header scopeAll <th> elements must include scope="col" (or scope="row" for row headers).
Sortable columnsSort buttons must use aria-sort with values: none, ascending, descending. Live region announces sort change.
Responsive stacked modeUses data-label attributes to maintain header–data relationships when table is linearized on mobile.
Focus managementSort buttons are keyboard-focusable. Focus remains on button after sort action.
Colour contrastAll text meets WCAG 2.1 AA (4.5:1 minimum) against striped/hover backgrounds.

9. SEO

  • Use semantic <table>, <thead>, <tbody>, <caption> elements — search engines parse these for structured data.
  • Caption text should include relevant keywords (e.g., "CAAT DBplus contribution rates").
  • Avoid rendering tables as CSS grid/flex layouts which lose semantic meaning for crawlers.
  • Tables presenting pension data may be eligible for rich snippets — ensure content is indexable (not loaded via JS).

10. Analytics

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 Notes

  • Resource type: caat/components/table
  • Sling model: Exposes caption, headerCells[], rows[].cells[], responsiveMode, sortable, striped.
  • HTL template: Iterates rows multifield; outputs data-label on each <td> when responsive mode is "stack".
  • Client library: caat.components.table — categories: caat.site.
  • Policies: Allow placement in Content Container, Two-Column Layout, and Full-Width Container.
  • Allowed renditions: Default table, Striped, Hover, Sortable (toggles combined).

12. Dialog Model (YAML)

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-table-dialog:
  jcr:primaryType: nt:unstructured
  sling:resourceType: cq/gui/components/authoring/dialog
  content:
    sling:resourceType: granite/ui/components/coral/foundation/container
    items:
      tabs:
        sling:resourceType: granite/ui/components/coral/foundation/tabs
        items:

          # ── Data tab ─────────────────────────────────
          data:
            jcr:title: Data
            items:
              caption:
                sling:resourceType: granite/ui/components/coral/foundation/form/textfield
                name: ./caption
                fieldLabel: Caption
                required: true
              headerRow:
                sling:resourceType: granite/ui/components/coral/foundation/form/multifield
                name: ./headerCells
                fieldLabel: Header columns
                composite: true
                required: true
                items:
                  field:
                    sling:resourceType: granite/ui/components/coral/foundation/container
                    items:
                      cellText:
                        sling:resourceType: granite/ui/components/coral/foundation/form/textfield
                        name: ./cellText
                        fieldLabel: Column header
                        required: true
              dataRows:
                sling:resourceType: granite/ui/components/coral/foundation/form/multifield
                name: ./dataRows
                fieldLabel: Data rows
                composite: true
                required: true
                items:
                  field:
                    sling:resourceType: granite/ui/components/coral/foundation/container
                    items:
                      cells:
                        sling:resourceType: granite/ui/components/coral/foundation/form/multifield
                        name: ./cells
                        fieldLabel: Cells
                        composite: true
                        items:
                          field:
                            sling:resourceType: granite/ui/components/coral/foundation/container
                            items:
                              cellValue:
                                sling:resourceType: granite/ui/components/coral/foundation/form/textfield
                                name: ./cellValue
                                fieldLabel: Cell value

          # ── Style tab ────────────────────────────────
          style:
            jcr:title: Style
            items:
              responsiveMode:
                sling:resourceType: granite/ui/components/coral/foundation/form/select
                name: ./responsiveMode
                fieldLabel: Responsive mode
                required: true
                items:
                  - { text: Horizontal scroll, value: scroll }
                  - { text: Stacked cards,     value: stack }
                  - { text: Collapse columns,  value: collapse }
                defaultValue: scroll
              sortable:
                sling:resourceType: granite/ui/components/coral/foundation/form/switch
                name: ./sortable
                fieldLabel: Enable sorting
                checked: false
              striped:
                sling:resourceType: granite/ui/components/coral/foundation/form/switch
                name: ./striped
                fieldLabel: Striped rows
                checked: true

13. QA Acceptance Checklist

  • Standard, striped, and hover table variants render with correct styling and spacing.
  • Responsive stacked mode displays rows as card-like blocks on viewports ≤ 768px, with data-label visible as pseudo-headers.
  • Horizontal scroll mode shows a scrollbar on overflow without breaking page layout.
  • Sortable columns toggle between ascending/descending on click; aria-sort updates correctly.
  • Keyboard navigation works: Tab to sort buttons, Enter/Space to activate sort.
  • <caption> or aria-labelledby is present on every rendered table.
  • All <th> elements have scope attributes.
  • Analytics table-sort event fires with correct column and direction payload.
  • Colour contrast meets WCAG 2.1 AA on striped and hover backgrounds.
  • Cross-browser tested (Chrome, Firefox, Safari, Edge) on desktop and mobile.

14. Definition of Done

AreaDone means
Design Figma component matches this contract. All variants (standard, striped, responsive stacked, sortable) documented. Tokens used — no hard-coded colour values.
Development AEM component created at caat/components/table. HTL template emits semantic HTML per Section 6. CSS uses design tokens. JS handles sort interaction and fires analytics events. Unit tests pass.
QA All items in the QA Acceptance Checklist (Section 13) pass. Manual accessibility audit completed. Cross-browser and responsive testing done.
Launch Component deployed to AEM production. Content authors trained on authoring dialog. Analytics dashboards confirmed receiving table-sort events. Documentation published.