Skip to content

ClinicFlow — The Apple Playbook & Execution Plan

ClinicFlow — The Apple Playbook & Execution Plan

Section titled “ClinicFlow — The Apple Playbook & Execution Plan”

Goal: Every surface a human can see looks and feels like Apple shipped it as of June 2026 — consistent, intentional, and expensive. Not “themed like Apple,” but built to Apple’s actual discipline.

This document is the single source of truth for the visual rebuild. It supersedes the loose “gold standard” notes. Author: Claude (Opus 4.8), 2026-06-26.


Updated 2026-06-27 after full Phase 3–5 implementation. Original audit was 2026-06-26 at ~75%.

The implementation plan (docs/superpowers/plans/2026-06-26-dashboard-apple-elevation.md) is complete. All phases executed and verified.

Area Grade Status
Design tokens (index.css :root) A 30-token system, full dark-mode parity, design:guard ✅ zero violations
DashboardLayout shell A Liquid Glass sidebar, flex scroll architecture (no calc hacks), Apple nav density
CalendarPage A− Flex height, no magic numbers, spring motion, reduced-motion guards
Landing A− Static inline styles swept; dynamic values (animations, gradients) documented-exempt
Auth (Login/Register) A− Tailwind sweep complete
Core CRUD pages A All use canonical ui/ primitives: Card, Button, Badge, PageHeader, Table, StatusPill
Shared modals/drawers A− Phase 5.4 sweep complete; ProfileEditModal, AssistantDrawer, OnboardingChecklist converted
Dental pages A− TreatmentPlansPage, PaymentsPage, RecallCampaigns: all glass-panel → bg-card + primitives
SuperAdmin B Own isolated design system; inline styles are out of dashboard scope (internal-only)

Final gate results (2026-06-27, branch feat/dashboard-apple-elevation-2026-06-26):

  • npm run typecheck ✅ clean
  • npm run lint ✅ clean
  • npm test ✅ 38 test files, 166 tests passing
  • npm run design:guardzero violations

Bottom line: Done. A clinic owner cannot point to two screens that feel like different products.


1. The Apple Playbook (June 2026) — Principles That Govern Every Component

Section titled “1. The Apple Playbook (June 2026) — Principles That Govern Every Component”

Grounded in Apple’s HIG + the 2025/2026 Liquid Glass language, refined by current research. These are the rules every screen must obey.

The three laws (Apple’s own): Clarity · Deference · Depth

Section titled “The three laws (Apple’s own): Clarity · Deference · Depth”
  • Clarity — content is the hero; chrome defers. Text legible at every size; icons precise; generous negative space with intent.
  • Deference — the UI gets out of the way. Glass and motion serve the content, never decorate it.
  • Depth — translucency + layering communicate hierarchy and位置. Depth is information, not ornament.

Liquid Glass — the hard rules (from research)

Section titled “Liquid Glass — the hard rules (from research)”
  1. One primary glass sheet per view. Do not stack two translucent panes. Today the dashboard risks glass-on-glass-on-glass (sidebar + topbar + panel + modal). Establish a glass hierarchy: one dominant sheet per context; everything else is solid --card or subtle tint.
  2. Frost 10–25%, never >30%. Above 30% reads as “milky plastic.” Our .glass-panel and sidebar values must be audited against this band per context (light content under glass needs less frost than busy content).
  3. 4.5:1 text contrast after blur. Test contrast on the actual blurred result, not the token in isolation.
  4. Text layers ride above the glass with a faint inner light (white ~30%, blur ~6) for lift — never baked into the blur group.
  5. Honor system settings — Reduced Transparency → solid fallback; Reduced Motion → kill springs; High Contrast → thicken borders. (We already guard reduced-motion in 49 places; extend to transparency.)
Component Apple-tier rule
Typography Satoshi as our SF Pro stand-in. Tight tracking on display (-0.02em…-0.04em), normal on body. One type scale (12·13·14·16·18·22·28·34). Weight carries hierarchy: 700 display, 600 titles, 500 labels, 400 body. Tabular figures (font-variant-numeric: tabular-nums) on every number, price, time, count, table cell.
Color Semantic tokens only in components. System Blue is the only accent. Color never the sole signal (pair with icon/text). Dark mode uses desaturated tonal variants (already done).
Spacing One 4/8pt rhythm. Section gaps in tiers (16·24·32·48). Whitespace groups related items — proximity is the layout, not borders.
Cards / surfaces 1px hairline border (--border), --radius-md/lg, ambient shadow only (--shadow-card). No drop shadows. Consistent elevation scale — a card is always the same card.
Buttons One primary CTA per screen; everything else subordinate. Canonical set: btn-primary, btn-secondary, btn-ghost, btn-icon. Press = subtle scale (0.97–0.98) + state-layer, never a layout shift. 44×44pt min target.
Inputs / forms Visible labels (never placeholder-only). Inline validation on blur. Errors below the field with cause + fix. focus:ring-4 ring-accent/20. Semantic input types for mobile keyboards. Autosave on long forms (calendar already does this).
Modals / sheets Animate from source (scale + fade). Scrim 40–60% black. One glass sheet. Escape + backdrop + button to dismiss. Confirm before discarding unsaved changes. Use the canonical GlassModal everywhere.
Tables / lists Tabular numbers, sortable headers with aria-sort, hover row state, zebra optional, sticky header, virtualize at 50+ rows. Empty state with a CTA, never a blank frame.
Charts Respect reduced-motion (readable instantly). Legends near chart, tooltips on hover/tap, accessible palette (not color-alone), skeleton while loading, locale number formatting. Dynamic colors are the one allowed inline-style exception.
Navigation Sidebar 240px (✓ matches Apple 240–280). Active state by color + weight + indicator. Bottom nav ≤5 on mobile (✓). Same nav placement on every page.
Motion Spring physics only (✓ — no cubic-bezier). Enter from below = deeper; exit up = back. Exit ~65% of enter duration. Stagger lists 30–50ms. Interruptible. One orchestrated moment beats scattered effects.
Empty / loading / error Skeletons (not spinners) for >300ms. Empty states invite action. Errors state what happened + how to recover, in the product’s voice. (Calendar already models this — make it the template.)

The “$100M” tells (what separates A− from A+)

Section titled “The “$100M” tells (what separates A− from A+)”
  • Optical alignment, not just mathematical — icons aligned to text baseline, not box.
  • Tabular numbers everywhere numbers live — the single biggest “cheap vs. expensive” tell in a data app.
  • Consistent corner radii nesting correctly (inner radius = outer − padding).
  • One motion rhythm — every transition shares duration/easing tokens.
  • Restraint — Chanel’s rule: before shipping a screen, remove one effect. The signature element is the one memorable thing; everything around it is quiet.

2. The Strategy — Consistency Comes From Primitives, Not Pages

Section titled “2. The Strategy — Consistency Comes From Primitives, Not Pages”

Converting page-by-page (the old approach) produces 20 slightly-different cards. Apple-level consistency comes from a tiny set of canonical primitives that every page imports. Build the vocabulary once; every screen speaks it.

Canonical primitive set (build/harden first, in components/ui/): Card · Button (variants) · Modal (= harden GlassModal) · Input/Select/Textarea/Field · Table · Badge/StatusPill · EmptyState (✓ exists) · Skeleton (✓ exists) · Tabs (✓) · Switch (✓) · Tooltip (✓) · PageHeader.

Once these exist and are token-pure, “converting a page” becomes “replace bespoke markup with primitives” — fast, mechanical, and automatically consistent.


3. Root-Cause Fix: The Calendar / Dashboard Scroll Architecture

Section titled “3. Root-Cause Fix: The Calendar / Dashboard Scroll Architecture”

This is the thing that’s been “kicking everyone’s ass” — and the commit log proves it (5+ consecutive fix(layout) / fix(calendar) commits fighting the same problem).

The bug (as it was): the now-retired react-big-calendar needed an explicit pixel height to render its internal time-grid scroll, and the fix at the time hard-coded h-[calc(100vh-260px)] on the panel — a magic number that broke the instant the topbar, trial banner, or footer changed height. Every layout commit re-tuned the constant instead of removing it. (react-big-calendar was deleted outright on 2026-08-05; the dashboard and the landing wall both render the hand-built CalendarGrid now.)

The Apple fix — flex column, single scroll region, zero magic numbers:

main-content → height: 100dvh; display: flex; flex-direction: column; overflow: hidden;
topbar/header → flex: 0 0 auto; (fixed height, doesn't scroll)
content-inner → flex: 1 1 0; min-height: 0; overflow-y: auto; (THE single scroll region)
page wrapper → min-height: 100%; display: flex; flex-direction: column;
calendar panel→ flex: 1 1 0; min-height: 0; (fills leftover space, no calc)
footer → flex: 0 0 auto; (always reachable, end of flow)

min-height: 0 on flex children is the missing ingredient that lets the calendar shrink-to-fit instead of overflowing. Fix this once in the layout primitives and the calendar, footer-reachability, and sidebar-collapse-resize problems all dissolve together. No more resizeKey hacks.


4. Execution Plan — Phased, Highest-Visibility First

Section titled “4. Execution Plan — Phased, Highest-Visibility First”

Ordered by what a clinic owner sees every day, not by what’s most broken. The daily driver (login → dashboard → calendar) earns the first polish.

Phase 0 — Foundation hardening ✅ COMPLETE

Section titled “Phase 0 — Foundation hardening ✅ COMPLETE”
  • Token audit, glass hierarchy documented, emerald/gold killed.
  • components/ui/ primitive set built: Card, Button, Badge/StatusPill, PageHeader, Table/THead/Th/Td/TableEmpty, Field.
  • Flex scroll architecture (no calc hacks), calendar fills without magic numbers.
  • Dashboard shell: Liquid Glass sidebar (one glass sheet), Apple nav density (~40px rows), topbar/mobile header.

Phase 2 — Core CRUD to primitives ✅ COMPLETE

Section titled “Phase 2 — Core CRUD to primitives ✅ COMPLETE”
  • Patients, Doctors, Conversations, Settings, Billing, Analytics, Staff, FAQ — all use canonical ui/ primitives.
  • TreatmentPlansPage, PaymentsPage, RecallCampaignsPage: glass-panel → bg-card, primitives adopted.

Phase 4 — Global polish + a11y ✅ COMPLETE

Section titled “Phase 4 — Global polish + a11y ✅ COMPLETE”
  • Motion: spring-only guard (MotionRhythm.test.js), stagger 40ms, no cubic-bezier in Framer Motion.
  • Spacing: 4/8pt sweep, off-grid values fixed.
  • Dark mode: full token coverage, regression guard (DarkModeTokens.test.js).
  • A11y: focus rings on Th + tab buttons, aria-sort/aria-live in primitives, GlassModal aria-label/labelledby, Escape dismiss, reduced-motion 72+ guards, prefers-reduced-transparency CSS.
  • Final gate: typecheck ✅ lint ✅ test ✅ design:guard ✅
Section titled “Phase 5 — Marketing / auth / legal Tailwind sweep ✅ COMPLETE”
  • Landing, auth, legal, shared components: static inline styles → Tailwind tokens.

  • Every page imports the same primitives; zero bespoke cards/buttons/inputs.
  • Zero non-dynamic inline styles; zero hardcoded hex outside dynamic computation; zero emerald/gold; zero Stethoscope.
  • One glass sheet per view; frost within 10–25%; 4.5:1 after blur.
  • Tabular numbers on all data. One motion rhythm. Dark mode verified per screen.
  • Calendar + footer + sidebar-collapse work without magic-number heights.
  • A clinic owner cannot point to two screens that feel like different products.

  1. Scope of first execution slice — do Phase 0+1 (daily driver) as the proof, then review before continuing? (Recommended.)
  2. --success color — keep emerald as semantic success, or shift to on-brand teal? (Apple uses green for success, so keeping it is defensible.)
  3. SuperAdmin priority — confirm it’s last (internal-only), or does it matter to investors/demos?