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.
| Token | Size | Usage |
|---|
displayLarge | 44 | Hero stat numbers (AppStat.hero) |
displayMedium | 36 | Primary screen heroes |
displaySmall | 32 | Smaller heroes |
headlineMedium | 28 | Screen titles |
headlineSmall | 22 | Section titles |
titleLarge | 20 | Card titles |
titleMedium | 18 | Subsection titles |
titleSmall | 16 | Small titles |
bodyLarge | 18 | Primary body text |
bodyMedium | 17 | Default body |
bodySmall | 13 | Caption / hint |
labelLarge | 15 | Button text |
labelMedium | 13 | Chip / tag |
labelSmall | 11 | Chart 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.
| Element | Requirement |
|---|
| Icon-only buttons | semanticLabel required |
| Check-in sliders | Label must include axis name and current value: “Body: 3 out of 5” |
| Mana bar | Label must convey remaining vs total: “18 of 34 mana remaining” |
| Task cards | Label must include task name and cost: “Shower, 4 mana” |
| Empty states | Must be readable: “No tasks today” |
| Loading states | Must announce: “Loading” |
| Error states | Must announce the message and the available action |
Navigation order
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:
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:
| Element | Minimum ratio |
|---|
| Normal text (< 18pt) | 4.5:1 |
| Large text (>= 18pt bold or 24pt regular) | 3:1 |
| UI components and graphics | 3: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: