Component Contract

Spinner

Loading indicator for asynchronous operations across the CAAT Pension Plan member experience.

Live Demo

Border Spinner

Rendered

Loading…
Loading…
Loading…

Small · Default · Large

Growing Spinner

Rendered

Loading…
Loading…
Loading…

Small · Default · Large

Spinner with Label

Rendered

Loading your pension details…

Button with Spinner

Rendered

1. Component Summary

NameSpinner
AEM resource typecaat/components/spinner
PurposeProvides visual feedback during asynchronous operations — data fetches, form submissions, and page transitions — so members know the system is working.
VariantsBorder (default), Growing, With Label, Button Integration
SizesSmall (--sm, 1 rem), Default (2 rem), Large (--lg, 3 rem)
Status New

2. Design Tokens

TokenRoleValue (reference)
--caat-blue-900Default spinner colour#003750
--caat-blue-700Hover / active tint#0b5a80
--caat-blue-100Spinner track (light)#e7f4fb
--caat-greenSuccess state colour#55a546
--caat-grey-200Reduced-motion fallback border#dfe6ef
--caat-focusFocus ring colour (button integration)#1A73E8
--caat-inkLabel text colour#102637
--caat-radiusBorder radius for button wrapper.75rem
--caat-font-primaryLabel font familyLibre Franklin

3. Authoring Fields

FieldTypeRequiredNotes
VariantSelectYesOptions: border (default), grow
SizeSelectNoOptions: sm, default, lg. Defaults to default.
Accessible labelTextYesScreen-reader text, e.g. "Loading pension details".
Visible labelTextNoIf provided, renders beside the spinner as .caat-spinner__label.
Colour overrideSelectNoOptions: default, light. Light uses --caat-blue-100 for dark backgrounds.

4. Validation Rules

No author-managed content beyond an accessible label, so there are no authoring validation rules. Accessibility requirements are covered in Section 8 (Accessibility).

5. Content Guidance

  • Always provide an accessible label — even if the spinner is purely decorative in a button, the button's own label must describe the loading state (e.g. "Processing…").
  • Be specific — prefer "Loading your pension details…" over a generic "Loading…" so members know what to expect.
  • Keep labels concise — aim for fewer than 40 characters for visible labels.
  • Use spinners sparingly — if content loads in under 300 ms a spinner adds visual noise rather than reassurance. Defer rendering until a short delay has elapsed.
  • Pair with disabled controls — when a spinner indicates a form submission, disable the submit button to prevent duplicate requests.

6. Semantic HTML

Border spinner (default)

<div class="caat-spinner" role="status">
  <span class="visually-hidden">Loading…</span>
</div>

Growing spinner — large

<div class="caat-spinner caat-spinner--grow caat-spinner--lg" role="status">
  <span class="visually-hidden">Loading…</span>
</div>

Spinner with visible label

<div class="caat-spinner__label" role="status">
  <div class="caat-spinner caat-spinner--sm" aria-hidden="true"></div>
  Loading your pension details&hellip;
</div>

Button with spinner

<button class="caat-button caat-button--primary" type="button" disabled>
  <span class="caat-spinner" aria-hidden="true"></span>
  Processing&hellip;
</button>

7. CSS Contract

SelectorPurpose
.caat-spinnerBase spinning ring — 2 rem, --caat-blue-900 border, 0.75 s rotation.
.caat-spinner--smSmall variant — 1 rem.
.caat-spinner--lgLarge variant — 3 rem.
.caat-spinner--growGrowing (pulse) variant — uses scale animation instead of rotation.
.caat-spinner__labelWrapper that places visible text inline beside the spinner.

Reduced motion: When prefers-reduced-motion: reduce is active, all animation stops and the spinner displays a static indicator with reduced opacity.

8. Accessibility

RequirementImplementation
ARIA rolerole="status" on the spinner or its label wrapper so assistive technology announces the loading state as a live region.
Screen-reader textA .visually-hidden <span> inside standalone spinners provides a text alternative.
Button integrationSpinner inside a button uses aria-hidden="true"; the button's own text ("Processing…") serves as the accessible label.
Reduced motion@media (prefers-reduced-motion: reduce) stops all animation and shows a static visual indicator.
Colour contrastSpinner border colour meets WCAG 2.1 AA non-text contrast (3 : 1) against white and light backgrounds.

9. SEO Considerations

Transient / structural UI with no indexable content of its own — no SEO markup required. Ensure any content this component wraps or reveals stays server-rendered and crawlable.

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 component — create at /apps/caat/components/spinner.
  • HTL template — emit a <div> with BEM classes driven by dialog selections. Use data-sly-attribute for role="status".
  • Client library — category caat.components.spinner. Include spinner.css only; no JS dependency for CSS-only animation.
  • Sling model — expose variant, size, accessibleLabel, visibleLabel, and colourOverride.
  • Allowed parents — may be embedded inside Button, Card, Modal, and any container component.
  • No JavaScript required — spinner animation is pure CSS. Toggle visibility via hidden attribute or class in consuming component JS.

12. Dialog Model (JSON)

Not authored directly. The spinner is a sub-element emitted and configured by its host component (or generated from page structure), so it has no standalone cq:dialog — any options are exposed through the host component's dialog.

13. QA Acceptance Checklist

  • Border spinner renders a smooth rotating ring in --caat-blue-900.
  • All three sizes (sm, default, lg) render at correct dimensions (1 rem, 2 rem, 3 rem).
  • Growing spinner pulses with scale animation.
  • Visible label sits inline beside the spinner with correct alignment.
  • Button-with-spinner variant shows spinner + "Processing…" text while disabled.
  • role="status" is announced by NVDA and VoiceOver.
  • .visually-hidden label is read by screen readers for standalone spinners.
  • Animation stops when prefers-reduced-motion: reduce is active; static indicator is visible.
  • Spinner colour meets WCAG 2.1 AA non-text contrast (3 : 1) against white.
  • Analytics events (spinner-shown, spinner-duration) fire correctly.
  • Cross-browser tested: Chrome, Firefox, Safari, Edge.

14. Definition of Done

AreaDone means
Design Figma component matches this contract. Border and growing variants documented with all sizes. Reduced-motion fallback included.
Development AEM proxy component created. HTL template emits semantic HTML per Section 6. CSS uses design tokens exclusively. role="status" and visually-hidden labels wired. No JS dependency.
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 accessible-label requirements. Documentation published. Analytics dashboards confirmed receiving events.