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
Contribution Rates by Tier
Member contributions
Employer contributions
Plain-language note: Contribution rates are applied to your earnings. Tier 1 applies up to the Year's Maximum Pensionable Earnings (YMPE); Tier 2 applies to earnings above that threshold.
| Tier | Rate | Paid by |
|---|---|---|
| Tier 1 (≤ YMPE) | 7.2% | Member |
| Tier 2 (> YMPE) | 9.6% | Member |
| Employer Tier 1 | 8.4% | Employer |
| Employer Tier 2 | 10.0% | Employer |
| Voluntary Add-on | 3.0% | Member (optional) |
Variant 3 — Donut / Ring Chart
Rendered
Portfolio Allocation
100%
Allocated
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.
| Asset class | Allocation |
|---|---|
| Equities | 45% |
| Fixed Income | 30% |
| Real Assets | 15% |
| Cash | 10% |
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
| Component | Data Visualization |
| AEM Resource Type | caat/components/data-visualization |
| Status | New |
| Purpose | Presents pension plan metrics — key statistics, contribution breakdowns, portfolio allocations, and performance comparisons — using accessible, CSS-only visual treatments. |
| Variants | Stat Cards, Horizontal Bar Chart, Donut / Ring Chart, Comparison Table |
| Content Source | Authored in AEM dialog; data values entered manually or injected via Content Fragment reference. |
2. Design Tokens
| Token | Role | Default |
|---|---|---|
--caat-blue-900 | Primary value colour, bar fills, donut segment | #003750 |
--caat-blue-700 | Secondary donut segment | #0b5a80 |
--caat-blue-100 | Bar track background | #e7f4fb |
--caat-green | Tertiary donut segment | #55a546 |
--caat-green-700 | Positive trend indicator | #367f32 |
--caat-grey-200 | Card borders, quaternary donut segment, benchmark bar | #dfe6ef |
--caat-focus | Focus ring | #1A73E8 |
--caat-ink | Label text colour | #102637 |
--caat-radius | Border radius for cards and bars | .75rem |
--caat-font-primary | Value font family | Libre Franklin |
3. Authoring Fields
| Field | Type | Required | Notes |
|---|---|---|---|
| Variant | Select | Yes | stat-cards | bar-chart | donut | comparison-table |
| Section Title | Text | No | Optional heading above the chart (e.g., "Portfolio Allocation") |
| Items (multifield) | Multifield | Yes | Each item: label (text), value (text), percentage (number 0-100), colour override (select) |
| Centre Label | Text | No | Donut variant only — text inside the ring |
| Show Trend | Checkbox | No | Stat cards only — enables trend arrow and delta text |
| Trend Direction | Select | No | up | down — per stat card item |
| Trend Text | Text | No | e.g., "8.3% YoY" |
| Accessible Description | Textarea | Yes | Full text alternative for the chart (used in aria-label) |
4. Validation Rules
| Rule | Enforcement |
|---|---|
| At least one item required in multifield | AEM dialog validation |
| Percentage values must be 0–100 | Number input with min/max |
| Donut variant percentages must sum to 100 | Client-side validation with author warning |
| Accessible description is mandatory | Required field in dialog |
| Stat card value must not be empty | Per-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
| Selector | Purpose |
|---|---|
.caat-dataviz | Block wrapper |
.caat-dataviz__row | Flex row for stat cards |
.caat-dataviz__card | Individual stat card |
.caat-dataviz__value | Large metric value — 2.5rem / 900 weight |
.caat-dataviz__label | Descriptive label below value |
.caat-dataviz__title | Section heading for chart |
.caat-dataviz__trend | Trend indicator with directional modifier |
.caat-dataviz__bar-chart | Bar chart container |
.caat-dataviz__bar-row | Single bar row (label + track) |
.caat-dataviz__bar-track | Background track for bar |
.caat-dataviz__bar | Filled bar — uses inline width % |
.caat-dataviz__bar--striped | Diagonal striped fill (blue) — never rely on colour alone |
.caat-dataviz__bar--striped-green | Diagonal striped fill (green) |
.caat-dataviz__bar--striped-teal | Diagonal striped fill (teal) |
.caat-dataviz__note | Plain-language note explaining chart takeaway |
.caat-dataviz__table-fallback | Semantic table equivalent for screen readers |
.caat-dataviz__legend-dot--striped | Striped legend swatch matching bar pattern |
.caat-dataviz__donut | 200px circle with conic-gradient |
.caat-dataviz__donut-hole | White inner circle overlay |
.caat-dataviz__legend | Flex row of legend items |
.caat-dataviz__legend-item | Legend entry (dot + text) |
.caat-dataviz__legend-dot | 12px colour circle |
.caat-dataviz__comparison-table | Performance comparison table |
.caat-dataviz__inline-bar | Inline bar inside table cell |
8. Accessibility
- Donut and bar chart wrappers use
role="img"with a descriptivearia-labelsummarising the data. - Stat card groups use
role="region"witharia-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 anaria-labelon 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-teston the variant field. - Multifield: Items stored as child resources under
./items. - Donut gradient: Built server-side from item percentages; emitted as inline
styleon.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"andaria-labelare correctly applied to chart wrappers. - Screen reader announces full accessible description for each chart variant.
- Bar width transitions disabled when
prefers-reduced-motion: reduceis active. - Analytics events (
dataviz-viewed,dataviz-interaction) fire correctly. - Cross-browser tested: Chrome, Firefox, Safari, Edge.
14. Definition of Done
| Area | Done 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. |