Skip to content

feat(mobile): move the screens onto the design tokens and component kit - #1562

Merged
janicduplessis merged 1 commit into
feat/1547-mobile-component-kitfrom
feat/1548-mobile-screens-tokens
Sep 26, 2026
Merged

janicduplessis merged 1 commit into
feat/1547-mobile-component-kitfrom
feat/1548-mobile-screens-tokens

Conversation

@janicduplessis

@janicduplessis janicduplessis commented Sep 26, 2026 •

Copy link
Copy Markdown
Collaborator

Description

Stacked on #1561 (component kit), which is stacked on #1560 (tokens and Unistyles); this diff is against #1561.

The screens in src/screens and src/app still used React Native StyleSheet, literal spacing, font sizes and colors, useColors() from src/theme.ts, and local copies of a button, row and section. Nothing uses theme.ts or Chip after this change, so both are deleted.

Solution

  • Stylesheets and text. Every screen uses Unistyles stylesheets, Text variants and tones, and the nearest token for each literal. The device viewer's chrome over dark video uses the fixed media colors. No hex or rgba color remains outside tokens.ts.
  • Local copies replaced by the kit:
    • Pair's local Button and text "✕" close button become Button and IconButton.
    • Rename's save button and Filters' Reset become Buttons.
    • The Machines list's Rename and Forget become plain and destructive Buttons.
    • The menu's gear becomes an IconButton.
    • Build details and Filters use SectionHeader, and build details' bordered lists are ListSections.
    • Machine status's Section/Line become ListSection/ListRow, and its read-only notice an inline Banner.
    • Workspace detail's chips become Pills.
  • Colors passed as props come from useUnistyles(). This covers @expo/ui (Host seed colors, SwiftUI and Compose rows in Settings), Stack options and toolbar tints, TextInput placeholders, Icon tints, and the Gesture Handler scroll views' backgrounds. Unistyles does not re-style these on a theme change, and the hook re-renders the component when the theme changes.
  • Root layout. _layout takes the scheme for StatusBar and the navigation theme from Unistyles' runtime.
  • Appearance type. It moves to hooks/settings.tsx.
  • README. apps/mobile/README.md gains a "Design system" section on the tokens, the Unistyles rules for props, the kit and the gallery.

Screenshots:

Each image shows, left to right: before light, after light, before dark, after dark. The before images come from origin/main, and the after images from the top of the stack, so they include the follow-ups #1563, #1564 and #1565.

iOS (iPhone 18 Pro simulator, iOS 27)

Home

Home on iOS

Menu

Menu on iOS

Device grid

Device grid on iOS

Machines

Machines on iOS

Filters

Filters on iOS

Machine sheet

Machine sheet on iOS

Workspace

Workspace on iOS

Build sheet

Build sheet on iOS

Logs

Logs on iOS

Device viewer

Device viewer on iOS

Settings

Settings on iOS

Pair

Pair on iOS

About

About on iOS

Android (emulator)

Home

Home on Android

Machine sheet

Machine sheet on Android

Workspace

Workspace on Android

Visible changes: literals snap to the nearest token, so most paddings and gaps move by 1 to 2 pt. The notable ones:

  • Machine status sections become list cards, and its read-only notice becomes a tinted Banner.
  • Sheet and screen titles go from 20 to 22 pt.
  • Pair's close button is the xmark icon.
  • Plain and destructive text buttons are semibold.
Every visible change
  • Machine status. Sections are ListSection cards: radius 14 -> 12, and rows padded 12x6 and centred, where they were in a 14-padded card with 10-pt gaps and baseline-aligned. Disk volumes are a row plus a padded bar. The read-only notice becomes an inline Banner, a raised tinted card with Allow control. Top padding 28 -> 24, gaps 14 -> 12, group titles 15 -> 16 pt, "Copy" 13 -> 14 pt, "Show N more" goes regular -> semibold, and issue boxes change padding 10 -> 8 and radius 8 -> 10.
  • Workspace detail. The stopped-Metro pill turns neutral (it was tertiary-tinted). The error pill gets the card press feedback. Content gap 14 -> 12. Warning boxes change padding 10 -> 8 and radius 8 -> 10.
  • Build details. Top padding 28 -> 24. Title 20 -> 22 pt. Section titles go from 12 pt secondary to 13 pt tertiary with 0.4 tracking, the shared SectionHeader. Change-row gap 10 -> 8.
  • Logs.
    • Filter gap 10 -> 8, and row body padding 10 -> 8.
    • Code frame, Copy and Share radius 8 -> 10, and Copy/Share padding 14 -> 12.
    • Jump to latest vertical padding 10 -> 8.
    • Log text line height 17 -> 16.
  • Device viewer. Banner padding 10 -> 8. Driver badge radius 8 -> 7. The title, subtitle and tool labels gain their variant line heights.
  • Pair. The close button is the xmark icon instead of the "✕" text glyph. Gap 18 -> 16. Fields 15 -> 16 pt with padding 11 -> 12. The link 15 -> 16 pt. Button padding 13 -> 12.
  • Home. Section headers get top padding 22 -> 20 and go 15 -> 16 pt. Machine chip gap 10 -> 8.
  • Menu. Row padding 11 -> 12 and recent-row padding 9 -> 8. The gear dims when pressed instead of showing the row underlay.
  • Filters and About. Titles 20 -> 22 pt (Filters) and top padding 28 -> 32. The About card radius 14 -> 12.
  • Plain and destructive buttons. Rename, Forget and Reset go from medium to semibold.

Test plan

  • Checks: pnpm run format:check, pnpm run lint, pnpm run typecheck and pnpm test in apps/mobile pass. grep finds no @/theme or components/chip import and no hex or rgba color outside src/design.
  • Builds: rebuilt with stim ios and stim android on the stack after feat(mobile): keep machine connections in a Zustand store and cache the last status per machine #1551 (MMKV), and the Android build configured Unistyles with AGP 9.2.1.
  • Screen walk: with agent-device, walked home, drawer, device grid, machines, filters, machine sheet, workspace, build sheet, logs, viewer, Settings, pair and About. I did this in light and dark on the iOS simulator and the Android emulator, and on a baseline worktree at origin/main for the before images.
  • Live switch: Appearance switched live under System, Light and Dark.

Fixes #1548

@janicduplessis janicduplessis left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fresh review of the diff against feat/1547-mobile-component-kit (head 1a49ec6). I found no blocking bugs.

What I checked

  • Handlers and accessibility. Every onPress survives the move to Button, IconButton, Pill and ListRow. The accessibility labels that used to sit on the wrapping Touch now go through the new Button.accessibilityLabel prop. This covers Rename/Forget, "Copy command", Close and Settings. The Control switch role and state, the device-view onLayout measurement, and the pointerEvents/touch-handler wiring on the overlay are unchanged.
  • Conditional rendering. Unchanged in every screen. The Line to ListRow swap keeps numberOfLines={2} on the title and the warning tone on the value.
  • Colors passed as props. Everything that is not a React Native component gets its colors from useUnistyles().theme: expo-image tintColor, Stack.Toolbar tint and badge, @expo/ui Host seedColor, the settings(.ios) modifiers, Icon, ActivityIndicator, and TextInput placeholderTextColor in logs, pair and device-view. Touch is withUnistyles(Touchable), so its stylesheet styles re-style too.
  • Gesture Handler lists on iOS. The ScrollView, FlatList and SectionList from @/components/lists, and pair's Gesture Handler ScrollView, receive only spacing from the stylesheet. Their backgrounds come from inline theme.colors.background read from the hook.
  • Removed files. No @/theme or @/components/chip import remains. Appearance moved to hooks/settings. Typecheck, lint and tests pass in CI.
  • Media colors. They map to the old literals: #FFFFFF66 becomes withAlpha(media.text, 0.4), #FFFFFF99 becomes media.textTertiary, and #FFFFFF1F becomes media.fill. The 1A tints become withAlpha(..., opacity.subtle).
  • README. It matches the code: index.ts imports src/design/unistyles.ts first, the /gallery route is __DEV__-only under the stim scheme, and the gallery has a light/dark switch.
  • Non-ASCII. The diff adds no non-ASCII characters.

Findings (all low)

  1. Nested themed Text may not re-style on an Appearance switch. See workspace-detail.tsx:194 (the branch name nested inside the location Text) and home.tsx:262 ("Show all" nested inside the footer Text). Unistyles has an open issue about this: nested Text is not updated on a theme change (jpudysz/react-native-unistyles#1045, open, still present on 3.3.0). Here both parents call useUnistyles(), so the screen re-renders and the nested element should get the new style from React. That probably hides the problem. It is still worth checking one of these on a device with a light/dark toggle. A bad case would be the branch name in #121212 on the dark card.
  2. Smaller touch targets. Button with plain/destructive uses hitSlop={6}, and the text actions it replaces used hitSlop={8}: filters.tsx:38 Reset, mac-list.tsx:51 Rename/Forget, and mac-status.tsx:232 Copy plus "Show N more". This is a small loss of tap area on small text actions. It is fine if intended.
  3. Leftover non-ASCII (nit, older than this PR). about.tsx:20 and about.tsx:31 still contain a literal ·. This PR rewrites that file and converts the same character to · in device-view.tsx. CLAUDE.md asks for ASCII-only src/, so these two lines could be converted too.

@janicduplessis
janicduplessis force-pushed the feat/1548-mobile-screens-tokens branch from 1a49ec6 to ff60d0b Compare September 26, 2026 18:42
@janicduplessis
janicduplessis marked this pull request as ready for review September 26, 2026 18:45
@janicduplessis
janicduplessis merged commit cfb3d7c into main Sep 26, 2026
1 of 2 checks passed
@janicduplessis
janicduplessis deleted the feat/1548-mobile-screens-tokens branch September 26, 2026 18:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mobile: move the screens onto the design tokens and component kit

1 participant