Skip to content
Back to skills

Mobile Ui

ASecurity

Build or review UI in apps/mobile: one-product design tokens, adaptive phone / flip-fold / tablet layouts, and the desktop-consistent hierarchy.

  • 15 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 29, 2026
developmentbashreactgit

Works with

  • cli

Security analysis

A100/100

Scanned September 29, 2026

npx -y skills add wisnuwiry/padu --skill mobile-ui --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Mobile Ui?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Mobile Ui
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/wisnuwiry-mobile-ui/badge)](https://www.skillsdirectory.com/skills/wisnuwiry-mobile-ui)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
---
name: mobile-ui
description: >-
  Build or review UI in apps/mobile: one-product design tokens, adaptive
  phone / flip-fold / tablet layouts, and the desktop-consistent hierarchy.
---

# Mobile UI Skill

`apps/mobile/` is the third surface of **one product**, not a separate app.
A screen must read as the same product as `apps/desktop/` and `apps/web/`, at
every window size a phone, flip phone, foldable, or tablet can produce.

Three pillars. All three are required — none is a nice-to-have:

| Pillar | Rule | Detail |
| :--- | :--- | :--- |
| **Tokens** | Colors, spacing, and radii come from `@/constants/theme`. No literal hex, no magic pixel values. | [design-tokens.md](./references/design-tokens.md) |
| **Adaptive** | Layout answers to **window width**, never to device model, hinge, or orientation. | [responsive-layout.md](./references/responsive-layout.md) |
| **Parity** | Same hierarchy and the same semantic color roles as desktop/web. | [design-tokens.md](./references/design-tokens.md#desktop--web-mapping) |

## When to use

- Any change under `apps/mobile/src/`.
- Porting a desktop/web UI change to mobile (see `protocol-parity-sync` for
  wire-level work).
- Reviewing a mobile diff before a PR.

## Step 0 — Ground yourself before writing

1. Find the desktop/web equivalent of the screen. The hierarchy must land the
   same way on mobile — read it, don't invent it.
2. Read the tokens you will use: `apps/mobile/src/constants/theme.ts`
   (generated) and [design-tokens.md](./references/design-tokens.md).
3. Decide the size classes the screen must survive:
   [responsive-layout.md](./references/responsive-layout.md) lists the required
   matrix (compact phone → cover screen → unfolded fold → tablet).

## Step 1 — Tokens, not literals

- `useTheme()` from `@/hooks/use-theme` for every color. Never
  `Colors[...]` directly in a screen, never a hex/rgba literal.
- `Spacing.*` and `Radius.*` for every gap, padding, and corner.
- Reuse an established font size; do not add an 18th size.
- `constants/theme.ts` is **generated** — edit `themes.json` and run
  `bun run theme:generate`, then `bun run theme:check`.

## Step 2 — Adaptive by width

- Read size with `useWindowDimensions()`. Never `Dimensions.get('screen')` —
  it ignores fold state, Android split-screen, and iPad Slide Over.
- Branch on **width only**. Never on `Platform.isPad`, device name, hinge
  angle, or orientation.
- Cap reading content at `MaxContentWidth` (800) and center it; never let
  prose span a tablet's full width.
- Respect safe-area insets via `useSafeAreaInsets()`; `useScreenHeaderInset()`
  for content that scrolls under the floating header.

## Step 3 — Clean hierarchy

- One primary action per screen; secondary actions behind the header cluster
  or a sheet.
- Mirror the transcript's established role treatments: user bubble on
  `raised` and right-aligned, assistant full-width with no bubble, system
  centered pill on `overlay`.
- Keep chrome floating and content beneath it (`ScreenHeader`, `GlassSurface`)
  rather than adding stacked bars.
- Every color carrying meaning is paired with an icon or text label.
- Every `Pressable` has `accessibilityRole`, an `accessibilityLabel`, and a
  ≥ 44pt hit target (`hitSlop` where the glyph is smaller).
- Honor reduce motion: gate decorative animation on
  `useReducedMotion()` from `react-native-reanimated`.
- Respect OS font scaling: never `allowFontScaling={false}`; use `minHeight`,
  not fixed `height`, around text.

## Step 4 — Verify

```bash
bun run mobile:typecheck
bun run mobile:test
bun run theme:check          # only if themes.json or tokens changed
```

Visual validation on a device/simulator happens **only when the user asks**
(see `AGENTS.md` → Development runtime). A passing typecheck is not evidence
that a layout is clean at 320pt or on a tablet.

## Native modules (Expo)

Expo is the mobile tree's alone. `expo-*` dependencies belong in
`apps/mobile/package.json` only — never add one to `apps/web/` or
`apps/desktop/`, and never import `expo-*` from outside `apps/mobile/src/`. A
native capability (camera, haptics, keychain) is platform-exclusive by design,
so it is the one case that does **not** need a desktop/web counterpart.

Adding one:

1. `bunx expo install <package>` from `apps/mobile` — never a hand-typed
   version, so the pin matches the pinned SDK.
2. Declare its permissions through the package's config plugin in
   `apps/mobile/app.json` (iOS usage strings, Android permissions). Declare
   only what the app uses: `recordAudioAndroid: false` on `expo-camera`, for
   instance, keeps `RECORD_AUDIO` out.
3. `expo prebuild` and rebuild. `ios/` and `android/` are gitignored build
   output, so a plugin edit is invisible until they are regenerated.

At runtime, handle the permission explicitly and always leave a way out:

- Gate the native view on the permission hook; never mount it before a grant.
- Offer the OS prompt while `canAskAgain`, and `Linking.openSettings()` once it
  is false — a denial the OS will not re-prompt for otherwise dead-ends.
- Re-read the grant on foreground (`AppState`) so a grant made in Settings
  takes effect when the user returns.
- Keep a non-native fallback on the same screen.

`apps/mobile/src/app/scan.tsx` is the worked example (QR scan on `expo-camera`).

## Review checklist

- [ ] No literal hex/rgba, no magic spacing or radius values.
- [ ] `useTheme()` used; both light and dark read correctly.
- [ ] No width logic beyond `useWindowDimensions()`; no device/orientation
      branching.
- [ ] Usable at 320pt wide and at 200% font scale: nothing clips, truncates
      gracefully, actions stay reachable.
- [ ] Wide window (≥ 1024): content centered and capped, not stretched.
- [ ] Safe-area insets applied; content clears the floating header.
- [ ] Hierarchy matches the desktop/web equivalent; semantic color roles match.
- [ ] Accessibility role/label on every control; ≥ 44pt targets.
- [ ] Any `expo-*` native view is permission-gated, with a settings or manual
      fallback path, and its dependency lives only in `apps/mobile`.
- [ ] `bun run mobile:typecheck` and `bun run mobile:test` pass.

## Related

- `.agents/skills/protocol-parity-sync/SKILL.md` — when the change touches the
  wire protocol or shared `@padu/client` state.
- `.agents/skills/pre-pr-review/SKILL.md` — diff audit before a PR.

Files in this skill

  • SKILL.md6.3 KB
  • references/design-tokens.md6.9 KB
  • references/responsive-layout.md8 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.