Design System
14 colours. 10 tokens. One font. Generated from token_data.dart. If it is not here, it does not exist, and adding one is a decision.
Specimens render in dark, the only theme the app ships. Light is defined in the tokens and listed below, but no screen reads it yet.
Color
Semantic
The ten tokens screens read. Never a raw hex, never a primitive. Every one resolves in both themes, so a screen written once works in both.
| Token | Light | Dark |
|---|---|---|
| surfaceBase | #F7F3EC | #17111F |
| surfaceRaised | #FFFDF8 | #211829 |
| surfaceSunken | #EFE9DE | #0F0A15 |
| textPrimary | #2E0249 | #F7F3EC |
| textSecondary | #7B7286 | #7B7286 |
| border | #2E02491F | #FFFFFF1F |
| brand | #2E0249 | #9B5FC9 |
| accent | #F47C7C | #F47C7C |
| success | #22C55E | #22C55E |
| danger | #EF4444 | #EF4444 |
Color
Palette
All 14 primitives, the whole set. Screens never touch these. They exist only so the semantic layer has somewhere to point.
brand
violet700
#2E0249
violet300
#9B5FC9
pink400
#F47C7C
neutral
0
#FFFDF8
50
#F7F3EC
100
#EFE9DE
500
#7B7286
800
#211829
900
#17111F
950
#0F0A15
feedback
green
#22C55E
red
#EF4444
premium
gold400
#FFD700
gold600
#FFA000
Brand
Mark
Wordmark on the brand gradient. Violet to pink, top to bottom, never reversed. Text on the gradient is always white.
Type
DM Sans
One family, six sizes. Weight does the work, not a second typeface. Display and body are the same font at different weights.
display . 32px . 700
Never be bored again.
h1 . 24px . 700
Never be bored again.
h2 . 18px . 600
Never be bored again.
body . 16px . 400
Never be bored again.
bodySm . 14px . 400
Never be bored again.
caption . 12px . 500
Never be bored again.
Space + radius
Rhythm
4pt base, seven steps. Four radii, closed set: sm for inputs, md for containers, lg for the date card, full for pills. Do not invent an eighth.
spacing
x14x28x312x416x624x832x1248radius
sm
md
lg
full
Surface + elevation
Depth
Three surfaces: sunken for inputs, base for the page, raised for anything that lifts. Shadow is for lift, not decoration. On dark the border carries the edge, because shadow does not read.
Motion
Timing
Three durations. Fast for state flips, base for most transitions, slow for a card leaving the deck. Nothing custom, ever.
120msbase 200msslow 320msWeb replicas of the Flutter widgets, driven by the same generated tokens. Change a token, these change with it.
Buttons
Actions
Primary is the brand gradient, pill shaped, white text. One primary per screen. Ghost is the secondary. Never two primaries side by side.
primary
ghost
primary . disabled
Cards
Containers
Raised lifts off the base and takes a shadow. Flat is for static rows inside a list. Radius md for containers, lg is reserved for the date card alone.
Raised
Lifts off the base. Sheets, dialogs.
Flat
Static rows inside a list.
Signature
Date card
The one component WeDo is recognised by. The photo IS the card: full bleed, scrim into the surface, meta line, title, one text action. Largest radius in the system and nothing else may use it. The block below stands in for the photo.
Status
Markers
Success and danger, tinted at 12 percent over the surface. These two are the only status colours. No warning, no info, no third state.
Forms
Inputs
Fields sit on the sunken surface so they read as recessed. Chips are the quiz control, single select, brand fill when active.
text field
chips . single select
Lists
Rows
Matches and Memories are hairline separated rows, never cards. One row, one action. A card here would make every row shout equally.
Paywall
Plan card
Selection is a 1.5px accent border plus a 10 percent tint and a filled radio. The saving badge overlaps the top edge. The struck price is twelve months at the monthly rate, never an invented number.
Paywall
Benefits and locks
Gold appears here and nowhere else. A gold check means Pro includes it, a gold lock means Pro opens it. Never use gold for emphasis in the core loop.
benefit rows
locked panel
Free keeps the last 5 readable. Pro opens all of them.
Paywall
Timeline
Used to show what a trial actually costs and when. The live step glows gold, past and future steps sit quiet. Three steps maximum.
Sheets
Sheet header
The drag handle sits on the photo, so a sheet never opens with a strip of chrome above the content. Title, then a meta line in caps.
States
Empty
A sentence that says what will fill this screen, and how. Left aligned, no illustration, no icon. The screen is empty, so the copy carries it.
No memories yet
Finish a date and it is kept here, with whatever you wrote about it.
Navigation
Tab bar
Icon over label, each in its own pill. The active tab takes an accent tint and a filled icon. Labels are sentence case and never truncate.
Gold is paywall only
Badges, benefit checks, locks. Never in the core loop.
Hairlines, not cards
A list of cards makes every row shout. Rules separate, cards contain.
One font
DM Sans, weight does the work. A second family was tried and dropped.
Gradient on the CTA
One primary per screen. Nothing else gets the brand gradient.
No emoji, anywhere
Not in copy, not as an icon, not as an image fallback.
Never a raw hex
Screens read the ten semantic tokens. Adding a token is a decision.