Calm by
design.
Everything you need to design for ago — colors, type, components, motion, and the language behind them. One question answered beautifully: how long has it been?
Brand Foundation
Ago is a calm iOS app built around one question: how long has it been since I last did X? The brand reflects quiet confidence, not productivity anxiety.
Voice & Tone
Ago speaks plainly, warmly, and without fuss. No hype, no pressure, no em dashes. One sentence says what three can't.
| Rule | Reason |
|---|---|
| No em dashes | Use a hyphen or rewrite the sentence instead |
| No exclamation marks | Confidence, not excitement. Excitement is noise |
| Sentence case | Title Case feels formal; sentence case feels human |
| Active voice | "Log it" not "It can be logged" |
| U.S. spelling | Organize, color, recognize |
| Short over clever | If it needs explaining, it's not done yet |
Design Principles
Five rules that govern every feature, screen, and element. When a choice is unclear, come back to these.
Colors
Derived from OKLCH. Warm off-white surfaces, dusty sage accent, warm grey-brown neutrals. Never cold, never wellness-pastel. Click any swatch to copy the hex.
| Token | Value | Swatch | Usage |
|---|
| Token | Value | Swatch | Usage |
|---|
Card Palette
60 identity colors for items. Colorblind-safe hue spread (informed by Okabe-Ito). Each shows light tint / accent. Identity color is decorative only - status is never communicated by color alone.
Typography
Manrope — a geometric sans-serif with excellent weight range. All hierarchy is created through weight contrast, not size jumps. Letter-spacing tightens at larger sizes.
Spacing
No formal spacing scale - values are derived from iOS HIG conventions and the specific needs of the card layout. Key values below.
Border Radii
Item Card
The core unit of the app. 104px tall, borderRadius 14, hairline border. The elapsed number is the hero - ExtraBold 32px, identity accent color. Name is SemiBold 14px.
| Element | Value | Notes |
|---|---|---|
| height | 104px | Fixed. Creates consistent rhythm in the list |
| borderRadius | 14px | |
| borderWidth | hairline (~0.5px) | StyleSheet.hairlineWidth |
| paddingLeft | 18px | Content start |
| paddingRight | 62px | Clears check button area |
| gap between cards | 8px | marginBottom on each card |
| hero number | 32px / ExtraBold / tracking -1.2px | Identity accent color |
| hero unit | 13px / Regular | textSecondary color |
| item name | 14px / SemiBold / tracking -0.1px | text color |
| dot / icon size | 7px dot / 15px icon | Identity accent color |
| check button | 28x28px circle | Right 18px, vertically centered |
Item Detail Screen
Full-screen view using the card's identity light/dark colors as background. Hero number is 80px ExtraBold, letter-spacing -3. Done Today is the primary CTA.
| Element | Value | Notes |
|---|---|---|
| header background | card.light / card.dark | Identity color, not theme surface |
| hero number | 80px / ExtraBold / tracking -3 / lh 0.95 | Much larger than card (32px). Color: card dark tint at 95% opacity |
| hero unit | 17px / Regular | Same text color as hero |
| Done Today button | accent bg / borderRadius 14 / paddingV 16 / 16px SemiBold | Uses card accent color, not theme accent |
| Done Today (already done) | accent at 16% opacity bg / accent text | Disabled state — ghost of accent color |
| Delete item | danger color / 15px Medium / center | Text-only destructive action, no background |
Sheet
Native iOS pageSheet with swipe-to-dismiss. All sheets share the same header pattern: surfaceElevated background, 32px top radius, drag handle, Feather X close button.
| Property | Value |
|---|---|
| background | surfaceElevated (#EEECEA / #272724) |
| borderTopRadius | 32px (SHEET_RADIUS) |
| drag handle | 36x4px, borderRadius 2, textTertiary at 40% opacity |
| handle padding-top | 10px above, 6px below |
| content padding | 20px horizontal |
| animation | Native iOS pageSheet slide — swipe to dismiss |
Sort Sheet
Action sheet variant — no drag handle. Springs up from bottom, transparent overlay, tap outside to dismiss. Same SHEET_RADIUS 32. List options with check indicator for selected.
| Property | Value |
|---|---|
| background | surfaceElevated |
| borderTopRadius | 32px (SHEET_RADIUS) |
| header | 17px Bold · paddingH 20 · paddingTop 16 · paddingBottom 10 |
| option row | 16px · paddingH 20 · paddingV 15 · hairline border-bottom |
| selected | SemiBold weight + Check icon 18px accent color |
| animation | Spring from bottom (tension 60 friction 11), no drag handle |
FAB + Tab Bar
A floating pill tab bar with the FAB alongside, centered as a unit at the bottom of the screen. Fixed total width: 238px.
| Token | Value |
|---|---|
| FAB_SIZE | 54px |
| FAB_TAB_GAP | 12px |
| pill internal padding | 4px — gives active fill breathing room |
| tab item size | 82 × 46px · borderRadius 18px |
| pill outer size | 172 × 54px (= 2×82 + 2×4 = 172, 46 + 2×4 = 54) |
| TAB_PILL_WIDTH | 172px |
| BOTTOM_ROW_WIDTH | 238px total (172 + 12 + 54) |
| TAB_BAR_HEIGHT | 54px (= FAB_SIZE, pill outer height) |
| TAB_BAR_BOTTOM_MARGIN | 8px |
| FAB color | accent (#3E7859 light / #5A9470 dark) |
| FAB shadow | 0 4px 8px rgba(62,120,89,0.3) |
| active tab fill | accent bg · white icon (fill weight) · borderRadius 18px |
| inactive tab | no bg · textTertiary icon (regular weight) |
| active pill animation | Spring scale 0.7→1 + opacity 0→1 on focus (tension 280, friction 18) |
| FAB + tab bar on detail nav | Both live at Tab Navigator level (outside ItemsStack). Fade opacity 1→0 over 180ms when Detail is pushed; fade back 0→1 on return. FAB presses trigger add via fabStore event. |
Buttons
All button patterns used across the app. No standalone button component — styles are applied inline per context.
| Variant | Style |
|---|---|
| primary | accent bg · borderRadius 12-14 · paddingV 15-16 · 16px SemiBold · white text |
| primary disabled | surfaceElevated bg · textTertiary · same shape |
| done (already logged) | accent at 16% opacity bg · accent text · same shape |
| text link | accent or textTertiary · no bg · 13-14px · hitSlop 8-10 |
| destructive | danger color · 15px Medium · no bg · center · paddingV 14 |
Input Fields
| Element | Value |
|---|---|
| text input | surfaceElevated bg · 1px border · borderRadius 10 · padding 12 14 · 15px Regular |
| date picker button | Same as input · row with chevron-down at right · textTertiary chevron |
| pill unselected | surfaceElevated · border · borderRadius 20 · padding 8 13 · 13px Regular · textSecondary |
| pill selected | accent bg · accent border · white text · 13px SemiBold |
| unit chip selected | card accent bg · card light color text · 12px SemiBold |
| unit chip unselected | accent at 13% opacity · accent text · 12px Regular |
| field label | 10px Medium · textTertiary · letter-spacing 0.8px · UPPERCASE |
Settings Row
Reusable list row: colored icon wrap, label, optional value or Pro badge, chevron. Grouped rows share surface background with hairline separators and borderRadius 14.
| Element | Value |
|---|---|
| group container | surface bg · hairline border · borderRadius 14 · overflow hidden |
| row | paddingH 14 · paddingV 13 · gap 12 · hairline border-bottom between rows |
| icon wrap | 30x30px · borderRadius 7 · white icon 15px · color varies per action |
| label | 15px Regular · text color |
| value / trailing | 15px Regular · textSecondary color |
| chevron | Feather chevron-right · 16px · textTertiary |
Pro Banner
Floating card shown to free users above the tab bar. Tappable — opens paywall. Height 68px, positioned absolute 16px above TAB_BAR_TOTAL.
| Property | Value |
|---|---|
| height | 68px (BANNER_HEIGHT) |
| background | surface |
| borderRadius | 16px |
| border | hairline · border color |
| icon wrap | 36x36 · borderRadius 10 · accentSubtle bg · Sparkle fill 18px |
| title | 13px SemiBold · text color · tracking -0.1px |
| subtitle | 11px Regular · textSecondary |
| unlock pill | accentSubtle bg · borderRadius 20 · 12px SemiBold · accent text |
| position | absolute · left/right 16 · bottom TAB_BAR_TOTAL + 12 |
States
| Property | Value |
|---|---|
| background (success) | colors.text — inverted: #1C1C1A in light, #F0EFEB in dark |
| background (error) | colors.error (#C94040 light / #D45555 dark) |
| text (success) | colors.background — inverted: #F6F5F2 in light, #141412 in dark · 14px Medium |
| text (error) | colors.textOnAccent (white) · 14px Medium |
| action label | colors.accent · 14px Bold · underline |
| borderRadius | 10px |
| padding | 14px vertical · 20px horizontal |
| position | left 16px, right 16px, above tab bar |
| animation | Spring from bottom (translateY 40 → 0, scale 0.92 → 1) |
Motion
Purposeful, never gratuitous. Every animation serves a transition or provides feedback at a meaningful moment. Spring physics for spatial moves; easing curves for opacity/scale.
Icons
Phosphor Icons (phosphor-react-native). Used at 15px in cards and 22px in the tab bar. Always weight="bold" in cards. Item icons use the card's identity accent color.