Patterns

How screens are put together in each kit, and what to avoid. Generated from the kits' DESIGN_SYSTEM.md.

These are the same rules the agent reads, straight from each kit's DESIGN_SYSTEM.md.

Mobile

Patterns

  • Screen skeleton: ScrollView className="bg-background flex-1" contentContainerClassName="gap-5 p-5 pb-32" → sections (SectionHeader + Card of ListRows) → StickyBottomBar after the ScrollView when there is a primary action.
  • Multi-step flow: one route, useState<Step> inside, one header, one Stepper, one StickyBottomBar. Not one route per step.
  • Tracking: what already happened and what comes next (an order, a delivery, an application) is a Timeline in a Card. Stepper is for a flow the person is filling in.
  • Asking for a rating: after the thing is done (an order delivered), as an input Rating in its own Card with a toast to thank them. Never in the middle of a task. Show a score as a display Rating with the number.
  • Pickers: OptionSheet on phones; Select when the list is short and the field sits in a form.
  • Confirmations: AlertDialog for reversible, SwipeToConfirm for money or deletion.
  • Success: SuccessScreen + StickyBottomBar with "Done". Always.
  • Loading: Skeleton for content shapes, Spinner inside buttons (tone="primary-foreground" on a filled one), Progress when determinate.
  • Motion: anything that slides, springs or moves calls useMotion() / useReducedMotion() from lib/reduced-motion.ts and collapses to 0ms when the OS asks. components/ui gets this from reanimated's ReduceMotion.System; the kit's Animated code has no equivalent, so it is not optional — a new animated component without it is a bug.
  • Shadow: only on something you can press, or something floating above the page (dialogs, sheets, menus, toasts, the FloatingButton). Content is flat — that includes inputs, which are neither.
  • Permission: PermissionPrimer before the OS dialog, always with a skip that runs the simulated path. Never show a raw denied state. Keep its copy in step with the strings in app.json.
  • Imagery: the kit ships no raster art. Placeholder (seeded, themed) fills image slots; Spot is the illustration for empty, success and permission screens.

Anti-patterns

  • ✗ Cards inside cards. ✗ Section titles inside the card. ✗ Everything centred.
  • ✗ Radio and checkbox choices in one group — split them; one picks one, the other picks any.
  • ✗ Identical card grids (same icon + heading + body repeated).
  • ✗ Same padding on every element; vary it to create rhythm.
  • ✗ Modals as the default home for secondary content.
  • ✗ Brand colour on selected chips/tabs. ✗ Grey-on-grey text below 4.5:1 contrast.
  • ✗ Tap targets under 44×44. ✗ Icon-only buttons without accessibilityLabel.
  • ✗ A control without a name: a Switch, RadioGroupItem or Checkbox needs a Label htmlFor its id, or an aria-label (a Switch at the end of a ListRow). ✗ accessibilityState/accessibilityValue in new components: the web export drops them; use the aria-* props (aria-checked, aria-selected, aria-valuenow), which native reads too. A selected button (chip, segment, day) keeps accessibilityState={{ selected }} for native and adds aria-pressed for web.

Web

Patterns

  • App page: AppShell in the prototype's layout.tsx → PageHeader → 2–4 StatTiles → Cards or a DataTable. One primary action per page, in the PageHeader.
  • Marketing page: Hero → FeatureGrid → social proof or a product shot → PricingCards → FAQ (Accordion) → a closing call to action. One promise, one primary action, repeated at the end.
  • AI conversation: ChatMessages in a centred column (max-w-3xl), ChatComposer pinned at the bottom. Show work as it happens (ThinkingIndicator, ToolCallCard), stream the answer (StreamingText), and ask with an ApprovalCard before anything irreversible or costly (consent before consequence). Past conversations go in a ChatThreadList: a sidebar on desktop, a Sheet on small screens. When the assistant can use files, give ChatComposer an accept; sent files show as AttachmentChips in the user's turn. An answer built on the person's data or documents cites them (CitedText + SourceList); the latest answer can end with FollowUpSuggestions. A cancelled tool call is stopped, not error.
  • Forms: a Label for every field, DatePicker for a single date, errors next to the field (aria-invalid + a text-destructive line), the submit button names the action.
  • Confirmations: AlertDialog for destructive actions, its button says what happens. toast() for done-and-undo.
  • Empty and loading: every list has an EmptyState with the next step; Skeleton for content shapes.
  • Shadow: only on what you can press or what floats (dialogs, popovers, menus, toasts). Content is flat.
  • Responsive: design at 375px and 1280px. Sidebars collapse into a sheet (AppShell does), tables scroll inside their card, grids drop columns.

Worked examples of the first three: /dashboard (an AppShell app with a chart, a filtered table, a dialog form, settings and a guarded delete), /landing and /assistant.

Anti-patterns

  • ✗ Cards inside cards. ✗ Identical icon + heading + body cards repeated as filler.
  • ✗ Brand colour on selected tabs, chips or nav items. ✗ Grey-on-grey text below 4.5:1.
  • ✗ More than one primary button in view. ✗ “OK”/“Submit” as the label of a consequential action.
  • ✗ Modals as the default home for secondary content. ✗ Icon-only buttons without aria-label.
  • ✗ An assistant that acts without asking, or hides what it did.

The hallucination guard

Before writing JSX, list the components you intend to use. Every name must appear in the registry above exactly. If you need something else: stop, describe the gap in one line, and use the add-component skill. Do not import from react-native-paper, @expo/vector-icons, gluestack or any UI library that is not in package.json.

On this page