git-subtree-dir: aiui git-subtree-mainline:0c4826f8ccgit-subtree-split:e30ac1d106
174 lines
6.7 KiB
Plaintext
174 lines
6.7 KiB
Plaintext
---
|
||
description: Mobile UX patterns informed by Apple iOS HIG — touch targets, typography, spacing, navigation, animations
|
||
globs: "**/*.vue,**/*.css"
|
||
alwaysApply: false
|
||
---
|
||
|
||
# Mobile UX (iOS HIG-Informed)
|
||
|
||
## Philosophy
|
||
Design for mobile first, enhance for desktop. Follow Apple iOS Human Interface Guidelines for sizing, spacing, and interaction patterns. Adapt native iOS conventions to our glass morphism dark theme.
|
||
|
||
## Typography (iOS Dynamic Type Mapped to CSS)
|
||
|
||
| iOS Text Style | Default Size | CSS Equivalent | AIUI Usage |
|
||
|---|---|---|---|
|
||
| Large Title | 34pt | `text-[34px]` / `text-3xl` | Page titles (rare) |
|
||
| Title 1 | 28pt | `text-[28px]` / `text-2xl` | Section headers |
|
||
| Title 2 | 22pt | `text-[22px]` / `text-xl` | Sub-section headers |
|
||
| Title 3 | 20pt | `text-[20px]` / `text-lg` | Card titles |
|
||
| Headline | 17pt semibold | `text-[17px] font-semibold` | Emphasis labels |
|
||
| Body | 17pt | `text-[17px]` / `text-base` | Primary content |
|
||
| Callout | 16pt | `text-[16px]` | Secondary content |
|
||
| Subheadline | 15pt | `text-[15px]` | Metadata |
|
||
| Footnote | 13pt | `text-[13px]` / `text-xs` | Timestamps, captions |
|
||
| Caption 1 | 12pt | `text-[12px]` | Badges, small labels |
|
||
| Caption 2 | 11pt | `text-[11px]` | Smallest text (tab labels) |
|
||
|
||
### Key rules
|
||
- **Minimum text size**: 11px (Caption 2) — never go smaller
|
||
- **Body text on mobile**: 17px (not 14px/16px) for comfortable reading
|
||
- Use `text-sm` (14px) sparingly — only for dense UI, not primary reading content
|
||
- Chat messages should use at least 15-16px on mobile
|
||
- Metadata/timestamps: 11-13px is acceptable
|
||
|
||
## Touch Targets
|
||
|
||
| Rule | Value | Tailwind |
|
||
|---|---|---|
|
||
| Minimum tap target | **44 × 44px** | `min-w-[44px] min-h-[44px]` |
|
||
| Minimum gap between targets | **8px** | `gap-2` |
|
||
| Comfortable button height | 44-50px | `h-11` to `h-[50px]` |
|
||
| iOS nav bar button | 44px | `h-11` |
|
||
|
||
### Key rules
|
||
- The 44px minimum applies to the **tappable area**, not the visual size
|
||
- A 24px icon can have a 44px tap target via padding: `p-2.5` on a 24px icon
|
||
- Our `w-9 h-9` (36px) header buttons are below 44px — compensate with generous spacing or padding hit areas
|
||
- Text buttons must extend touch target beyond text bounds
|
||
|
||
## Spacing & Layout
|
||
|
||
| Element | iOS Value | CSS |
|
||
|---|---|---|
|
||
| Side margins (iPhone) | 16px | `px-4` |
|
||
| Nav bar height | 44px | `h-11` |
|
||
| Tab bar height | 49px (+34px safe area) | `h-[49px]` + `pb-[env(safe-area-inset-bottom)]` |
|
||
| Bottom safe area (notch) | 34px | `env(safe-area-inset-bottom)` |
|
||
| Search bar | 36px field + 8px padding | `h-9` + `py-1` |
|
||
| Standard content inset | 16px horizontal | `px-4` |
|
||
|
||
### Safe area insets
|
||
```css
|
||
/* Always use for full-screen layouts */
|
||
padding-top: env(safe-area-inset-top);
|
||
padding-bottom: env(safe-area-inset-bottom);
|
||
padding-left: env(safe-area-inset-left);
|
||
padding-right: env(safe-area-inset-right);
|
||
height: 100dvh; /* Dynamic viewport height — avoids iOS Safari toolbar */
|
||
```
|
||
|
||
## Navigation Patterns
|
||
|
||
### iOS-native patterns to follow
|
||
- **Primary navigation**: Bottom tab bar (persists across screens)
|
||
- **Secondary navigation**: Top nav bar with back button (left) and actions (right)
|
||
- **Modals**: Sheet sliding up from bottom (half-screen or full)
|
||
- **Context menus**: Long-press or action sheets from bottom
|
||
|
||
### Primary action placement
|
||
```
|
||
Top 20%: Navigation, info, secondary actions
|
||
Middle 60%: Main content (scrollable)
|
||
Bottom 20%: Primary actions (thumb zone) — send, approve, play
|
||
```
|
||
|
||
### Sheets & modals on mobile
|
||
- Use bottom sheets with three detents: small (~25%), medium (~50%), large (full)
|
||
- Always provide a close button — don't rely solely on swipe-to-dismiss
|
||
- Content panels: full-screen overlay or bottom sheet, never side-by-side
|
||
|
||
## Form Inputs
|
||
|
||
| Rule | Value | Why |
|
||
|---|---|---|
|
||
| **Minimum input font** | **16px** | Prevents iOS Safari auto-zoom on focus |
|
||
| Minimum field height | 44px | Matches tap target |
|
||
| Use `inputmode` | `numeric`, `email`, `tel`, `url`, `search` | Shows appropriate keyboard |
|
||
| Use `autocomplete` | Standard attributes | Enables autofill |
|
||
| Submit button placement | Bottom of form, thumb zone | Easy to reach |
|
||
|
||
## Animations & Motion (iOS Spring Model)
|
||
|
||
### Duration guidelines
|
||
| Type | Duration | Tailwind |
|
||
|---|---|---|
|
||
| Micro-interaction (tap, toggle) | 100-200ms | `duration-150` |
|
||
| Standard transition (push/pop) | 250-350ms | `duration-300` |
|
||
| Modal presentation (sheet) | 300-400ms | `duration-300` |
|
||
| Complex transitions | 400-500ms | `duration-500` |
|
||
|
||
### iOS-style easing
|
||
```css
|
||
/* Standard iOS-like transition (ease out / decelerate) */
|
||
transition: transform 0.35s cubic-bezier(0.2, 0.9, 0.3, 1.0);
|
||
|
||
/* Bouncy spring-like (for playful entrances) */
|
||
transition: transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||
|
||
/* Quick snap (micro-interactions) */
|
||
transition: transform 0.25s cubic-bezier(0.0, 0.0, 0.2, 1.0);
|
||
```
|
||
|
||
### Motion rules
|
||
- Entrances: ease-out (decelerate)
|
||
- Exits: ease-in (accelerate)
|
||
- Only animate `transform` and `opacity`
|
||
- **Always** respect `prefers-reduced-motion`:
|
||
```css
|
||
@media (prefers-reduced-motion: reduce) {
|
||
*, *::before, *::after {
|
||
animation-duration: 0.01ms !important;
|
||
transition-duration: 0.01ms !important;
|
||
}
|
||
}
|
||
```
|
||
|
||
## Gestures
|
||
- Swipe left/right: gallery nav, dismiss
|
||
- Swipe down: close overlay/bottom sheet, pull-to-refresh
|
||
- Long press: context menu, selection
|
||
- Pinch: zoom on images
|
||
- Minimum swipe distance: 50px before triggering
|
||
|
||
## Scroll Behavior
|
||
- Lock body scroll when modal/drawer is open
|
||
- `overscroll-behavior: contain` on modal content
|
||
- `touch-action: manipulation` to prevent zoom on double-tap
|
||
- `-webkit-overflow-scrolling: touch` for smooth iOS scroll
|
||
|
||
## Iconography
|
||
| Context | Size | Style |
|
||
|---|---|---|
|
||
| Tab bar | 25px | Filled/solid |
|
||
| Nav bar / toolbar | 22px | Outlined, 1.5px stroke |
|
||
| Inline with text | Match font size | Outlined |
|
||
| Standalone | 28-33px | Filled or outlined |
|
||
|
||
## AIUI Custom Overrides (Keep These)
|
||
These deviate from stock iOS but are intentional for our design language:
|
||
- **Dark-only theme**: No light mode. Background `#0a0a0a`, not iOS system colors
|
||
- **Glass morphism**: Translucent surfaces with backdrop-blur instead of iOS solid materials
|
||
- **Accent color**: Bitcoin orange `#F7931A` instead of iOS systemBlue
|
||
- **Text opacity scale**: Our `/25` → `/96` scale instead of iOS label hierarchy
|
||
- **No separator borders**: We use spacing and glass layering instead
|
||
- **Custom animations**: `animate-fade-up`, `animate-scale-in` per our design system
|
||
- **Header buttons**: Currently 36px (`w-9 h-9`), acceptable with generous spacing
|
||
|
||
## Performance on Mobile
|
||
- Test on real devices, not just emulators
|
||
- Test on 3G/4G connections
|
||
- Debounce scroll handlers
|
||
- Lazy load images with `loading="lazy"`
|
||
- Critical CSS inlined, rest loaded async
|