Skip to content
Back to skills

Mobile Ui

ASecurity

Use when a ticket builds or reworks UI in a React Native / Expo (or React + Capacitor) mobile app and it must feel like a real, store-credible native app — not a wrapped mobile website. Covers the native-feel bar, safe areas and edge-to-edge, gestures and back navigation, haptics, Dynamic Type and screen readers, store-readiness, and mobile performance. Invoke for "make this screen feel native", "build the mobile X screen", "this feels like a website", "would this get approved", or as the mob...

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 5, 2026
ai-agentsgoreacttestinggitapisecurityperformance

Works with

  • cli
  • api
  • mcp

Security analysis

A100/100

Scanned September 29, 2026

npx -y skills add tmj-90/gaffer --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/tmj-90-mobile-ui/badge)](https://www.skillsdirectory.com/skills/tmj-90-mobile-ui)

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

Download with Pro
SKILL.md
---
name: mobile-ui
description: Use when a ticket builds or reworks UI in a React Native / Expo (or React + Capacitor) mobile app and it must feel like a real, store-credible native app — not a wrapped mobile website. Covers the native-feel bar, safe areas and edge-to-edge, gestures and back navigation, haptics, Dynamic Type and screen readers, store-readiness, and mobile performance. Invoke for "make this screen feel native", "build the mobile X screen", "this feels like a website", "would this get approved", or as the mobile pack for any app-UI change.
stack: [react-native, native, expo]
area: mobile
---

# Build store-credible native-feel mobile UI

A mobile screen is judged against the platform: Apple's Human Interface Guidelines on
iOS, Material 3 and Android's system behaviour on Android. It must respect insets and
system gestures, scale with the user's text size, speak to VoiceOver and TalkBack, stay
smooth, and clear the store reviewers' checklist. Build to that bar, and change only what
the ticket asks: a visual rework does not alter navigation routes, state, business or
game logic. A structural idea ("this should be a sheet") goes in the evidence as a
proposal, not into the diff.

## Steps

1. **Read the set-up.** Call `search_lore`; read `BRAND.md`/tokens (the `design-system`
   skill) and a sibling screen. Confirm the stack and versions: React Native (0.82+ runs
   only the New Architecture), Expo SDK, navigation library (React Navigation / Expo
   Router), animation (Reanimated), list (FlatList/FlashList), or Capacitor with a web
   UI. Extend the existing design language; do not re-pitch it.
2. **Insets and edge-to-edge.** Android apps targeting Android 15+ draw edge-to-edge,
   and targeting Android 16 removes the opt-out. Use `react-native-safe-area-context`
   (`SafeAreaProvider` + `useSafeAreaInsets` or its `SafeAreaView`; React Native's own
   `SafeAreaView` is deprecated) and apply insets per edge — backgrounds run under the
   bars, content and touch targets do not. Capacitor/web: `viewport-fit=cover` plus
   `env(safe-area-inset-*)`. Never hardcode status-bar or home-indicator heights.
3. **Navigation and back.** Use the navigator's native stack and platform transitions;
   iOS swipe-back works on every pushed screen; Android back (gesture, button, predictive
   back) goes through the navigator or `BackHandler`, closes sheets and dialogs first,
   and never exits the app from a nested screen. Unsaved-changes guards use the
   navigator's prevent-remove API, not a raw back-key listener.
4. **Platform idioms.** Bottom sheets or native modals for contextual tasks rather than
   centred web-style dialogs; native pickers, date pickers and share sheets; tab bars with
   3–5 destinations; pull-to-refresh only on refreshable lists. Follow the platform where
   the two differ (iOS back chevron and large titles; Android top app bar and system
   back) unless the brand explicitly unifies them. Close the usual web-hybrid tells:
   fade-only screen transitions (use the platform push or a shared-element transition),
   fonts flashing in after the splash (load them before hiding it), wrong scroll bounce,
   the default launch screen. On a visual rework, commit to the brand: a real type scale,
   branded chrome and one named hero detail per screen (taste items; they do not block
   review).
5. **Touch and feedback.** Targets at least 44×44 pt (iOS) / 48×48 dp (Android) — use
   `hitSlop` when the visual is smaller; pressed states on every touchable (`Pressable`
   with a style callback or Android ripple); haptics (`expo-haptics` or the repo's wrapper)
   on meaningful moments only — selection change, success, error — through one shared
   hook.
6. **Text size and accessibility.** Text scales with Dynamic Type / Android font scale up
   to at least 200%: no fixed-height text containers, layouts that wrap or scroll, and
   `maxFontSizeMultiplier` only on chrome that truly cannot grow. Every touchable has
   `accessibilityRole` (or `role`), an `accessibilityLabel` when it has no visible text,
   and `accessibilityState` for selected/disabled/checked; group related text with
   `accessible`; announce async results with `AccessibilityInfo.announceForAccessibility`.
   Respect Reduce Motion (`AccessibilityInfo.isReduceMotionEnabled` or Reanimated's
   `useReducedMotion`) and the system colour scheme (`useColorScheme`); check contrast in
   both schemes (4.5:1 text, 3:1 icons and borders).
7. **Keyboard.** Forms stay visible above the keyboard (`KeyboardAvoidingView` or the
   repo's keyboard library), `returnKeyType` moves to the next field, correct
   `keyboardType`/`textContentType`/`autoComplete` for email, codes and passwords, and
   tapping outside dismisses the keyboard where expected.
8. **Performance.** Animations on the UI thread (Reanimated worklets or
   `useNativeDriver: true`) using transforms and opacity; long lists virtualised with
   stable `keyExtractor` and memoised rows (FlashList where the repo uses it); images
   sized to their box and cached; no heavy work in render or on the JS thread during
   gestures. Frame-rate claims need a release build on a mid-range Android device or
   emulator — dev builds are not representative; without one, list it as unverified in
   the evidence rather than claiming smoothness.
9. **Store readiness** (flag, do not guess, anything that needs a human decision):
   purpose strings for every permission requested (iOS `NS…UsageDescription`, requested
   in context, not on launch); iOS privacy manifest and Play Data safety entries kept in
   step with new data collection; a real launch screen and app icon; no placeholder text
   or broken links; in-app account deletion when the app creates accounts (App Review
   5.1.1(v)); enough native functionality that the app is not a repackaged website
   (App Review 4.2).
10. **Verify and evidence.** Run type-check, lint and tests (React Native Testing Library:
    query by role and label). For each AC, a test or an E2E flow (Maestro or Detox if the
    repo has one, run only if a simulator or emulator is already available) that
    exercises its behaviour. Where one is, screenshot small and large phone, largest text
    size, dark mode and Android gesture back (into the repo's git-ignored test output;
    never committed unless an AC asks); you cannot drive VoiceOver/TalkBack, so
    assert roles, labels and states in tests and name any unchecked item in the evidence.
    A failing existing logic test means you changed behaviour — undo
    that change; never edit the test to pass. Record per-AC evidence with the
    `record-evidence` skill, then stop.

## Review checklist

A reviewer blocks on an item only when it breaks an AC, leaves an AC untested, or is a
correctness, security or WCAG A/AA defect; taste and polish items are `(optional)` notes.

- Nothing under the notch, status bar or home indicator; no hardcoded bar heights.
- Swipe-back and Android back behave natively; sheets close before screens pop.
- Targets ≥ 44 pt / 48 dp; pressed states present; haptics sparing and consistent.
- Text scales to 200% without clipping; every control has role, label and state.
- Reduce Motion and dark mode respected; contrast holds in both schemes.
- Tokens only; no raw hex or magic numbers in components.
- UI-thread animations; virtualised lists; no jank on a release build.
- Routes, state and logic unchanged unless the ticket asked; structural ideas raised as
  proposals.
- Permission strings, privacy disclosures and account deletion accounted for.

## Anti-patterns

- A web layout in a WebView with hover states, tiny links and centred modals.
- `Dimensions`-based status-bar maths; `SafeAreaView` wrapping the whole app blindly.
- `allowFontScaling={false}` app-wide; icon-only buttons with no label.
- Haptics on every tap; permission prompts on first launch with no context.

## Capture lore

This skill is one of the places durable, reusable knowledge naturally surfaces:
**A mobile convention this repo enforces — the design system/tokens, brand voice, a native-feel pattern, a store-submission requirement, or a "never touch" gameplay/logic boundary.** That kind of fact is *lore*. Capture it via the **lore-capture
protocol in your brief** (`CLAUDE.factory.md`, step 11 "Memory contribution"):
call the Memory MCP `suggest_lore` once at the close of your work — reusable
conventions, gotchas, decisions, and boundaries only, never per-ticket trivia.

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…