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.
Canthus — Design System & Copy Reference
Version 2.4 | March 2026
North Star: Protect the user’s mental state first. Enable task completion second.
Every colour, typeface, spacing decision, animation, and word choice in this system must pass one question: does this make the person feel capable, or does it subtly imply failure?
This is not a productivity app. It is a capacity tool for people whose capacity fluctuates unpredictably. Every design choice flows from this distinction.
Table of Contents
- Typography
- Colour System
- Spacing & Layout
- Elevation & Depth
- Iconography
- App Structure & Navigation
- Core Components
- Temporal Tasks & Appointments
- Add Task Flow
- Motion & Animation
- Copy System
- Accessibility
- Data & Privacy
- What This System Does Not Do
1. Typography
1.1 Font Pairing
Primary (body text): Atkinson Hyperlegible
Developed by the Braille Institute specifically for low vision and reading difficulty. Used for all body text, labels, and captions. Chosen for its proven legibility during cognitive fatigue and its unique letterforms that prevent common misreadings (b/d, i/l, 0/O). Free on Google Fonts, available in Flutter via google_fonts.
Display (headings): DM Sans
A warm humanist sans-serif used in Bold and SemiBold weights for headings and the mana display number. Brings warmth and approachability to high-visibility elements without sacrificing readability. Also free on Google Fonts.
Why this pairing: Atkinson Hyperlegible provides the strongest accessibility foundation where it matters most — body text the user reads repeatedly during cognitive fatigue. DM Sans display weights bring warmth and character to high-visibility elements.
1.2 Type Scale
| Token | Font | Size | Weight | Line Height | Letter Spacing | Use |
|---|
display | DM Sans | 28sp | 700 | 1.2 | -0.3px | Mana number, screen hero text |
heading-1 | DM Sans | 22sp | 600 | 1.3 | -0.2px | Screen titles |
heading-2 | DM Sans | 18sp | 600 | 1.35 | -0.1px | Section headers, card titles |
body-lg | Atkinson | 16sp | 400 | 1.65 | 0 | Primary task text |
body | Atkinson | 14sp | 400 | 1.65 | 0 | Supporting content |
label | Atkinson | 13sp | 700 | 1.0 | 0.2px | Buttons, nav labels, chips |
caption | Atkinson | 12sp | 400 | 1.5 | 0.1px | Timestamps, hints |
1.3 Hard Rules
- Minimum readable size: 14sp. Nothing the user needs to read is smaller.
- Body line height: never below 1.5. Non-negotiable for brain fog readability.
- No italics for body text. Increases cognitive load for visual processing difficulties.
- No label-as-placeholder pattern. Form fields always show a persistent visible label.
2. Colour System
The app uses a Material 3 seeded colour system. Both light and dark themes are generated from the brand seed #4249C9 and the resulting ColorScheme roles are the source of truth for implementation.
2.1 Compliance
This system targets compliance with three standards, all of which converge on the same contrast requirements:
- WCAG 2.1 AA — 4.5:1 normal text, 3:1 large text, 3:1 UI components
- ADA (2024 DOJ rule) — requires WCAG 2.1 AA for digital services
- European Accessibility Act 2025 — mandates EN 301 549 v3.2.1, which references WCAG 2.1 AA
Meeting WCAG 2.1 AA satisfies all three.
2.2 Seeded Material Foundation
| Source | Value | Use |
|---|
| Brand seed | #4249C9 | Input to Material 3 tonal palette generation |
| Light scheme | generated | Default light theme |
| Dark scheme | generated | Default dark theme |
2.3 Core Material Roles
| Role | Use |
|---|
surface | App background and base canvas |
surfaceContainerLow | Resting cards |
surfaceContainer | Inputs, emphasized cards, secondary surfaces |
surfaceContainerHigh | Bottom sheets, floating surfaces |
onSurface | Primary readable text on surfaces |
onSurfaceVariant | Supporting text, hints, dividers via related outline roles |
primary | Primary emphasis, active mana accent, filled emphasis |
secondary | Secondary accent, supportive emphasis |
tertiary | Additional accent separation where the UI needs a second highlight |
outline / outlineVariant | Strokes, dividers, low-emphasis boundaries |
error / onError | Error states only |
2.4 Semantic Accents
Some product semantics remain app-specific because they are not core theme roles:
| Token | Hex | Use |
|---|
success | #5DBF8A | Task completed |
caution | #D4A843 | Gentle heads-up, deadline approaching |
rest | #9B8FD4 | Recovery day, rest framing |
These accents are additive. They do not replace Material surface, text, or border roles.
2.5 Contrast Rules
- Validate both generated light and dark schemes against WCAG 2.1 AA.
- Use
onSurface for primary readable text and onSurfaceVariant for supporting text.
- Do not create a separate hard-coded tertiary text colour; reduced emphasis comes from Material roles and component states.
- Interactive emphasis uses Material accent roles (
primary, secondary, tertiary) rather than bespoke hex ladders.
- Semantic colour is never the sole state indicator. Every coloured state has a companion shape, icon, or label. This satisfies WCAG 1.4.1 (Use of Colour).
3. Spacing & Layout
3.1 Grid
Base grid: 8pt. Micro-adjustments: 4pt.
| Token | Value | Use |
|---|
space-1 | 4px | Micro — icon internal padding, tight badges |
space-2 | 8px | XS — tight internal spacing |
space-3 | 12px | SM — compact card padding |
space-4 | 16px | MD — standard gap, content padding |
space-5 | 20px | LG — section gaps |
space-6 | 24px | XL — between cards |
space-8 | 32px | 2XL — major section breaks |
space-12 | 48px | 3XL — hero areas |
3.2 Screen Layout
- Horizontal margins: 20px (wider than standard 16px — reduces density and overwhelm)
- Safe areas: Always respected. Nothing behind notch, dynamic island, or home indicator.
- Content never truncates at any system text scale setting.
3.3 Touch Targets
- Minimum: 48×48dp for all interactive elements (WCAG 2.5.8 AA)
- Primary actions: 56×56dp
- Minimum gap between targets: 8dp
- All swipe gestures have a tap alternative (WCAG 2.5.1)
3.4 Corner Radius
| Token | Value | Use |
|---|
radius-sm | 8px | Chips, tags, small badges |
radius-md | 12px | Cards, inputs, task cards |
radius-lg | 16px | Bottom sheets, large cards |
radius-xl | 24px | Modals, nav bar pill |
radius-full | 9999px | Buttons, completion circles, pills |
4. Elevation & Depth
Hierarchy uses colour temperature shift, not drop shadows. Surfaces get progressively warmer as they elevate. This produces lower visual noise than shadows and works better in dark mode.
| Level | Surface Token | Additional Treatment | Use |
|---|
| 0 | surface | None | App base |
| 1 | surfaceContainerLow | None | Cards at rest |
| 2 | surfaceContainer | 1px outlineVariant top edge only | Focused states, active cards |
| 3 | surfaceContainerHigh | Subtle mana-tinted glow rgba(85,96,204,0.06) | Bottom sheets, floating nav |
| 4 | surfaceContainerHigh | Stronger mana glow rgba(85,96,204,0.12) | Critical overlays |
The mana-tinted glow at levels 3–4 is a barely-perceptible aura, not a hard shadow. It reinforces the mana metaphor at a subconscious level.
5. Iconography
5.1 Philosophy
Icons carry the mana/wizardry metaphor. They should feel like objects from an arcane world — not gamified (no cartoonish effects), not clinical (no medical crosses or charts), not corporate (no briefcases or bar graphs). The aesthetic is quiet enchantment — things that feel like they belong in an apothecary or a scholar’s study.
5.2 Style Specifications
- Grid: 24×24dp base, 2dp padding (20dp content area)
- Stroke weight: 1.5px at 24dp — slightly lighter than standard, more delicate and arcane
- Corners: Fully rounded joins and terminals — no sharp points
- Style: Outline at rest, filled/glowing for active states
- Colour at rest:
onSurfaceVariant
- Colour active:
primary fill with subtle primary glow
5.3 Mana Cost Icons — Crystal Orbs
The mana cost metaphor uses crystal orbs — spherical gems that show fill level, connecting directly to the mana pool concept. Four tiers:
| Tier | Visual | Cost Range |
|---|
| 1 | Single small orb | 1–3 mana |
| 2 | Single larger orb with inner glow | 4–8 mana |
| 3 | Orb with orbiting spark | 9–15 mana |
| 4 | Double orb cluster with radiant burst | 16+ mana |
Each tier has a washed state (using lower-emphasis accent roles such as secondaryContainer) and a filled state (using the active accent role, typically primary). The number itself is shown in caption size beside the orb cluster.
5.4 Full Icon Set
| Icon | Metaphor | Use |
|---|
| Crystal orb (4 tiers × 2 states) | Glowing sphere | Mana cost on tasks |
| Mana pool / cauldron | Bubbling vessel | Mana screen, pool display |
| Rune / sigil | Abstract geometric mark | Check-in state, loading |
| Wand / spark | Thin wand with star tip | Add task, create action |
| Scroll | Rolled parchment | Task list item |
| Seedling / sprout | Small growing plant | Orchard tab icon |
| Constellation | 3 connected stars | Insights / pattern view |
| Moon phase | Crescent | Rest day, low mana state |
| Hourglass (arcane) | Sand timer with rune markings | Temporal task indicator |
| Shield | Simple rounded shield | Protection mode, essential task flag |
| Eye | Arcane eye | Transparency panel |
| Spiral | Inward spiral | Loading, calibrating |
| Flame (small) | Gentle candle flame | ”Warmth” indicator (not a streak) |
| Body axis | Simplified person | Check-in body axis |
| Brain / star burst | Abstract neural burst | Check-in mind axis |
| Heart | Soft heart | Check-in mood axis |
| Settings / compass | Arcane compass rose | Settings |
6. App Structure & Navigation
6.1 Three Tabs
| Tab | Purpose | What it contains |
|---|
| Mana | Where the user learns what they have today | Check-in, pool display, upcoming appointments, patterns |
| Tasks | Survival and maintenance tasks | Only tasks achievable within current pool, plus essential tasks always |
| Orchard | Leisure, hobbies, passion projects | Same mana pool — creative nourishment is equal to chores |
The Mana tab is the primary entry point. The full 3-axis check-in is the recommended daily path and produces the most accurate pool. However, the Tasks and Orchard tabs are never hard-locked. A tiered check-in system (§7.1) ensures users always have access to their task list, with data quality degrading gracefully when a full check-in isn’t possible. The system needs a state vector to compute the pool accurately — but on days when the user cannot provide one, the system estimates rather than blocks.
Why the Orchard has equal status to Tasks: Purely restrictive pacing is no more effective than standard care. Enabling users to plan and protect mana for the things they love — not just survival tasks — is clinically supported and philosophically essential.
6.2 Navigation Bar
- Floating pill shape,
surfaceContainerHigh background, level-3 mana glow, radius-xl
- 16px above bottom safe area edge, width = screen width minus 32px
- Active:
primary filled icon + label, primaryContainer circle background behind icon
- Inactive:
onSurfaceVariant outline icon, no label
- Before check-in: All three tabs are navigable. Tasks and Orchard use estimated pools (see §7.1 tiers). A subtle
onSurfaceVariant banner at the top of Tasks/Orchard reads: “Your mana is estimated today — check in when you’re ready for a better reading.” The banner is dismissable.
- Touch targets: 56dp each
7. Core Components
7.1 Mana Screen
The first screen the user sees each day. Single scrollable column — all content in one continuous flow.
State A — Check-in required
The screen opens showing the full 3-axis check-in as the recommended path. Three rows, one per axis (Body, Mind, Mood). Each axis is a 1–5 scale implemented as five large tappable circles (56dp each). No sliders — sliders require fine motor precision that isn’t appropriate for this audience.
Anchor labels for each axis:
| Axis | Low End | High End |
|---|
| Body | ”Moving gently" | "Feeling strong” |
| Mind | ”Foggy today" | "Sharp and clear” |
| Mood | ”A hard one" | "Really good” |
No numbers on the circles. After all three are selected, a single primary button appears: “See my mana”.
Below the primary button, two alternative paths are visible:
Quick check-in (ghost button): Opens a single-question view — “How are you today?” with three large tappable options: “Rough day”, “Okay day”, “Good day”. These map to pre-defined state vectors derived from the user’s personal history (25th / 50th / 75th percentile of recorded states, or [2,2,2] / [3,3,3] / [4,4,4] for new users). Produces a valid state vector in one tap. The pool is computed. Tasks surface. Data is less precise but clinically defensible — it approximates the single-dimension energy rating used in Energy Envelope research.
“Skip for now” (text link): If yesterday’s check-in exists, carries forward yesterday’s state vector with a conservative dampening factor (each axis × 0.9, floored at 1). If no yesterday exists, uses the user’s historical median with an 0.8 multiplier applied to the resulting pool. In both cases, the system marks this as an inferred check-in with reduced confidence weighting.
The full check-in remains the visually prominent path. The alternatives are clearly available without being emphasised — easy to find, hard to abuse.
State B — Check-in complete
Shows the daily mana pool (see Pool Display below), then scrollable content: check-in summary pills, Upcoming section, Your Patterns section.
7.2 Pool Display
- Large mana number in
display style, primary, centred
- Below: the word “mana” in
body, primary
- Below: crystal orb icons indicating pool tier (decorative)
- Below: a single transparency sentence in
body, onSurfaceVariant — explains why today’s pool is what it is
The pool display deliberately does not show a bar, a percentage, or a fraction. Just the number and its context sentence.
7.3 Task Card
The repeating unit for both Tasks and Orchard screens.
Anatomy:
- Container:
surfaceContainerLow, radius-md, 1px outlineVariant stroke
- Left accent bar: 3px wide, full card height, colour maps to mana tier
- Completion target: 44dp circle,
outlineVariant stroke at rest
- Task name:
body-lg, onSurface
- Subtitle:
caption, onSurfaceVariant
- Mana cost: right-aligned orb cluster + number in
caption, primary
Visibility rule: Tasks whose mana cost exceeds the remaining pool are not shown, with one exception: essential tasks (see §7.3.1). Non-essential over-budget tasks don’t appear greyed out, dimmed, or locked — they simply surface when conditions allow. Absence is less distressing than visible unavailability. The user is not confronted with things they cannot do.
States: Default → Selected (multi-complete mode) → Completing (800ms hold) → Completed (slides out, moves to “Done today” section)
Orchard variant: Identical structure with a seedling icon replacing the completion circle. Uses a “Tend” action rather than “Complete”.
7.3.1 Essential Tasks
A task can be flagged as essential by the user via a toggle at creation or edit time: “This is essential” (with a small shield icon). Essential tasks are things the user cannot safely skip — medication, feeding a dependent, critical medical appointments. The toggle is off by default. The app does not suggest which tasks should be essential — the user decides.
Surfacing rules:
Essential tasks are always visible on the Tasks screen, regardless of pool. Their display adapts based on whether they fit within the remaining pool:
-
Within pool: Standard task card. No special treatment. The essential flag is invisible in normal operation — it only activates below the pool threshold.
-
Over pool: The task card appears with a distinct treatment:
- Card background shifts to
surfaceContainer (slightly elevated — this card is different)
- Mana cost orbs use
caution amber instead of standard mana colour
- A single line below the task name: “This will use more than you have today” in
caption, onSurfaceVariant
- The completion circle remains active — completing an over-pool essential task pushes the user into overflow, which the existing system handles gracefully
-
On rest days (pool ≤ 5): Essential tasks appear in a dedicated “Essentials” section at the top of the Tasks screen, above any regular tasks. Section header: “What matters most today” in
heading-2, onSurface. Below it: “These are your essentials. Everything else can wait.” in caption, onSurfaceVariant.
Essential flag limit: If a user marks more than 5 tasks as essential, a soft prompt: “You have quite a few essentials — on a tough day, fewer means easier. Would you like to review which ones are truly must-dos?” This is a suggestion, not a block.
Appointments on their day are implicitly essential — the anchor card treatment (§8.4) already handles this. No additional essential flag needed.
7.4 Multi-Task Completion
Long press on any task card enters multi-complete mode. User taps additional cards to select, then taps “Complete all” — all selected cards animate simultaneously in a single 800ms hold. Completing 10 tasks takes: 1 long press + 9 taps + 1 confirm + 800ms (not 10 × 800ms = 8 seconds of forced waiting).
7.5 Stability Indicator
A small ambient constellation icon in the top-left corner of the Mana screen. Three dots connected by two thin lines forming a triangle. Only the colour changes across states.
Availability: Not shown until 14 check-in days recorded.
| State | Colour | Meaning |
|---|
| Stable | primary | Consistent rhythm for 14+ days |
| Transitional | onSurfaceVariant | Pattern is settling or recovering |
| Unstable | onSurfaceVariant | Regime shift detected — app is adjusting |
The icon is never absent once the threshold is reached — removing it during bad periods would itself be a worse signal. No animation, no pulsing. Long-press shows a one-sentence tooltip.
7.6 Your Patterns Section
Lives at the bottom of the Mana screen scroll. Three tabbed panels:
Rhythm (available from 5 check-in days) — 14-day bar chart showing daily mana spend (what was done, not what was available). Y-axis uses named bands (Rest / Gentle / Moderate / Full) relative to personal baseline. No numerical axis by default. Long-press any bar for a percentage tooltip.
Days (available from 21 check-in days) — Weekly pattern view showing average spend per day of the week over 28 days. Purely a cross-day comparison — cannot show a directional trend.
Balance (available from 7 check-in days) — Proportion bar showing physical vs cognitive mana split over 14 days. Descriptive context sentence below (e.g. “A fairly even mix lately”). Purely descriptive, never implies the balance should change.
Missing days are filled with greyed estimated bars (using day-of-week average or personal baseline), with honest tooltips explaining the estimation.
PEM recovery banner: Shown above active panel content when the system is in post-PEM recovery. Text: “Your pattern is in recovery — this data reflects a difficult stretch. It will settle as you feel better.” Below the banner text, a quiet text link: “Do you know what triggered this?” — tapping opens a bottom sheet listing completed tasks from the past 7 days. The user can select tasks they believe triggered the crash, or tap “Not sure” to dismiss. This is never prompted via notification or modal — it appears only within Patterns, only when scrolled to, only during recovery.
| Variant | Background | Text | Height |
|---|
| Primary | primary | surface | 52dp |
| Secondary | Transparent | primary (1.5px primary border) | 52dp |
| Destructive | error with low-emphasis fill | error | 52dp |
| Ghost | Transparent | primary | 44dp |
All buttons: label type, radius-full, 48dp minimum touch target. Destructive actions always require two confirmations.
7.8 Bottom Sheets
- Background:
surfaceContainerHigh, radius-lg top corners, flat bottom
- Drag handle: 36×4px,
outline, centred, 12px below top edge
- Overlay:
rgba(20,18,16,0.72) — warm-tinted dark scrim
- Always dismissible by drag or overlay tap
- Max height: 85% screen height with internal scroll
8. Temporal Tasks & Appointments
8.1 Philosophy
Temporal tasks are treated as a capacity problem, not a time problem. The design never says “you are running out of time.” It says “here is what this needs, and here is what you have.”
This is grounded in stress appraisal theory: the same deadline can be appraised as a threat (“I might fail”) or a challenge (“here is what I need to solve this”). The system systematically produces challenge appraisal. PEM risk from emotional distress is equivalent to physical exertion — a poorly designed urgency signal is a medical risk.
8.2 Two Task Types
Deadline tasks — Can be completed on any day within a window. “Pay electricity bill by Thursday.” Governed by the horizon system.
Appointment tasks — Fixed to a specific day and time. A GP visit, a hospital scan. Cannot be completed early or deferred without real-world consequence.
8.3 Deadline Tasks — Horizon System
| Window | Behaviour |
|---|
| > 7 days out | Invisible — not in any list |
| 3–7 days out | Surfaces in task list with hourglass icon and calm date label |
| < 3 days out | Hourglass becomes filled, caution amber pill label added |
| Day of | Amber pill reads “Today” — single word, no countdown |
The horizon window (default 7 days) is per-task configurable at creation. A “Show upcoming” toggle is available for users who prefer full visibility. No red states at any point — caution amber is the maximum urgency signal.
Date labels use day names only — “Thursday”, not “3 days” or “Due Thu 14 Mar”. Day names are calendar-natural. Countdowns are pressure.
8.4 Appointment Tasks
Created via a “This happens on a specific day” toggle. Fields: name, date, optional time, mana estimate (1–5 scale), optional recurrence, optional notes.
Mana reservation: On creation, users can set aside mana for the appointment day. On the day, the pool reflects the reservation and task surfacing uses the reduced available mana.
Day-of Anchor card: Appears at the top of the Tasks screen, surfaceContainer background, caution amber accent bar, filled hourglass icon. Cannot be swiped away — can only be marked as attended.
Needs Updating flow: If an appointment passes without being marked attended, it moves to a “Needs updating” section (never “Missed” or “Failed”). The correction window is 3 check-in days (not calendar days) — a user in a flare who hasn’t opened the app is not locked out.
8.5 Day-Before Notification
Sent at 11am (configurable 8am–6pm). Suppressed if the user has already viewed the Upcoming section that day. Appointment name is never included in the notification body — lock screen privacy.
Single appointment: “You have something tomorrow that will take some energy.”
Multiple: “You have a couple of things tomorrow that will take some energy.”
9. Add Task Flow
9.1 Philosophy
Task creation must never cost more energy than the task itself. Two principles: fast capture first, detail optional second, and the task exists the moment the user names it.
9.2 Two-Step Flow
Step 1 — Quick Capture (half-height bottom sheet):
- Text input for task name (keyboard raised immediately)
- Duration field (natural language: “20 minutes”, “half an hour”)
- Destination chips: Task · Orchard · Appointment
- Essential toggle (shield icon, off by default) — only shown for Task destination
- Two buttons: Done (creates immediately) and More (expands to detail)
Step 2 — Detail Layer (full-height sheet, all fields optional):
- Name (pre-filled)
- Duration (chip selector: Quick 5min / Short 15min / Medium 30min / Long 60min / Custom)
- Difficulty rating 1–5 (“How much energy does this usually take you?”) — shown with the same “big circles” UI pattern as the check-in, positioned as the first detail field after name and duration to emphasise its importance
- Physical or Mental? (Physical / Mix / Mental)
- Essential toggle (if not already set in Step 1)
- Live mana preview
- Notes, Repeat options
9.3 Cost Estimation Pipeline
Runs automatically as the user types. Five steps in priority order:
- Personal library lookup — Matches against tasks the user has previously created/completed. Token-level Levenshtein distance matching with recency weighting. After 10 completions of a personal library task, template-derived costs are permanently overridden by the user’s actual patterns.
- System template lookup — Matches against bundled task templates. Same Levenshtein matching.
- Semantic embedding match — On-device MiniLM-L6-v2 model (~22MB) computes cosine similarity against pre-encoded template embeddings.
- LLM estimation (opt-in) — On-device model preferred, cloud fallback sends task name only.
- Difficulty rating fallback — User rates 1–5 manually.
Medium-confidence matches show a confirmation chip: “Looks similar to [Template Name] — right?” with Yes/No options.
9.4 Mana Cost Display States
| State | Trigger | Appearance |
|---|
| Known | High-confidence match or all fields user-confirmed | Normal colour, full opacity |
| Uncertain | Any field defaulted | 50% opacity, onSurfaceVariant colour |
| Range | Future-day task with calculable range | Shows ~X or X–Y |
| Unknown | No match, no user input | Shows ? |
Tapping the mana cost area in any non-Known state opens the task detail for editing.
10. Motion & Animation
10.1 Principles
No spring physics. No bounce. No overshoot. No parallax. No looping animations except the mana calibration loading state (slow spiral rotation, stops when complete).
Vestibular disorders are common in ME/CFS, fibromyalgia, and POTS. Any motion suggesting rotation, oscillation, or rapid movement can trigger symptoms. Transitions should feel like the UI is already in the right state before you noticed it moved.
10.2 Duration & Easing
| Token | Duration | Curve | Use |
|---|
motion-instant | 0ms | — | Reduce Motion mode only |
motion-fast | 120ms | ease-standard | Toggle states, icon changes |
motion-med | 220ms | ease-enter | Card selections, panel opens |
motion-slow | 380ms | ease-enter | Screen transitions, bottom sheet entry |
motion-complete | 800ms | ease-standard | Task completion hold |
| Curve | Value |
|---|
ease-standard | cubic-bezier(0.4, 0.0, 0.2, 1.0) |
ease-enter | cubic-bezier(0.0, 0.0, 0.2, 1.0) |
ease-exit | cubic-bezier(0.4, 0.0, 1.0, 1.0) |
10.3 Reduce Motion
Detected via MediaQuery.of(context).disableAnimations in Flutter. When enabled:
- All transitions become instant opacity fades at
motion-fast (120ms, opacity only)
- Task completion hold retained at 400ms with opacity fade only (success feedback preserved, motion removed)
- No exceptions. Every animation has a declared Reduce Motion variant.
11. Copy System
11.1 Voice
The app speaks as a calm, knowledgeable friend who happens to understand energy management. Not a medical professional (clinical distance), not a productivity coach (pressure), not a cheerleader (false positivity). Warm, matter-of-fact, never surprised by difficulty.
Copy principles:
- Honest. The system does not hide bad states. Users with chronic illness know when they’re in a bad period.
- Non-patronising. Never tell the user how to feel or what they should do.
- Non-prescriptive. Avoid “you should”, “you need to”, “make sure to”.
- No urgency manufacture. The user already has enough. Do not add to it.
- No false positivity. Don’t celebrate low activity or frame hard days as opportunities.
11.2 Banned Phrases
| ❌ Banned | Why | ✅ Replacement |
|---|
| ”You only completed…” | Frames completion as a score | Don’t surface completion counts this way |
| ”Don’t forget…” | Creates urgency | Remove or rephrase as neutral info |
| ”You’re falling behind” | Implies failure | Never use |
| ”Great job!” / “You did it!” | Patronising for adults managing illness | Remove or use neutral acknowledgement |
| ”Push yourself” / “Keep going!” | Encourages overexertion | Never use |
| ”No tasks completed today” | Frames rest as failure | ”Nothing tracked today” or nothing at all |
| ”Streak” | Implies continuity is a goal | Never use |
| ”Overdue” | Guilt-inducing | ”Carried over”, or remove |
| ”You missed…” | Blame | Never use |
| Any red urgency indicator on tasks | Visual guilt | Do not implement |
| ”Low energy” | Clinical framing | ”Gentle day" |
| "Depleted” | Deficit language | ”Resting" |
| "Task too expensive” | Transactional | ”Save this for a stronger day" |
| "Failed to complete” | Failure language | ”Carried forward" |
| "Insufficient mana” | System language | ”Not quite yet" |
| "Try harder” | — | Never, under any circumstances |
| ”Welcome back” | Implies leaving was notable | Don’t comment on absence |
| ”We missed you” | Implies obligation to use the app | Don’t comment on absence |
11.3 Copy by Context
Low capacity day:
Show the shorter task list without comment. If a label is needed: “Today’s list” or nothing. Never say “You don’t have much energy today. Take it easy!”
Rest day with essentials (pool ≤ 5):
The Essentials section header reads “What matters most today”. Below it: “These are your essentials. Everything else can wait.” No commentary on the low pool.
Skipped or deferred tasks:
“Carried over” or move silently. No badge, no indicator. If the user asks: “Carried from [day].”
Empty states:
| Context | Copy |
|---|
| Tasks — all done | ”You’ve done enough for today.” |
| Tasks — no tasks yet | ”Nothing here yet. Add something small if you’d like.” |
| Orchard — empty | ”Your orchard is waiting. Add something you love.” |
| Mana pool at minimum | ”Today is a rest day. That is enough.” |
| No check-in data | ”Let’s see how you’re feeling before we figure out your day.” |
Errors:
Calm, one clear next step, no urgency. Never exclamation marks, never “Warning:”, never blame framing.
Insights and patterns:
Describe patterns factually without framing them as problems. “Thursday check-ins are often lower than the rest of the week.” Let the user interpret. Never frame observations as warnings.
PEM / low state:
No special message. Show the smaller list. Do not comment on check-in scores. Do not say “You’re having a rough day.” Do not say “Rest up.”
Onboarding:
Factual instructions. “Choose the tasks you did yesterday.” “Rate how hard each felt.” No hype. Never “Let’s get you set up!” or “You’re going to love this!“
11.4 Transparency Sentences
The sentences that explain the mana pool. Translate system state into accessible language:
- Low confidence (< 0.3): “We’re still learning your patterns. Today’s mana is set carefully — it’s designed to feel comfortable, not like a stretch.”
- Medium confidence (0.3–0.7): “On days you’ve felt like this recently, you’ve usually managed around [X] mana. Today is set a little below that to give you room.”
- High confidence (> 0.7): “Based on your history, [X] mana is a good fit for how you’re feeling today.”
- Post-PEM: “You had a tough stretch recently. Your mana is lower while you recover — it’ll grow back as you feel better.”
- Post-overflow: “You’ve been doing more than your pool lately — we’ve nudged it up a bit.”
- Returning after absence (7+ days, first 3 check-in days): “It’s been a little while — today’s mana is set gently while the app catches up.”
- Quick check-in or skip used: “Your mana is estimated today — check in when you’re ready for a better reading.”
No percentages, no confidence scores, no coefficient values. The user sees the mana number and a human sentence.
11.5 Copy Review Checklist
Before shipping any new UI copy, check:
12. Accessibility
12.1 Standards
- WCAG 2.1 AA (all success criteria)
- WCAG 2.2 AA (adds 2.5.8 Target Size minimum)
- ADA 2024 (DOJ rule requires WCAG 2.1 AA)
- European Accessibility Act 2025 / EN 301 549 v3.2.1
12.2 Key Requirements
Colour & Contrast: All pairings validated against the generated light and dark Material schemes. Use onSurface for primary readable text, onSurfaceVariant for supporting text, and Material accent roles for interactive emphasis. Semantic colours are always paired with a non-colour indicator.
Motor & Touch: 48dp minimum touch targets throughout. 8dp minimum gap between adjacent targets. All gestures have tap alternatives. No time-limited interactions. No precision gestures (discrete taps replace sliders in check-in).
Cognitive: No content auto-dismisses. All form fields have persistent visible labels. Error states explain the corrective action. Single action per screen where possible. The check-in is always bypassable — the tiered system (§7.1) ensures the user is never hard-locked from their task list.
Screen Readers (Flutter Semantics): All interactive widgets wrapped in Semantics with meaningful labels. Mana orb clusters labelled (e.g. “3 mana cost”). Completion circles labelled (e.g. “Complete [task name]”). Live regions on mana number update and task completion. Bottom sheets announce heading on open. Multi-select announces count. Essential task flag announced: “Essential task: [task name]”.
Text Scaling: All layouts tested at 200% text scale. No fixed-height containers that clip. Task cards expand vertically — names never truncate. Icon+label pairs in nav bar reflow to stacked if needed.
12.3 Acceptance Criteria for New Screens
Before a new screen ships:
13. Data & Privacy
13.1 Architecture
The app uses four layers: UI (widgets, screens, navigation), Domain (mana logic, services), Data (local SQLite via Drift), and Platform (notifications). Domain has no outward dependencies — it defines interfaces that other layers implement.
Local-first: The app is fully functional with no internet connection. SQLite (Drift) is the source of truth. Every feature works against the local store alone. No cross-device sync — user data stays on their device.
13.2 Storage
| Store | Contents | Encrypted | Wiped on Reset |
|---|
| SQLite (Drift) | Tasks, check-ins, events, mana state | Yes (SQLCipher) | Yes |
shared_preferences | Settings, feature flags | No | No |
Settings intentionally survive a data reset — notification preferences and accessibility settings are not health data.
13.3 Encryption
All SQLite data is encrypted at rest using SQLCipher. A 256-bit key is generated on first launch and stored in the platform keystore (iOS Keychain / Android Keystore). The key is never written to iCloud, Google Drive, or any cloud backup.
13.4 Delete All Data
Accessible from Settings. Wipes the SQLite database and encryption key atomically. Settings and feature flags survive. Calm, factual confirmation copy:
“This will permanently delete all your data on this device. Your app settings will be kept. This cannot be undone.”
Button: “Delete everything” — no exclamation marks, no “Are you sure?”, no blame framing.
14. What This System Does Not Do
- No streaks. No consecutive day tracking of any kind.
- No leaderboards or comparison. This is a private capacity tool.
- No red mana states. Low mana =
rest purple. Red = system errors only.
- No task counts in headers. The list shows what is achievable, not the full backlog count.
- No animations celebrating high-capacity days. High days are not achievements. They are just high days.
- No internal numbers shown. Coefficient, confidence score, EWMA values, pool percentage — none of these are ever visible. The user sees the mana number and a human sentence.
- No automatic recalibration prompts. The system adapts silently. Recalibration is a user-initiated option, never suggested.
- No “you exceeded your pool” warning framing. Overflow is noted warmly, once per day, never as an alarm.
- No “you should” language. Ever.
- No hard lock-out. The user always has access to their task list, regardless of check-in status.
- No absence penalty. Time away from the app does not decay the user’s data, patterns, or system trust. The app waits.
- No commentary on absence. No “welcome back,” no “we missed you.” The app picks up where the user is.
Changelog — v2.3 → v2.4
Issue 1 — Tiered check-in (no hard lock): §6.1 rewritten from “gated entry point” to “primary entry point.” §6.2 navigation bar updated to remove locked state. §7.1 Mana screen now documents three alternative check-in paths (quick, momentum carry-forward, skip). §12 cognitive accessibility updated.
Issue 2 — Absence handling: §11.2 added “Welcome back” and “We missed you” to banned phrases. §11.4 added returning-after-absence transparency sentence and quick-check-in/skip sentence. §14 added no absence penalty and no commentary on absence.
Issue 3 — Terminology: All references to netMET and impliedNetMET throughout this document have been replaced with relativeCost and impliedRelativeCost. These values represent relative effort for this specific person, not absolute metabolic equivalents. The mana formula, pipeline, and personal library logic are unchanged — only the naming. §9.3 strengthened personal library override: after 10 completions, user patterns permanently override template-derived costs. §9.2 elevated difficulty rating to first detail field after name and duration.
Issue 4 — PEM attribution: §7.6 PEM recovery banner now includes optional “Do you know what triggered this?” link for user-reported crash attribution.
Issue 5 — Essential tasks: New §7.3.1 specifying the essential task flag, surfacing rules, over-pool display treatment, rest-day Essentials section, and essential count limit prompt. §7.3 visibility rule updated with the essential exception. §9.2 Quick Capture gains essential toggle. §11.3 added rest-day-with-essentials copy. §5.4 shield icon usage updated.
This document is the single design and copy source of truth for Canthus. When a new design or copy decision arises, return to the North Star and work forward.