Design System¶
Decided 2026-10-12. This page is the source of truth for how every screen looks and reads: the home page, the therapist and patient web portals, and both sides of the phone app. The values live in code (OktaPT-FE/tailwind.config.js, Okta-Mobile/lib/theme.ts); this page says what they mean and how to use them. Change the code and this page together.
How to use this page¶
Every screen is judged against three priorities in this order: simple, then warm, then professional. When two pull apart, the earlier one wins.
| Priority | What it means on a screen | What it rules out |
|---|---|---|
| 1. Simple | One job per screen, one primary action, the fewest words that still say it. Good defaults instead of settings. | Dense toolbars, nested menus, decorative chrome, two accent colors on one screen |
| 2. Warm | Soft off-white surfaces, warm neutrals, rounded shapes, plain friendly language, real people in photos. | Cold grays, pure black text, sharp corners, clinical jargon |
| 3. Professional | Precise numbers, consistent alignment, legible at a glance, calm motion. Nothing that reads as a toy. | Gradients for their own sake, bouncy motion, emoji in the interface, playful illustration |
A screen that needs something this page does not cover gets a line added here before it ships, not a one-off style.
Foundations¶
Warm paper, white cards with hairlines, one accent, green only for done.
Color¶
| Token | Value | Where it goes | Web class | App key |
|---|---|---|---|---|
| paper | #FAFAF9 |
Screen background (a stronger cream, #F7F4F2, was tried on 2026-10-12 and read as too tinted) |
bg-paper |
colors.paper |
| paper-muted | #F5F5F4 |
Segmented tracks, icon tiles, skeletons, neutral chips | bg-paper-muted |
colors.paperMuted |
| card | #FFFFFF |
Cards, rows, sheets, inputs | bg-card |
colors.card |
| line | #E7E5E4 |
Hairline borders and dividers | border-line, divide-line |
colors.line |
| ink | #1C1917 |
Titles and body | text-ink |
colors.ink |
| ink-2 | #57534E |
Secondary text, meta lines | text-ink-2 |
colors.ink2 |
| ink-3 | #A8A29E |
Placeholders, disabled text, chevrons | text-ink-3 |
colors.ink3 |
| accent | #2563EB (the logo blue; a clinic deployment may override it on the web) |
Primary buttons, links, selection, the active tab, focus rings | bg-accent, text-accent, ring-accent |
colors.accent |
| accent-strong | #1D4ED8 |
Hover and pressed | hover:bg-accent-strong |
colors.accentStrong |
| accent-soft | #EFF6FF |
Selected rows, icon tiles, the "seeing today" pill | bg-accent-soft |
colors.accentSoft |
| good | fill #10B981, text #047857, tint #ECFDF5 |
Done, on track, success | bg-good, text-good-strong, bg-good-soft |
colors.good, goodStrong, goodSoft |
| attention | fill #F59E0B, text #B45309, tint #FFFBEB |
Overdue, needs review, a warning | bg-attention, text-attention-strong, bg-attention-soft |
colors.attention, attentionStrong, attentionSoft |
| problem | fill #DC2626, text #B91C1C, tint #FEF2F2 |
Errors, destructive actions, a flag | bg-problem, text-problem-strong, bg-problem-soft |
colors.problem, problemStrong, problemSoft |
| scrim | ink at 60 % (rgba(28, 25, 23, 0.6)) |
A small badge on a picture, such as the play button on an exercise's photo (app only so far) | colors.scrim |
On the web, gray-* classes map to Tailwind's warm stone scale, so existing code is already on the warm neutrals; new code uses the named tokens above. The brand-* classes remain the per-deployment accent scale (--brand-500 is the tenant's primary colour from GET /v2/branding/theme, default #2563EB); accent is an alias of it. In the app the accent is static for now; per-clinic accents in the app are a later step.
Status mapping: compliance grades A+, A and B are good, C and D attention, F problem, no grade neutral (paper-muted on ink-2). Streak days: done is good, a rest day neutral, a missed day attention, never problem. Pain and exertion ratings use the same three steps. Never more than one status color in a row.
Leaderboard tiers (Starter to Platinum) are neutral chips in Label text, never a status color; four Gold weeks in a row add a small outline trophy to the chip. A board's podium is three cards with the place as the one big number, the first a little taller, names on up to two lines and one short fact under the points ("3 of 3", "14 patients") rather than a cut-off sentence; the viewer's own row is accent-soft and its sentence is read whole, and nothing on a board uses green, since nothing on it is "done".
No dark mode for now; the names allow it later without renaming anything.
Type¶
Nunito everywhere, the face the web already had: self-hosted through next/font on the web (pages/_app.tsx, Latin and Cyrillic subsets), assets/fonts/nunito in the app, registered in app/_layout.tsx and picked per weight through fonts.* in lib/theme.ts. Six roles; no pixel size outside this table.
| Role | Web size / line | App size / line | Weight | Use | Web class | App key |
|---|---|---|---|---|---|---|
| Display | 36 / 40 | 34 / 40 | 800 | The one big number on a screen: minutes, sessions, days | text-display |
type.display |
| Title | 24 / 32 | 28 / 34 | 700 | Screen title | text-title |
type.title |
| Heading | 18 / 26 | 18 / 24 | 700 | Card title, section heading | text-heading |
type.heading |
| Body | 15 / 22 | 16 / 22 | 500 | Rows, paragraphs, buttons (600 for a row's name and button labels) | text-body |
type.body, type.bodyStrong |
| Meta | 13 / 18 | 13 / 18 | 500 | Second line of a row, dates | text-meta |
type.meta |
| Label | 12 / 16 | 12 / 16 | 600 | Chips, captions, table headers, in sentence case | text-label |
type.label |
Spacing¶
Steps of 4: 4, 8, 12, 16, 24, 32, 48 (space.xs to space.xxxl in the app; Tailwind's own scale on the web). Gutter 16 on phones, 24 on desktop. Rows 8 apart, blocks 24 apart. Card padding 16, or 12 on rows with a 44 px avatar. Every tap target is at least 44 px (hit.min); primary buttons are 52 on touch screens (hit.primary) and 44 on desktop.
Shape¶
| Radius | Where | Web class | App key |
|---|---|---|---|
| 8 | Thumbnails inside rows, small controls | rounded-lg |
radius.sm |
| 12 | Inputs, buttons, menus | rounded-control |
radius.control |
| 16 | Cards, rows, icon tiles | rounded-card |
radius.card |
| 24 | Sheets and hero cards | rounded-sheet |
radius.sheet |
| Pill | Chips, segmented controls, the tab bar, avatars | rounded-full |
radius.pill |
Elevation¶
Cards and rows are flat: card white on paper with a 1 px line. One shadow exists, for things that float (menus, the tab bar, toasts): offset 0 4, blur 12, #1C1917 at 8 percent (shadow-float, shadow.float). Dialog backdrops are #1C1917 at 40 percent (colors.backdrop).
Motion¶
150 ms in, 200 ms out, ease-out, no bounce. Pressing scales to 0.98 with a light haptic in the app. Sheets slide up in 240 ms. Reduced motion turns all of it off except opacity. (motion.* in the app.)
The one exception is finishing a session (decision 12). The app celebrates it once, the same way however the session was finished: a thin ring in the done colour pulses out of the point of completion and a burst of confetti in accent, good and attention rises and falls for 1.2 s (motion.celebrate), the check on a done view pops in with a small overshoot, and the phone plays the celebration rhythm: a firm tap, the success chime, then two soft taps as the pieces land. It lies over the screen without catching touches and says nothing to screen readers. Reduced motion keeps the haptics and skips the burst. Nothing else gets confetti, and new motion stays calm. (SessionCelebration, CelebrationCheck and hapticCelebrate() in the app.)
Icons and imagery¶
Outline sets only: Heroicons 24 outline on the web, Ionicons outline in the app. 20 px inline, 24 px in bars, filled only for the active tab. No emoji as icons: the streak uses a flame icon, warnings a warning icon. Photos of real people belong on the home page; product screens use the patient's own exercise thumbnails and icons, not illustrations. AI-written content is never marked with a sparkle or star icon; say it in words.
Components¶
These parts cover every screen. Each exists once per platform; a screen that needs a variant adds a prop, not a copy.
| Component | Looks like | Rules |
|---|---|---|
| Button, primary | Accent fill, white Body text, radius 12, 44 tall on desktop and 52 on touch screens | One per screen. Loading swaps the label for a spinner and keeps the width |
| Button, secondary | Card fill, 1 px line, ink text, same size | Pairs with a primary; never two secondaries alone |
| Button, text | Accent text, no box | Cancel, See all, Skip |
| Button, destructive | Problem text, outlined; filled only inside a confirm dialog | Always behind a confirm step |
| Card | Card fill, radius 16, 1 px line, padding 16, no shadow | Heading at the top, one action at most, never a card inside a card |
| Row | 44 avatar or 48 thumbnail (radius 8), name Body 600, Meta line, right slot for a chip or chevron; at least 64 tall | 8 px between rows, one fact per line |
| Suggested request | A full-width button for a request the person is likely to make next: card fill, 1 px line, a name and a reason line. Web: radius 12, at least 44 tall, Meta 600 label over a Meta ink-2 reason, both wrapping. App: a Row (Body 600 name, Meta line, chevron), two lines each | Before the person types, in an assistant's empty state; a tap sends the request as if typed. Up to five on the web, three in the app sheet; skeleton rows while the first list loads, the previous list dimmed and disabled while a fresh one comes. Never pills, never marked as AI |
| Media row | A row whose picture fills its left edge, 96 by 72 (the card's radius clips it; a paper-muted tile with an icon when there is none), then name Body 600, the dose in Meta, at most two neutral chips, a one-line note; a 44 px trash (ink-2) or chevron column at the right | For lists a therapist edits exercise by exercise (the HEP editor in the app). The picture is read first, so nothing covers it: a 24 px scrim badge (play, expand; problem red for a failed video) sits 8 px into its corner |
| Chip | Pill, Label text, tint fill and text from one status family; neutral is paper-muted on ink-2 | At most one status color per row |
| Input and search | Card fill, 1 px line, radius 12, 44 tall (48 in the app), placeholder ink-3, 2 px accent ring on focus | Search gets a leading icon and a clear button. A value filled in by AI stays marked, accent line on accent-soft, until the person edits it or saves |
| File drop (web) | Dashed 1 px line on paper, outline icon in ink-3, a "choose files" link in accent; accent line on accent-soft while a file is over it | Says which files it takes and that pasting works too |
| Segmented control | Paper-muted track, radius 12, padding 3; active segment card white, radius 9, soft shadow; Label 600 | Two or three views, never a navigation; four only for parallel rankings of the same thing (the therapist leaderboard), where labels stay whole and the track scrolls sideways when they do not fit (scrollable in the app) |
| Choice tiles (app) | Two tiles side by side under a Body 600 question: card fill, 1 px line, radius 12, at least 52 tall, outline icon and Body 600 label; the chosen one accent line on accent-soft with accent text; a Meta hint underneath that turns problem-strong when the answer is missing | A required either-or question inside a form (who a new video is for), answered in one tap; no default, so the hint says what to choose |
| Question card (app) | A card in a sheet's footer, in place of its primary button: Heading question, one Meta line, then a primary and a secondary button | A one-time question before an action goes through (the HEP picker's "Is this video for {Name} only?"); the action runs once it is answered |
| Tabs (web) | Underline tabs, accent for the active one | Sections within a page; never on the same screen as a segmented control |
| Toolbar tabs (web) | The segmented track at toolbar height: paper-muted, radius 12, padding 3, 44 tall; active tab card white, radius 9, soft shadow, accent text; Body 600 | A page's tabs at the head of a toolbar of 44 px controls (the exercise library: All, My private, Favorites). A view toggle in that toolbar takes the same track, icon only, each button named by a tooltip and an accessibility label |
| Sheet | App: from the bottom, top radius 24, 36 by 4 handle in line color, 40 percent backdrop. Web: bottom sheet on phones, centered radius 16 on desktop. A work surface where a plan or a template is put together (the plan builder, the template editor) fills the screen on desktop too: a compact header over a hairline (the title level with the close button) and a compact footer | One sheet component per platform; no hand-made overlays |
| Empty state | Icon 48 in ink-3, Heading, one Meta line, one primary button | Says what will appear here |
| Loading | Skeleton rows in paper-muted with a shimmer; spinner only inside a button | Never a full-screen spinner over existing content |
| Toast | Card fill, radius 12, floating shadow, status icon, gone after 3 s | One at a time, bottom on phones |
| Notice | Next to a form, radius 12, padding 12, a 20 status icon then the text. Error: problem-soft fill, problem-strong text. Info: accent-soft fill (an accent text button on it keeps 4.5:1), accent icon | Says what happened and what to do next. May carry one text button as that next step and never two: after a refused email sign-in the "Forgot password?" link gives way to the notice "Did you forget your password?" with Reset password, and the reset sheet opens on the email already typed (FormNotice with action in the app, ForgotPasswordPrompt on the web) |
| Stat tile | Label in ink-2 over a Display or Heading value with a small unit | Three per row at most |
| Progress | Ring, 6 px stroke, accent on paper-muted, for one completion; bar 8 px pill for a timeline; done turns good green | One progress element per card |
| Table (web desktop) | Header Label in ink-2 on paper-muted, sentence case; rows 56 tall with hairlines; hover paper | Switches to Rows under 768 px |
| Set log | One header row of Labels in ink-2 (Set, Reps or Seconds, the weight unit) over one line per set with hairlines: the set number, number boxes (Input, 44 tall on the web and 48 in the app, up to 96 wide) and a 44 round tick, card with an ink-3 ring, good-strong when done; a done set's boxes tint good-soft | Labels once, never repeated on each line. The plan shows in ink-3 inside the boxes of sets not done yet, what was entered in ink; an empty box on a done set reads "—". The grey weight in an empty box is the therapist's weight for that set, else a weight present in a set above, else the same set's weight last time. Under the dose, a Meta line in ink-2 with an outline clock (ink-3) says the last logged result, "Last: 4 × 5 · 35 lbs", with the day after 30 days and always for the therapist ("Last, Oct 24: …") |
| Avatar | Pill; initials Body 600 on accent-soft for patients and paper-muted for staff; photo when there is one | 44 in rows, 36 in lists of staff |
| Tab bar (app) | Floating pill, 64 tall, one accent for every tab, Label text under each icon | Accessibility label on every tab |
| Top bar (app) | 44 px back target, Title left or Body 600 centered | One right-side action at most |
| Provider sign-in (app) | Above a sign-in or sign-up form: Apple's own button on iOS (the system draws it; white outline, radius 12, 52 tall), then Google as a secondary button with the Google mark, then an "or" hairline | Only where an account is signed in or created; a provider's brand color never becomes the screen's; a build without a provider draws nothing |
| Provider sign-in (web) | Above a sign-in or sign-up form: Sign in with Apple as a secondary button (white, hairline, radius 12, 44 tall) with the Apple mark, then Google's own button (Google draws it: outline, large, 40 tall with its own corners), then an "or" hairline | Only where the site offers them; never the primary button; Google's button cannot be restyled, so nothing else is matched to it; a site without a provider, or with Google's script blocked and no Apple, draws nothing (no lone "or" line) |
Patterns by surface¶
The same tokens everywhere; what changes per surface is density and the one job each screen has.
- Home page. The marketing pages have their own palette and display face, below. Cards with a 1 px ring, buttons with the 12 px radius, one primary call to action per section; accent only on links and that button.
- Therapist web. Dense but calm. The dashboard is a Table on desktop and Rows on phones, with one primary action at the top right (Add patient). Status lives in Chips; sub-tabs are plain tabs with counts, not colors. The patient popup stays a Sheet with tabs.
- Patient web. The patient app, larger: today's plan as a Card with one Start button and a progress ring, history as Rows. The player stays full screen.
- Doctor app. The reference the system grew from. Keep its structure (hairline rows, 8 px gaps, 44 px targets, one fact per line) and use the tokens: paper, line, accent. No hand-rolled buttons. The HEP editor's week is slim day headings on the paper (Heading date, Meta session and count, the focus on its own Meta line, a text Add and a quiet more button) over Media rows, 24 px between days. A Program row is a card whose top section has the picture at the left (120 wide, 104 under 390 px) beside the name, a strip of day dots and a dose chip (paper-muted, Meta in ink; accent-soft with accent text while open). The strip keeps every session on one row: dots are 20 px for a normal week and shrink for more sessions, a day's sessions sit 1 px apart and the days 4 px; below 16 px the letters drop and empty dots take the line colour. Under the top section, one at a time, the strip unfolds a sideways-scrolling row of 36 px preset chips (paper-muted, the current one accent-filled), with 44 px tall day chips for Custom, and the dose chip unfolds the full-size steppers; the note runs across the card below, behind a hairline. The presets and the dose are choice chips, not pill buttons (decision 4), and each is a 44 px target.
- Patient app. The doctor app's Card and Row for the workout, visit and screening cards. One accent across messages, history and profile. The streak uses an icon. The tab bar has one accent and labels.
- Admin. Internal; swaps blue-600 for the accent and keeps its kit.
- Email. One layout for every email (
OktaPT-API/src/services/notifications/templates/layout.ts, decided 2026-10-17): the brand name in ink above a white card with a 1 px line and radius 16 on paper, 560 px wide, no logo image, gradient or shadow. Nunito where the reader has it, the system font otherwise; no web font is fetched. Inside: one Title, the greeting, body text, at most one accent button (the second action a secondary button), a paper-muted panel for the thing to notice, hairline rows for exercises, Meta lines for what a link does and how long it works; under the card, a Meta line on why the person got it. Wording, brand and language rules and the list of emails: Transactional emails.
Marketing pages¶
The home page, Pricing, Team, Careers and Contact (both brands) and the footer, approved on 2026-10-13. They stay simple and professional but carry more warmth than the product: a photo of real-looking people (AI-generated, no real person), a serif display face and a little colour. None of this is used inside the product.
| Token | Value | Where it goes | Web class |
|---|---|---|---|
| marketing-paper | #F4F7FC |
Every other section's background: paper with a hint of the logo blue | bg-marketing-paper |
| marketing-tint | #E9EEF8 |
Icon tiles, chips and tracks inside the product mocks | bg-marketing-tint |
| marketing-wash | #F7F9FD |
The lightest fill inside cards and mocks | bg-marketing-wash |
| marketing-stage | #EEF2FA |
The flat stage behind a section's product visual | bg-marketing-stage |
| marketing-line | #DDE5F4 |
Hairlines on the tinted paper | bg-marketing-line |
| marketing-navy | #0B1739 |
Dark panels: the AI band, the closing call to action, the highlighted plan, the footer | bg-marketing-navy |
| marketing-sunrise | #FFF6EC |
The top of the warm wash over every hero, fading to white | from-marketing-sunrise to-white/0 |
| marketing-ember | #9A5A2A |
The warm shadow under the hero photo, at 20 percent | shadow-marketing-ember/20 |
- Type. Headlines (the hero, section titles and big figures) use Lora 400 through
next/font(components/landing/shared/displayFont.ts), sized from Tailwind's scale (4xl to 7xl), with balanced wrapping (text-balance) so no line is left holding one word; subtitles usetext-pretty. Body text stays Nunito. - Imagery. One family of soft clay-style illustrations (AI-generated, on a plain colour, no text) on the platform cards, the loop, the onboarding steps, the AI band and the compliance strip. Product visuals are text-built mocks with fictional people, so they translate and show no patient.
- No pastel glows on light surfaces. Warmth comes from the sunrise wash, the photos and the clay art; radial glows live only on navy. Solid warm blocks behind images read as orange and were dropped.
- Eyebrows only where a title alone would not say who a section is for (the owner and therapist sections).
Narrow screens¶
Every screen is checked at 360 and 390 px, at 768, and at 1024 to 1920 on desktop, in English and Russian (Russian labels run about a fifth longer).
- Text keeps the width. When a card or bar has text and a button, the button goes under the text on phones, full width, instead of squeezing the text into a narrow column beside it. Side by side is for
smand up. - Labels never wrap. Buttons, tabs, nav links and segmented controls keep their label on one line. Two buttons in a row share it evenly and drop their icons on phones; a row of tabs scrolls sideways rather than pushing the page wider than the screen.
- The desktop header gives way in steps. When the nav doesn't fit, its links drop their icons first, then the menu button stands in for the whole nav, as on a tablet.
- Tabs and their toolbar share a line only when both fit. Otherwise the toolbar drops under the tabs as one row; a tab is never cut off and a control never wraps onto a second line.
- Tables lose a column, not their actions. Between 768 and 1024 px a table hides its least needed column so the actions stay on screen.
- Short windows keep the list. Under 900 px of height (
short:in Tailwind, stacked asshort:md:; a 13–14" laptop with the browser's bars and a taskbar) a sheet that holds a list takes almost the whole window (the plan builder and the template editor fill it at any height). In an exercise picker only the search box stays pinned; the scope buttons, the tag filter, the chosen tags and the AI results line scroll away with the cards, so two rows of cards stay in view. The AI results line is one line: "AI: “knee” · 10" with a small ×, the query cut short when long.
Voice and writing¶
Write the way a good therapist talks in the room: short, concrete, kind. Patients get everyday words; therapists get clinical terms where they are faster (ROM, RTM, HEP), never where a plain word does the same job.
| Rule | Say | Not |
|---|---|---|
| Buttons are verbs, one or two words | Start, Mark done, Send, Add patient | Submit, OK, Click here to continue |
| Titles say what the screen is for the person reading it | Your plan for today, Patients to review | Assigned HEP, Dashboard |
| One warm line at a milestone, none in between | Nice work. That is five days in a row. | Awesome!!! You crushed it! |
| Errors say what happened and what to do next | The video did not upload. Check your connection and try again. | Error 500, Something went wrong |
| Empty states name what will appear and offer the one action | No patients yet. Add your first patient. | Nothing to show |
| Numbers carry a unit, in words a person uses | 12 min, 3 of 5 done, 110 degrees | 00:12:00, 60%, 110° ROM |
| Weights use the clinic's unit: kg on OKTA, lbs on OTERA, kept with the number on one line | 16 kg, 35 lbs, 2,5 кг | 16, 35 lb on an OKTA screen |
| Exclamation marks: at most one per screen, only for a result the person earned | Workout complete! | Hello Patient! |
AI-written content is labelled in words next to it ("Written by AI from the visit captions") and reviewed by the therapist before a patient sees it. Every string ships in English and Russian together; Russian uses the formal "вы" everywhere.
Decisions¶
The first eleven were made on 2026-10-12, the twelfth on 2026-10-16. Reopen one by changing the code and this page together, with the reason in the changelog.
| # | Decision | Outcome |
|---|---|---|
| 1 | The accent | The logo blue #2563EB on web and app; the app's emerald is the "done" color only |
| 2 | Neutrals | Near-white paper #FAFAF9, line #E7E5E4, stone inks; web gray-* aliased to stone. The cream #F7F4F2 was tried and rejected the same day as too tinted |
| 3 | Typeface | Nunito on both platforms, one six-step scale. Manrope was tried the same day and the web's Nunito was preferred |
| 4 | Shape and elevation | Cards 16, buttons and inputs 12, sheets 24, chips pill; flat cards with hairlines, shadow only on floating things; no pill buttons |
| 5 | Buttons | One primary per screen; 44 on desktop, 52 on touch; secondary white with a line; destructive as red text |
| 6 | Status colors | Three families (good, attention, problem) plus neutral; grades and streak days map onto them |
| 7 | App tab bar | One accent, labels under the icons, accessibility labels; the floating pill stays |
| 8 | Home page hero | Lora headline on white under a warm wash, two calls to action and a clinic photo with product cards; the marketing pages keep their own palette (see Marketing pages). Settled 2026-10-13 |
| 9 | Icons and emoji | Outline icons only; no emoji as icons |
| 10 | Dark mode | Parked; token names allow it later |
| 11 | Where the system lives | Tokens in code, this page, lint against raw hex in the app |
| 12 | Finishing a session | The one playful moment: a 1.2 s confetti burst with a haptic rhythm when a patient finishes a session, whichever way they finish it; calm motion everywhere else stands |
Rollout¶
- Tokens (landed in the
design-tokensbranches):tailwind.config.jsandstyles/global.csson the web (palette, type roles, radii, the float shadow, Nunito throughnext/font,grayaliased to stone, paper as the page background);lib/theme.ts, the Nunito files and theapp/_layout.tsxfont gate in the app; a lint warning on raw hex values outside the theme file. - Primitives (landed in the
design-tokensbranches). Web, incomponents/ui/:Button(themesprimary,secondary/default,text/alternative,danger; sizes xs 32, sm 36, md 44;loadingkeeps the width),Card(DashboardCardnow renders it, framed on phones too),Chip,InputandSearchInput(components/Input.tsxwraps them),Sheet(Modalis a wrapper over it),EmptyStateandTable(the admin copies re-export). App, incomponents/ui/:Card,Row,Chip,InputandSearchInput,EmptyState,Avatar,Sheet;PrimaryButton(52 tall, accent;brandandprimaryboth mean the accent,recordis problem red),SecondaryButtonandSegmentedrestyled; the tab bar has one accent, labels under the icons and accessibility labels. - Screens, in this order: doctor app patient management (landed: dashboard, rows, grades and flags, search, today picker, patient detail, quick analysis, visits, the two onboarding modals' buttons), therapist web dashboard and patient popup (landed: dashboard tabs and sub-tabs, patient table and phone cards, archived / assign / resend modals, popup header, Profile, Cases, Workouts and Videos tabs), patient app (landed: home and streak header, workout cards and the whole workout flow, history, messaging, profile, the sign-in theme, loading / error / toast states), patient web (landed: dashboard, plan management, the player and its sheets, completion, feedback and setup modals, shared ConfirmDiscardDialog / DOBFieldInput / TherapistBubbles / PDF button), then admin. The classic plan editors, Edit session, Create session and the exercise picker landed on 2026-10-21, sharing the HEP builder's dose controls (
components/plan-creation/hep-builder/controls/), whose labels moved to the Label role; the feedback card in Messages the same day. The home page v2 was rebased onto the tokens on 2026-10-14; its colours are themarketing.*tokens. - Guardrails. The app's hex lint goes from warning to error when the count reaches zero; the mobile design doc is cut down to what the code does.
References¶
The six reference shots agree on one recipe: a light off-white screen, white cards with large radius, one accent, pill chips and buttons, rows of thumbnail + name + one fact, real photos, almost no shadows. We took the recipe and left the gradients.
- Pilates Workout App and Pilates Fitness App by Imran Jakir: exercise rows, pill filters, one big Start, player controls as three pills.
- AthletiQ training and AthletiQ programs by Panze: one big number as the hero, three-up stats with small units. Gradients and the dark tab bar were left out.
- Training, analytics and progress by Helal Hossain: calendar with completed days, two-tile stat row, one-tint bar chart.
- FitFlow by Sienna Rose: cream background with a single accent, hairline card borders, a human line on the summary ring. The warmest of the six.
- In-house: the doctor app's patient management screens (
components/doctor/PatientRow.tsx,DoctorDashboard.tsx): hairline rows, 8 px gaps, 44 px targets, one fact per line.