Component Contract

Data Visualization

Charts, graphs, and data display components for presenting pension plan metrics across the CAAT member experience.

Live Demo

Variant 1 — Stat Cards

Rendered

$2.1B
Assets Under Management
8.3% YoY
85,000+
Members
3.1% YoY
96%
Funded Ratio
2.0% YoY
4.2%
Average Return
0.5% YoY

Variant 2 — Horizontal Bar Chart

Rendered

Text equivalent — Contribution rates by tier
TierRatePaid by
Tier 1 (≤ YMPE)7.2%Member
Tier 2 (> YMPE)9.6%Member
Employer Tier 18.4%Employer
Employer Tier 210.0%Employer
Voluntary Add-on3.0%Member (optional)

Variant 3 — Donut / Ring Chart

Rendered

Portfolio Allocation

Equities 45% Fixed Income 30% Real Assets 15% Cash 10%
Plain-language takeaway: The portfolio is intentionally diversified across growth, income, and real asset strategies to support long-term sustainability.
Text equivalent — Portfolio allocation
Asset classAllocation
Equities45%
Fixed Income30%
Real Assets15%
Cash10%

Variant 4 — Comparison Table with Visual Bars

Rendered

Plan Performance vs Benchmark

Year Plan Return Benchmark Comparison
2025 7.8% 6.5%
2024 5.2% 5.8%
2023 9.1% 7.4%
2022 -2.3% -3.1%
2021 11.4% 10.2%

1. Summary

ComponentData Visualization
AEM Resource Typecaat/components/data-visualization
Status New
PurposePresents pension plan metrics — key statistics, contribution breakdowns, portfolio allocations, and performance comparisons — using accessible, CSS-only visual treatments.
VariantsStat Cards, Horizontal Bar Chart, Donut / Ring Chart, Comparison Table
Content SourceAuthored in AEM dialog; data values entered manually or injected via Content Fragment reference.

2. Design Tokens

TokenRoleDefault
--caat-blue-900Primary value colour, bar fills, donut segment#003750
--caat-blue-700Secondary donut segment#0b5a80
--caat-blue-100Bar track background#e7f4fb
--caat-greenTertiary donut segment#55a546
--caat-green-700Positive trend indicator#367f32
--caat-grey-200Card borders, quaternary donut segment, benchmark bar#dfe6ef
--caat-focusFocus ring#1A73E8
--caat-inkLabel text colour#102637
--caat-radiusBorder radius for cards and bars.75rem
--caat-font-primaryValue font familyLibre Franklin

3. Authoring Fields

FieldTypeRequiredNotes
VariantSelectYesstat-cards | bar-chart | donut | comparison-table
Section TitleTextNoOptional heading above the chart (e.g., "Portfolio Allocation")
Items (multifield)MultifieldYesEach item: label (text), value (text), percentage (number 0-100), colour override (select)
Centre LabelTextNoDonut variant only — text inside the ring
Show TrendCheckboxNoStat cards only — enables trend arrow and delta text
Trend DirectionSelectNoup | down — per stat card item
Trend TextTextNoe.g., "8.3% YoY"
Accessible DescriptionTextareaYesFull text alternative for the chart (used in aria-label)

4. Validation Rules

RuleEnforcement
At least one item required in multifieldAEM dialog validation
Percentage values must be 0–100Number input with min/max
Donut variant percentages must sum to 100Client-side validation with author warning
Accessible description is mandatoryRequired field in dialog
Stat card value must not be emptyPer-item required field

5. Content Guidance

  • Stat cards: Keep values short (e.g., "$2.1B" not "$2,100,000,000"). Use a concise label (2–4 words).
  • Bar charts: Limit to 3–7 bars. Labels should be ≤ 25 characters. Always include the numeric value in the bar.
  • Donut charts: Use 2–5 segments. Ensure percentages sum to 100%. Provide a centre summary label.
  • Comparison tables: Include a clear header row. Use consistent units across rows (e.g., all percentages).
  • Accessible description: Write a full sentence summarising the data, e.g., "Portfolio allocation: Equities 45%, Fixed Income 30%, Real Assets 15%, Cash 10%."
  • Always pair visual data with a text alternative — never rely solely on colour to convey meaning.

6. Semantic HTML

Stat Cards

<div class="caat-dataviz" role="region" aria-label="Key pension statistics">
  <div class="caat-dataviz__row">
    <div class="caat-dataviz__card">
      <div class="caat-dataviz__value">$2.1B</div>
      <div class="caat-dataviz__label">Assets Under Management</div>
      <div class="caat-dataviz__trend caat-dataviz__trend--up">
        <i class="bi bi-arrow-up-short" aria-hidden="true"></i> 8.3% YoY
      </div>
    </div>
    <!-- additional cards -->
  </div>
</div>

Bar Chart

<div class="caat-dataviz" role="img" aria-label="Contribution rates by tier">
  <h4 class="caat-dataviz__title">Contribution Rates by Tier</h4>
  <div class="caat-dataviz__bar-chart">
    <div class="caat-dataviz__bar-row">
      <span class="caat-dataviz__bar-label">Tier 1</span>
      <div class="caat-dataviz__bar-track">
        <div class="caat-dataviz__bar caat-dataviz__bar--striped" style="width:72%">7.2%</div>
      </div>
    </div>
  </div>
  <div class="caat-dataviz__legend">
    <span class="caat-dataviz__legend-item">
      <span class="caat-dataviz__legend-dot--striped"></span> Member
    </span>
  </div>
  <div class="caat-dataviz__note">
    <strong>Plain-language note:</strong> Explain the chart’s takeaway.
  </div>
</div>
<!-- Accessible table fallback -->
<div class="caat-dataviz__table-fallback">
  <table>
    <caption>Text equivalent — Contribution rates</caption>
    <thead><tr><th scope="col">Tier</th><th scope="col">Rate</th></tr></thead>
    <tbody><tr><th scope="row">Tier 1</th><td>7.2%</td></tr></tbody>
  </table>
</div>

Donut Chart

<div class="caat-dataviz">
  <div class="caat-dataviz__donut" role="img"
       aria-label="Portfolio allocation: Equities 45%, Fixed Income 30%, Real Assets 15%, Cash 10%"
       style="background:conic-gradient(…)">
    <div class="caat-dataviz__donut-hole">
      <div class="caat-dataviz__value">100%</div>
      <div class="caat-dataviz__label">Allocated</div>
    </div>
  </div>
  <div class="caat-dataviz__legend">…</div>
</div>

Comparison Table

<div class="caat-dataviz">
  <table class="caat-dataviz__comparison-table">
    <thead><tr><th>Year</th><th>Plan Return</th><th>Benchmark</th><th>Comparison</th></tr></thead>
    <tbody>
      <tr>
        <td>2025</td><td>7.8%</td><td>6.5%</td>
        <td><span class="caat-dataviz__inline-bar" style="width:78%;background:var(--caat-blue-900)"></span></td>
      </tr>
    </tbody>
  </table>
</div>

7. CSS Contract

SelectorPurpose
.caat-datavizBlock wrapper
.caat-dataviz__rowFlex row for stat cards
.caat-dataviz__cardIndividual stat card
.caat-dataviz__valueLarge metric value — 2.5rem / 900 weight
.caat-dataviz__labelDescriptive label below value
.caat-dataviz__titleSection heading for chart
.caat-dataviz__trendTrend indicator with directional modifier
.caat-dataviz__bar-chartBar chart container
.caat-dataviz__bar-rowSingle bar row (label + track)
.caat-dataviz__bar-trackBackground track for bar
.caat-dataviz__barFilled bar — uses inline width %
.caat-dataviz__bar--stripedDiagonal striped fill (blue) — never rely on colour alone
.caat-dataviz__bar--striped-greenDiagonal striped fill (green)
.caat-dataviz__bar--striped-tealDiagonal striped fill (teal)
.caat-dataviz__notePlain-language note explaining chart takeaway
.caat-dataviz__table-fallbackSemantic table equivalent for screen readers
.caat-dataviz__legend-dot--stripedStriped legend swatch matching bar pattern
.caat-dataviz__donut200px circle with conic-gradient
.caat-dataviz__donut-holeWhite inner circle overlay
.caat-dataviz__legendFlex row of legend items
.caat-dataviz__legend-itemLegend entry (dot + text)
.caat-dataviz__legend-dot12px colour circle
.caat-dataviz__comparison-tablePerformance comparison table
.caat-dataviz__inline-barInline bar inside table cell

8. Accessibility

  • Donut and bar chart wrappers use role="img" with a descriptive aria-label summarising the data.
  • Stat card groups use role="region" with aria-label.
  • Never rely on colour alone — bars use diagonal striped patterns (repeating-linear-gradient) so categories remain distinguishable without colour vision. Legend swatches match the stripe pattern.
  • Every chart variant must include an accessible table fallback (.caat-dataviz__table-fallback) providing the same data in a semantic <table> with <caption> and <th scope>.
  • A plain-language note (.caat-dataviz__note) explains the chart’s takeaway in readable prose.
  • Trend icons include aria-hidden="true"; trend text is readable by screen readers.
  • Comparison table inline bars use aria-hidden="true" with an aria-label on the parent <td> describing the comparison.
  • Comparison tables use semantic <table>, <thead>, <th> elements.
  • All interactive elements (if any) must have visible focus indicators using --caat-focus.
  • Meets WCAG 2.1 AA colour contrast (4.5:1 for text, 3:1 for graphical elements).
  • Reduced motion: bar width transitions respect prefers-reduced-motion: reduce.

9. SEO

  • Use semantic headings (<h3>/<h4>) for chart titles to support page outline.
  • Accessible descriptions provide text equivalents that can be indexed.
  • Stat card values use text (not images) — fully crawlable by search engines.
  • Comparison tables use proper <table> markup for structured data extraction.

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 Implementation Notes

  • Proxy path: /apps/caat/components/data-visualization
  • Super type: core/wcm/components/commons/v1
  • HTL template: Renders variant-specific markup via data-sly-test on the variant field.
  • Multifield: Items stored as child resources under ./items.
  • Donut gradient: Built server-side from item percentages; emitted as inline style on .caat-dataviz__donut.
  • Content Fragment: Optionally bind items to a CF model for API-driven data updates.
  • Allowed parents: Container, Layout Container, Experience Fragment.

12. Dialog Model (JSON)

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).

{
  "jcr:primaryType": "nt:unstructured",
  "sling:resourceType": "cq/gui/components/authoring/dialog",
  "content": {
    "items": {
      "variant": {
        "sling:resourceType": "granite/ui/components/coral/foundation/form/select",
        "label": "Variant",
        "name": "./variant",
        "required": true,
        "options": [
          { "text": "Stat Cards", "value": "stat-cards" },
          { "text": "Horizontal Bar Chart", "value": "bar-chart" },
          { "text": "Donut / Ring Chart", "value": "donut" },
          { "text": "Comparison Table", "value": "comparison-table" }
        ]
      },
      "title": {
        "sling:resourceType": "granite/ui/components/coral/foundation/form/textfield",
        "label": "Section Title",
        "name": "./title"
      },
      "accessibleDescription": {
        "sling:resourceType": "granite/ui/components/coral/foundation/form/textarea",
        "label": "Accessible Description",
        "name": "./accessibleDescription",
        "required": true
      },
      "items": {
        "sling:resourceType": "granite/ui/components/coral/foundation/form/multifield",
        "label": "Data Items",
        "name": "./items",
        "required": true,
        "fields": {
          "label": { "type": "textfield", "label": "Label", "required": true },
          "value": { "type": "textfield", "label": "Display Value", "required": true },
          "percentage": { "type": "numberfield", "label": "Percentage (0-100)", "min": 0, "max": 100 },
          "trendDirection": { "type": "select", "label": "Trend Direction", "options": ["up","down"] },
          "trendText": { "type": "textfield", "label": "Trend Text" }
        }
      }
    }
  }
}

13. QA Acceptance Checklist

  • Stat cards render with correct value, label, and trend indicator.
  • Stat cards wrap to a single column on mobile viewports (< 768px).
  • Bar chart bars render at correct widths matching percentage values.
  • Bar labels are left-aligned and legible at all breakpoints.
  • Donut chart renders as a 200px circle with correct conic-gradient segments.
  • Donut centre label is visible and centred within the ring.
  • Legend items display correct colour dots matching donut segments.
  • Comparison table inline bars scale correctly relative to values.
  • role="img" and aria-label are correctly applied to chart wrappers.
  • Screen reader announces full accessible description for each chart variant.
  • Bar width transitions disabled when prefers-reduced-motion: reduce is active.
  • Analytics events (dataviz-viewed, dataviz-interaction) fire correctly.
  • Cross-browser tested: Chrome, Firefox, Safari, Edge.

14. Definition of Done

AreaDone means
Design Figma components for all four variants match this contract. Token usage documented. Responsive behaviour specified for mobile and tablet.
Development AEM proxy component created. HTL template emits semantic HTML per Section 6. CSS uses design tokens exclusively. Donut conic-gradient built server-side. Accessible descriptions wired. Unit tests pass for all variants.
QA All items in the QA Acceptance Checklist (Section 13) pass. Manual accessibility audit completed. Cross-browser tested (Chrome, Firefox, Safari, Edge).
Launch Component deployed to AEM production. Content authors trained on variant selection and accessible description requirements. Documentation published. Analytics dashboards confirmed receiving events.