plugins/development/skills/ios-ui-guidelines/SKILL.md
Provides Apple Human Interface Guidelines adapted for React Native + Expo + React Native Paper, covering typography, spacing, colors, dark mode, shapes, animations, Liquid Glass, gestures, forms, navigation, and accessibility. Use when designing, building, or reviewing iOS screens in React Native apps, or when user asks about HIG compliance, iOS design patterns, or "how should this look on iOS". NOT for Android-specific design or web-only UI.
npx skillsauth add petrogurcak/skills ios-ui-guidelinesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Apple Human Interface Guidelines adapted for React Native + Expo + React Native Paper.
System font: San Francisco (default in RN on iOS). Sizes match Dynamic Type:
| Style | Size | Weight | Leading | RN Paper variant |
| ----------- | ---- | -------- | ------- | ------------------------ |
| Large Title | 34pt | Bold | 41pt | headlineLarge (custom) |
| Title 1 | 28pt | Regular | 34pt | headlineMedium |
| Title 2 | 22pt | Regular | 28pt | headlineSmall |
| Title 3 | 20pt | Regular | 25pt | titleLarge |
| Headline | 17pt | Semibold | 22pt | titleMedium |
| Body | 17pt | Regular | 22pt | bodyLarge |
| Callout | 16pt | Regular | 21pt | bodyMedium |
| Subheadline | 15pt | Regular | 20pt | bodyMedium |
| Footnote | 13pt | Regular | 18pt | bodySmall |
| Caption 1 | 12pt | Regular | 16pt | labelSmall |
| Caption 2 | 11pt | Regular | 13pt | — (minimum readable) |
Rules:
allowFontScaling (default true in RN)8-point grid. All spacing in multiples of 8.
| Element | Value | | ----------------------------- | -------------------------------- | | Screen horizontal padding | 16pt (compact) or 20pt (regular) | | Section spacing | 24-32pt | | Field spacing (form) | 16pt | | Inner padding (cards, inputs) | 12-16pt | | Between label and input | 8pt |
Safe Areas — always use SafeAreaView or useSafeAreaInsets:
| Area | Height | | -------------------- | ----------------- | | Status bar (notch) | ~59pt | | Home indicator | 34pt | | Nav bar (single row) | 44pt | | Tab bar | ~49pt + safe area |
Screen widths: 375pt (SE) — 440pt (17 Pro Max). Design for 375pt min.
Layout principles:
Minimum 44x44 pt. Non-negotiable.
<Pressable hitSlop={8} style={{ minHeight: 44, justifyContent: 'center' }}>
| Rule | Value |
| --------------------------- | ----------------------------------------------- |
| Min touch target | 44x44pt |
| Min spacing between targets | 8pt (12pt recommended with bezel, 24pt without) |
| Primary button height | 50pt recommended |
| Text button | Use hitSlop to ensure 44pt area |
Use semantic colors, never hardcoded hex.
| Semantic | Light | Dark | Purpose | | ------------------------- | ------------------- | --------------------- | -------------------- | | systemBackground | #FFFFFF | #000000 | Primary bg | | secondarySystemBackground | #F2F2F7 | #1C1C1E | Grouped secondary bg | | systemGroupedBackground | #F2F2F7 | #000000 | Grouped primary bg | | label | #000000 | #FFFFFF | Primary text | | secondaryLabel | rgba(60,60,67,0.6) | rgba(235,235,245,0.6) | Secondary text | | separator | rgba(60,60,67,0.29) | rgba(84,84,88,0.6) | Dividers | | tint/primary | App accent | App accent | Interactive elements |
iOS system colors (auto-adapt to appearance + Increase Contrast):
| Color | Light | Dark | Use | | ------ | ------- | ------- | --------------------- | | Blue | #007AFF | #0A84FF | Primary action, links | | Green | #34C759 | #30D158 | Success | | Red | #FF3B30 | #FF453A | Destructive, errors | | Orange | #FF9500 | #FF9F0A | Warnings | | Yellow | #FFCC00 | #FFD60A | Caution |
Contrast ratios:
Plan from the start. Not an afterthought.
Implementation in RN Paper:
import { useColorScheme } from 'react-native';
import { MD3DarkTheme, MD3LightTheme, PaperProvider } from 'react-native-paper';
import { ThemeProvider, DarkTheme, DefaultTheme } from '@react-navigation/native';
const colorScheme = useColorScheme();
const paperTheme = colorScheme === 'dark' ? MD3DarkTheme : MD3LightTheme;
const navTheme = colorScheme === 'dark' ? DarkTheme : DefaultTheme;
<PaperProvider theme={paperTheme}>
<ThemeProvider value={navTheme}>
Rules:
useTheme() for all colors. Zero hardcoded hex in components.iOS 26 design system uses three shape types:
| Type | Rule | Use |
| -------------- | -------------------------------------- | -------------------------------- |
| Fixed | Constant borderRadius | Standalone elements |
| Capsule | borderRadius: height / 2 | Buttons, pills, tags, sliders |
| Concentric | borderRadius: parentRadius - padding | Nested containers (card > image) |
// Capsule button
<Pressable style={{ borderRadius: height / 2, height: 50 }}>
// Concentric: card with image
const CARD_RADIUS = 16;
const CARD_PADDING = 12;
<View style={{ borderRadius: CARD_RADIUS, padding: CARD_PADDING }}>
<Image style={{ borderRadius: CARD_RADIUS - CARD_PADDING }} />
</View>
Rules:
Motion is meaningful, not decorative. Use to show relationships, provide feedback, communicate state.
Principles:
Reanimated spring configs for iOS feel:
import { withSpring, withTiming, ReduceMotion } from "react-native-reanimated";
// iOS-like button press feedback
const scale = withSpring(0.96, { damping: 15, stiffness: 300 });
// Smooth content transition
const translateY = withSpring(0, { damping: 20, stiffness: 200 });
// Bouncy entrance
const opacity = withSpring(1, { damping: 12, stiffness: 100 });
// Quick snap (toggle, switch)
const position = withSpring(targetX, { damping: 20, stiffness: 400 });
Spring presets:
| Preset | Damping | Stiffness | Use | | ------ | ------- | --------- | ------------------------------- | | Bouncy | 8-12 | 100-150 | Playful elements, entrance | | Snappy | 18-25 | 300-400 | Buttons, toggles, selection | | Gentle | 15 | 100-200 | Content transitions | | Stiff | 20+ | 400+ | Micro-interactions, quick snaps |
Reduce Motion — respect always:
// Option A: Per-animation (built into Reanimated)
withSpring(value, { reduceMotion: ReduceMotion.System }); // default
// Option B: Check globally
import { useReducedMotion } from "react-native-reanimated";
const reduceMotion = useReducedMotion();
// If true: replace transitions with fades, tighten springs, skip bounces
When Reduce Motion is on:
Liquid Glass is Apple's new translucent material for the navigation/control layer.
React Native: @callstack/liquid-glass (requires RN 0.80+, Xcode 26+, not in Expo Go)
import {
LiquidGlassView,
isLiquidGlassSupported,
} from "@callstack/liquid-glass";
import { BlurView } from "expo-blur";
// With fallback
function GlassBar({ children }) {
if (isLiquidGlassSupported) {
return (
<LiquidGlassView effect="regular" style={styles.bar}>
{children}
</LiquidGlassView>
);
}
return (
<BlurView intensity={20} tint="light" style={styles.bar}>
{children}
</BlurView>
);
}
Two variants:
| Variant | When | Props |
| ----------- | -------------------------------------------------- | ------------------ |
| Regular | Default. Adaptive, works everywhere. | effect="regular" |
| Clear | Over media-rich content only. Needs dimming layer. | effect="clear" |
Rules:
PlatformColor for auto-adapting text color on glassLiquidGlassContainerView to merge nearby glass elements (spacing prop)expo-blur BlurView. On Android: solid background with opacity.Accessibility: Liquid Glass auto-handles these when system settings are on:
Input height: 44-56pt (44pt min for touch target).
<TextInput
mode="outlined"
label="Email"
autoFocus={isFirstField}
returnKeyType="next"
blurOnSubmit={false}
onSubmitEditing={() => nextRef.current?.focus()}
autoComplete="email"
textContentType="emailAddress"
keyboardType="email-address"
autoCapitalize="none"
style={{ backgroundColor: colors.surface }}
/>
Rules:
mode="outlined" for iOS standard appearancereturnKeyType: "next" for non-last, "done"/"go" for lastautoComplete + textContentType for iOS autofillblurOnSubmit={false} + ref chain for multi-field formsHelperText type="error"Keyboard:
<KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<ScrollView keyboardShouldPersistTaps="handled" keyboardDismissMode="interactive">
| Pattern | When | Implementation |
| ----------------- | ----------------- | -------------------------------- |
| Tab bar | Top-level (max 5) | Expo Router (tabs) |
| Stack | Drill-down | Expo Router Stack |
| Modal/Sheet | Focused task | presentation="modal" |
| Full-screen modal | Immersive | presentation="fullScreenModal" |
iOS conventions:
headerLargeTitle: true)Standard gestures — don't override their expected behavior:
| Gesture | Expected action | | ----------------- | -------------------------------- | | Tap | Activate/select | | Swipe | Reveal actions, dismiss, scroll | | Long press | Context menu, additional options | | Drag | Move element | | Pinch | Zoom | | Edge swipe (left) | Navigate back |
Rules:
// Swipe actions on list row (react-native-gesture-handler)
import { Swipeable } from "react-native-gesture-handler";
<Swipeable renderRightActions={renderDeleteAction}>
<ListItem />
</Swipeable>;
allowFontScaling, test at AX5 (largest)accessibilityLabel on all interactive elements, logical navigation orderexpo-haptics)accessibilityRole on all interactive elementsaccessibilityState for toggles, checkboxes, disabled statesaccessibilityHint for complex inputs<Pressable
accessibilityRole="button"
accessibilityLabel="Delete item"
accessibilityHint="Removes this item from your list"
accessibilityState={{ disabled: isLoading }}
style={{ minHeight: 44 }}
>
Buttons:
// Primary: contained, full width, 50pt, capsule
<Button mode="contained" style={{ borderRadius: 25, paddingVertical: 4 }}>
// Secondary: outlined
<Button mode="outlined">
// Tertiary: text
<Button mode="text">
Components:
TextInput with label prop (floating labels = iOS standard)HelperText for field errorsChip for selection (not toggle buttons)SegmentedButtons for exclusive choices (2-5 options)Divider between sectionsMenu for overflow actionsRun before marking any screen done:
Layout:
SafeAreaView or useSafeAreaInsets wrapping contentColors & Theme:
useTheme() — zero hardcoded hexTypography:
Interaction:
Forms (if applicable):
KeyboardAvoidingView presentreturnKeyType set on all inputsautoComplete / textContentType set for autofillAnimation (if applicable):
Accessibility:
accessibilityLabel on all interactive elementsaccessibilityRole set correctlydevelopment
Builds a pre-launch social proof strategy through structured beta programs using D'Souza Brain Audit interviews. Use when launching new products/services and need compelling testimonials, planning a beta cohort, designing interview questions to harvest objection-busting social proof, improving video testimonials for landing pages, or designing case studies with metrics. Trigger phrases include "beta tester program for testimonials", "pre-launch social proof", "Brain Audit testimonial framework", "case study harvest", "reverse testimonial", "video testimonial mechanics", "social proof landing page", "sběr referencí", "beta tester program", "testimonial pro landing page", "social proof před launchem", "rozhovor s klientem", "case study sběr", "reference před spuštěním". NOT for ongoing case study production (use growth-hacking case-study approach), offer design (use offer-creation), or conversion optimization (use ux-optimization).
development
Use when planning a product launch and the product type is unclear or could be either generic (SaaS/app/physical) or info-product. Routes between marketing:launch-strategy (generic launches) and marketing:info-product-launch (courses, memberships, ebooks, cohorts, communities). Trigger phrases - "launch", "spuštění", "go-to-market", "product launch", "release strategy", "uvedení na trh", "launch plan", "spuštění produktu", "launch sequence", "launch strategy". Do NOT trigger when product type is already clear (use specific skill directly).
testing
Specialized 8-week launch cadence for info-products — online courses, cohort programs, memberships, communities, ebooks, masterminds. Combines Jeff Walker's Product Launch Formula (Seed/Internal/JV variants, PLC sequence, open-cart day-by-day) with Stu McLaren's membership mechanics (closed cart, Success Path) and Hormozi Grand Slam Offer stacking. Use when planning "launch online kurzu", "info-product launch", "PLF launch", "course launch", "membership launch", "cohort launch", "ebook launch", "open cart close cart", "8-week launch of online course", "beta cohort to launch sequence", "spuštění kurzu", "launch členské sekce", "open cart strategie". Differentiates from marketing:launch-strategy (generic SaaS/app launches) — info-product-specific. NOT for SaaS launches, physical products, or services.
development
Use when releasing an Expo/React Native mobile app to App Store and Google Play - covers eas submit, ASC "Submit for Review", Play promote Internal→Production, OTA update, and decoding common silent failures (Apple agreement expiry, missing English locale, Background Location declaration, web bundle failure on react-native-maps).