Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.canthus.org/llms.txt

Use this file to discover all available pages before exploring further.

Text scaling

All screens must remain functional and readable at the system’s largest text size setting (iOS: Accessibility sizes up to 310% scale; Android: font scale up to 2.0x). The app clamps MediaQuery.textScaler to the range 0.85x – 1.4x at the root (main.dart). This honors user preference up to the WCAG AA target while preventing layouts from breaking at extreme settings. Anything above 1.4x is rendered as 1.4x.
  • No text must be clipped or truncated without an accessible alternative.
  • Layouts must reflow rather than overflow. Fixed-height containers that clip text are not acceptable.
  • Interactive elements must not overlap when text is enlarged.
  • Test at: iOS Accessibility Inspector with largest Dynamic Type size; Android with font size at maximum.
  • Golden tests in app/test/design/primitives/ exercise primitives at 1.0x and 1.3x scale to catch regressions.

Typography

The app ships a single canonical type scale defined in app/lib/core/design/tokens/typography.dart and wired into ThemeData.textTheme. Feature code must reach for Theme.of(context).textTheme.* rather than hardcoding fontSize: values. The architecture test test/architecture/design_system_discipline_test.dart enforces this.
TokenSizeUsage
displayLarge44Hero stat numbers (AppStat.hero)
displayMedium36Primary screen heroes
displaySmall32Smaller heroes
headlineMedium28Screen titles
headlineSmall22Section titles
titleLarge20Card titles
titleMedium18Subsection titles
titleSmall16Small titles
bodyLarge18Primary body text
bodyMedium17Default body
bodySmall13Caption / hint
labelLarge15Button text
labelMedium13Chip / tag
labelSmall11Chart axis (auto-scales)
Body text uses Atkinson Hyperlegible, a typeface designed for low vision. Display text uses DM Sans. Stats and chart labels use tabular numerals (withTabularFigures(...)) so digits stay aligned as values change.

Screen readers

Required semantics

Every interactive element must have a semantic label. Flutter’s Semantics widget or Tooltip is used where the visual label is insufficient.
ElementRequirement
Icon-only buttonssemanticLabel required
Check-in slidersLabel must include axis name and current value: “Body: 3 out of 5”
Mana barLabel must convey remaining vs total: “18 of 34 mana remaining”
Task cardsLabel must include task name and cost: “Shower, 4 mana”
Empty statesMust be readable: “No tasks today”
Loading statesMust announce: “Loading”
Error statesMust announce the message and the available action
Focus order must follow visual reading order (top to bottom, left to right). Custom traversal order (FocusTraversalGroup) is required for any non-standard layout.

Screen-by-screen semantics

New screens added to the app must include a semantics review before release. The review checks:
  • All interactive elements have labels
  • Focus order is logical
  • State changes are announced (e.g. task marked done, pool updated)
  • No redundant labels (e.g. “Button” without context)

Reduced motion

Users who enable “Reduce Motion” on their device must not see vestibular-triggering animations.

Rules

  • Entrances/exits: Replace sliding or scaling transitions with a simple cross-fade or no animation.
  • Looping animations: Must be disabled entirely under reduced motion. No pulsing, spinning, or continuous movement.
  • Pool and cost updates: Numeric changes must not animate between values under reduced motion.
  • Duration cap (non-reduced): No transition in normal mode exceeds 300ms. Animations that feel slow are worse than no animation.

Implementation

Check the platform preference via MediaQuery.of(context).disableAnimations. Apply to all animated widgets at the design system level (lib/ui/core/design/), not per-screen.
final reduceMotion = MediaQuery.of(context).disableAnimations;

AnimatedOpacity(
  duration: reduceMotion ? Duration.zero : const Duration(milliseconds: 200),
  opacity: visible ? 1.0 : 0.0,
  child: child,
)

Contrast

Minimum contrast ratios follow WCAG 2.1 AA:
ElementMinimum ratio
Normal text (< 18pt)4.5:1
Large text (>= 18pt bold or 24pt regular)3:1
UI components and graphics3:1
The brand seed #4249C9 must be verified through the generated Material 3 light and dark colour schemes. Both schemes must pass independently anywhere their derived roles are used for text, UI components, or graphics. Contrast must be verified with a tool (e.g. Colour Contrast Analyser) before new colour pairings are introduced to the design system.

Touch targets

Minimum touch target size: 44x44pt (logical pixels) on all platforms.
  • Interactive elements smaller than 44x44pt in their visual size must have their tap area extended using GestureDetector with HitTestBehavior or wrapping in a minimum-sized InkWell.
  • Targets must have at least 8pt of non-interactive space between them to prevent mis-taps.

Acceptance criteria for new screens

Before a new screen ships, it must pass:
  • Renders correctly at maximum system text size (no clipping, no overflow)
  • All interactive elements have semantic labels
  • Focus order is logical under screen reader navigation
  • State changes are announced
  • Reduced motion preference disables or replaces all animations
  • All text and UI elements meet WCAG AA contrast
  • All touch targets are >= 44x44pt