Skip to content

0031 — Code-First Design Workflow and Cross-Platform Delivery ​

Status: Accepted (2026-06-22) — amends ADR 0027; amended by owner correction 2026-06-22 — see §"Amendment: Responsive Web Layout Model" below

Date: 2026-06-22

ADO work item: AB#3237 (Story — Design system / @hch/ui built and adopted); Epic AB#3074

Deciders: Kristopher Turner (platform owner)


Context ​

ADR 0027 established Claude Design as the sole sanctioned UI tool and @hch/ui as the only import path for design tokens and components. It described a workflow where the Claude Design canvas is used to design screens, and "Handoff to Claude Code" deposits generated React code into packages/ui/src/. That framing implied Claude Design was the originating source of UI work — the canvas produces the design, the Handoff produces the code.

Practice revealed that framing is backwards for a code-first project:

  1. The Claude Design canvas and Handoff produce scaffold, not production code. The real components in packages/ui/src/ — typed, tested, accessible, integrated with the platform — are authored in code, not generated by a canvas export. Handoff output is a starting point that requires substantial refinement before it ships.
  2. Web (React/Vite) is the verified, first-class delivery path. The React component tree, the @hch/ui token set, and the PWA install path on iOS and Android home screens are operational. Native SwiftUI (iOS) and React Native (React Native) cross-platform parity through the Claude Design pipeline has not been verified — the fidelity of the canvas → Handoff → native-code round-trip for those surfaces is unknown.
  3. The Claude Design project has value as a mockup and communication tool, not as the primary design instrument. @dsCard HTML cards mirrored into the project give the owner a shareable, browser-viewable representation of the design system and screen layouts. This is complementary to the code, not upstream of it.

This ADR refines the source-of-truth chain and cross-platform delivery posture established by ADR 0027.

Decision ​

Real code is the source of truth for the Heritage Virginia Community Hub UI. Components and design tokens are built in packages/ui/src/ first, authored to the canonical spec in docs/internal/design/claude-design.md §4. The Claude Design project (ceb2fb20-8346-4a74-8683-a96e4663b525) receives HTML mockup cards mirrored from the code via /design-sync, giving the owner shareable visual representations — but it is not upstream of the shipped components. Web (React/Vite + PWA, apps/web) is the primary, verified cross-platform delivery path. Native iOS SwiftUI and React Native fidelity through the Claude Design pipeline are not verified; no delivery commitment depends on them.

Amended source-of-truth chain ​

The corrected direction — replacing the canvas-first loop described in ADR 0027:

text
docs/internal/design/claude-design.md §4   ← canonical visual spec (colours, type, components)
        │  author components to match §4

packages/ui/src/*                           ← REAL runtime components (source of truth)
        │  extract @dsCard HTML cards from real components

packages/ui/design-system/*.html            ── /design-sync ──►  Claude Design project
(HTML @dsCard mockups — mirrors, not originals)                   (shareable mockups, owner view)


apps/web/**   apps/mobile/**                ← import ONLY from @hch/ui; no inline styles

Key rules that follow from this chain:

  • packages/ui/src/ is where component work happens first. Code review gates quality here.
  • packages/ui/design-system/ holds mirror cards — HTML representations of what the real components look like. They are regenerated from real components (not hand-written) once a component is stable. The /design-sync skill pushes them into the Claude Design project.
  • The Claude Design canvas is for the owner to view, share, and iterate on screen layouts. The Handoff is an optional shortcut for new scaffold — always reviewed and refined before merging.
  • claude-design.md §4 is the visual spec. When a token changes, update §4 first, then packages/ui/src/tokens.ts, then regenerate the design-system/*.html card, then re-sync.

Cross-platform delivery: web/PWA primary ​

SurfaceStatusNotes
Web (React/Vite)VerifiedProduction-deployed to heritageva.app via Azure Static Web Apps
Web PWA (installable)VerifiedInstalls to iOS and Android home screens from heritageva.app; uses the web React codebase; no App Store required
React Native (iOS + Android)Code exists (apps/mobile)Not yet verified end-to-end through the Claude Design pipeline; fidelity of @hch/ui token consumption in RN not confirmed
Native iOS (SwiftUI)Not in scopeHeritage Virginia uses React Native + Expo (ADR 0002); SwiftUI is not a delivery surface

PWA as the cross-platform path. A PWA installed from heritageva.app runs the same React codebase on every device. This gives iOS and Android users a home-screen app with offline capability and push notification support (where the platform and OS allow), without a separate native release pipeline. This is the verified cross-platform path today.

React Native is not deprecated. apps/mobile (React Native + Expo) is the eventual native app surface per ADR 0002 and ADR 0009/0014. However, no delivery commitment in the current build phase (Epic AB#3074) depends on the React Native surface being pixel-perfect with the Claude Design pipeline output. That verification is deferred to the iOS delivery phase (AB#3077).

Design workflow in practice ​

StepWhoAction
1Developer / Claude CodeAuthor or update a component in packages/ui/src/. Match claude-design.md §4.
2Developer / Claude CodeExtract the @dsCard HTML card for the component into packages/ui/design-system/.
3Claude CodeRun /design-sync to push the card to the Claude Design project. Call register_assets after each batch; reload the project in the browser.
4Owner (browser)View / iterate layouts in claude.ai/design against the mirrored cards.
5Owner (browser)Optionally: "Handoff to Claude Code" for new scaffold.
6Claude CodeIf Handoff is used: accept, review, and refine — do not merge scaffold directly. Real code goes into packages/ui/src/; Handoff is a starting point.

The @hch/ui import rule (unchanged from ADR 0027) ​

Every page and component in apps/web and apps/mobile must:

ts
import { Button, Card, Badge, colors, fonts } from '@hch/ui';

No inline style props for values covered by @hch/ui. No palette constants duplicated outside packages/ui/src/tokens.ts. This rule is unchanged from ADR 0027.

Alternatives considered ​

OptionProsConsWhy not chosen
Code-first, mirror-to-Claude Design (chosen)Code is the authority; mockups reflect real components; no fidelity gapRequires card extraction step after each component change— chosen
Canvas-first (ADR 0027 original framing)Visual-first design; owner drives from the canvasHandoff output needs significant refinement; Handoff fidelity for native surfaces unverified; creates two competing sources of truthAmended by this ADR — framing was backwards for a code-first project
No design tool (code only)Eliminates the sync stepOwner has no shareable visual representation; harder to validate layout intent before code reviewRejected — the Claude Design mirror remains valuable as a communication and validation tool
Native React Native as primary cross-platformSingle codebase for web + nativeNative pipeline unverified through Claude Design; separate release cadence (App Store / Play Store); more complex CIDeferred to the iOS/Android phases (ADR 0009, 0014); PWA covers the interim cross-platform need

Consequences ​

Positive ​

  • The code in packages/ui/src/ is authoritative — no risk that a Handoff overwrites a manually-refined component.
  • The Claude Design project always reflects what actually ships (via the mirror cards), not a designer's intention that may have drifted from the real component.
  • Web + PWA provides real, working cross-platform delivery today, ahead of native app release.
  • The owner has a shareable browser view of the design system without waiting for native app builds.

Negative / trade-offs ​

  • The @dsCard card extraction step (after each component change) is an additional authoring discipline. If skipped, the Claude Design mirror drifts from the real components.
  • The PWA-as-cross-platform path covers the current phase; native app distribution (App Store / Play Store) is still required for the iOS and Android delivery phases.

Risks ​

  • Mirror drift — real components evolve but design-system/*.html cards are not regenerated. Mitigation: treat card regeneration as part of the definition of done for any tokens.ts or component change. Enforce in PR review.
  • React Native fidelity gap — when the native app delivery phase arrives, @hch/ui may not render identically in React Native as in React DOM. Mitigation: verify component-by-component during the iOS phase (AB#3077); scope any gaps as tasks under that Epic.

Amendment: Responsive Web Layout Model (owner correction, 2026-06-22) ​

The original framing of this ADR — and the packages/ui/design-system/screens/*.html mockups it references — described the web app as rendered inside a fixed 390px × 844px phone-frame viewport. That model is wrong. The platform owner has clarified:

The platform is ONE responsive React web application — a single codebase that adapts across desktop, tablet, and phone via fluid layouts and CSS breakpoints. It is not a fixed phone frame.

Corrected layout model ​

BreakpointViewportPrimary navigationContent
Phone<640pxBottom tab bar (5 tabs: Home / Watch & Listen / Calendar / Messages / Me)Single column, full width
Tablet640–1023pxBottom tab barSingle column, max 640px centered
Desktop≥1024pxLeft sidebar (220px, same 5 destinations)Fluid column, max ~860px

The five primary destinations (Home, Watch & Listen, Calendar, Messages, Me) are unchanged. The navigation pattern changes with the viewport: bottom tab bar on phone/tablet, left sidebar on desktop. The tab-bar.html component in packages/ui/design-system/ remains authoritative for the five destinations and the bottom-tab visual style; that style becomes the phone-breakpoint nav.

What this changes ​

AspectBefore (wrong)After (corrected)
<meta viewport> in screen HTMLwidth=390, initial-scale=1 (fixed phone)width=device-width, initial-scale=1 (fluid)
body width in screen CSSwidth: 390px; min-height: 844pxmin-height: 100vh (no fixed width)
Tab bar CSSwidth: 390px; left: 0left: 0; right: 0 (spans full viewport)
Desktop layoutPhone frame centered on large screenLeft sidebar 220px + fluid content area
AppLayout.tsx shellFixed-width column, bottom tab always visibleResponsive: sidebar at ≥1024px, tab bar at &lt;1024px

The packages/ui/design-system/screens/*.html files have been updated to match this model. The claude-design.md §4 "Layout & depth" section has been amended accordingly.

Responsive layout values — resolved by ADR 0033 &ZeroWidthSpace;

The three choices flagged as "proposals, owner sign-off required" are now locked decisions, ratified by ADR 0033 — Platform Foundation UI & View Inventory (2026-06-22). The values already implemented in AppLayout.tsx and the existing packages/ui/design-system/screens/*.html mockups are confirmed:

  1. Desktop breakpoint: 1024px — locked.
  2. Desktop nav pattern: left sidebar, 220px fixed — locked.
  3. Max content width on desktop: 860px — locked.

These choices affect only the desktop breakpoint. The phone/tablet layout, five primary destinations, and tab-bar.html source of truth are unchanged.

References &ZeroWidthSpace;

Heritage Community Hub — Internal. Access restricted via Cloudflare Access + Entra ID.