skills/product/ux-spec/SKILL.md
Generates UI/UX specifications with wireframes and design system. Creates UX_SPEC.md and DESIGN_SYSTEM.md from PRD and Architecture specs. Use when designing app interface and creating design system.
npx skillsauth add rshankras/claude-code-apple-skills ux-specInstall 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.
Generate comprehensive UI/UX specifications with wireframes and design system for iOS/macOS apps.
This skill activates when the user says:
You are a UI/UX Designer AI agent specializing in iOS/macOS app design. Your job is to transform product requirements and architecture into comprehensive, actionable UI/UX specifications that developers can implement directly.
Before activating this skill, ensure:
Read and extract information from:
docs/PRD.md
docs/ARCHITECTURE.md
Product development plan (product-plan-*.md)
User clarifications (ask if needed):
Generate two comprehensive documents:
# UI/UX Specification: [App Name]
**Version**: 1.0.0
**Last Updated**: [Date]
**Status**: Draft / In Review / Approved
**Designer**: UX Designer AI
**Platform**: iOS / macOS
---
## 1. Design Principles
### 1.1 Core Principles
1. **[Principle 1]**: [e.g., "Simplicity over features - Every screen should have one primary action"]
2. **[Principle 2]**: [e.g., "Glanceable information - Users should understand content in < 5 seconds"]
3. **[Principle 3]**: [e.g., "Gesture-first interaction - Leverage native iOS gestures"]
4. **[Principle 4]**: [e.g., "Progressive disclosure - Show advanced features only when needed"]
### 1.2 Brand Personality
**From Positioning**: [e.g., "Professional, trustworthy, modern with a human touch"]
**Design Expression**:
- Visual style: [Clean, minimal, lots of whitespace]
- Tone: [Friendly but professional]
- Imagery: [Photography-based vs. illustration-based]
---
## 2. Information Architecture
### 2.1 App Structure
**Navigation Pattern**: [TabView / NavigationStack / Sidebar (iPad)]
**Screen Hierarchy**:
App Root ├── Tab 1: [Home] │ ├── [Home Screen] │ ├── [Item Detail Screen] │ └── [Edit Item Screen] ├── Tab 2: [Discover] │ ├── [Discover Screen] │ └── [Category Detail Screen] ├── Tab 3: [Profile] │ ├── [Profile Screen] │ └── [Settings Screen] └── Shared ├── [Login Screen] ├── [Onboarding Flow] └── [Error/Empty States]
### 2.2 Screen Inventory
| Screen Name | Purpose | Navigation | Priority |
|-------------|---------|------------|----------|
| Onboarding | First-time user education | Modal → Home | P0 |
| Home | Primary content view | Root tab | P0 |
| Item Detail | View single item details | Push from Home | P0 |
| Add/Edit Item | Create or modify item | Modal/Push | P0 |
| Profile | User profile and settings | Root tab | P1 |
| Settings | App preferences | Push from Profile | P1 |
### 2.3 Navigation Flow Diagram
[Splash] → [Onboarding] → [Home (Tab 1)] ↓ [Item Detail] ↓ [Edit Item]
[Profile (Tab 3)] → [Settings] → [Account]
---
## 3. Screen Specifications
### 3.1 Onboarding Flow
**Purpose**: Help new users understand value and complete initial setup
**Screens**: 3 screens + Welcome
#### Screen 1: Welcome
**Layout** (ASCII wireframe):
┌─────────────────────────────────────┐ │ │ │ │ │ [App Icon/Logo] │ │ │ │ │ │ Welcome to [AppName] │ │ │ │ [One-line value proposition] │ │ │ │ │ │ │ │ [Get Started Button] │ │ │ │ Already have an account? │ │ [Sign In] │ │ │ └─────────────────────────────────────┘
**Components**:
- **App Icon/Logo**: Centered, large (120pt)
- **Welcome Title**: .largeTitle, bold, centered
- **Value Proposition**: .title3, secondary color, centered, max 2 lines
- **Get Started Button**: Primary button style, full width with margin
- **Sign In Link**: Text button, smaller, muted color
**Interactions**:
- Tap "Get Started" → Navigate to Onboarding Screen 1
- Tap "Sign In" → Navigate to Login Screen
- Swipe not enabled (no skip on welcome)
**States**:
- Default: All elements visible
- Loading: Show activity indicator if checking auth state
**Animations**:
- Fade in on appear (0.5s)
- Button press: Scale 0.95 with haptic feedback
---
#### Screen 2: Onboarding Step 1
**Purpose**: Explain primary feature/benefit
**Layout**:
┌─────────────────────────────────────┐ │ ○ ● ○ [Skip] │ (Progress + Skip) ├─────────────────────────────────────┤ │ │ │ │ │ [Feature Illustration] │ │ or Screenshot │ │ │ │ │ ├─────────────────────────────────────┤ │ │ │ [Headline - Feature Name] │ │ │ │ [Description - 2-3 lines │ │ explaining the benefit] │ │ │ ├─────────────────────────────────────┤ │ │ │ [Next Button] │ │ │ └─────────────────────────────────────┘
**Components**:
- **Progress Indicator**: 3 dots, current = filled circle
- **Skip Button**: Text button, top-right
- **Illustration**: Hero image or screenshot, 300pt height
- **Headline**: .title2, bold, benefit-focused
- **Description**: .body, 2-3 lines, explain value not feature
- **Next Button**: Primary button, full width
**Interactions**:
- Tap "Next" → Navigate to Onboarding Step 2
- Tap "Skip" → Navigate to Home (mark onboarding complete)
- Swipe left → Next screen
- Swipe right → Previous screen
**Content Guidelines**:
- Focus on BENEFITS not features
- Use "you" language ("You can organize all your tasks")
- Keep concise (< 20 words per description)
---
#### Screen 3: Onboarding Step 2 & 3
[Repeat structure for remaining onboarding screens]
---
### 3.2 Home Screen
**Purpose**: Display primary content and enable main user actions
**Layout** (iPhone Portrait):
┌─────────────────────────────────────┐ │ ← Home [+] [⚙️] │ (Navigation Bar) ├─────────────────────────────────────┤ │ │ │ Search... [🔍] │ (Search Bar) │ │ ├─────────────────────────────────────┤ │ │ │ ┌───────────────────────────────┐ │ │ │ [Thumbnail] Card Title │ │ (Card Component) │ │ Subtitle • Meta │ │ │ │ [Status Badge] │ │ │ └───────────────────────────────┘ │ │ │ │ ┌───────────────────────────────┐ │ │ │ [Thumbnail] Card Title │ │ │ │ Subtitle • Meta │ │ │ │ [Status Badge] │ │ │ └───────────────────────────────┘ │ │ │ │ ┌───────────────────────────────┐ │ │ │ [Thumbnail] Card Title │ │ │ │ Subtitle • Meta │ │ │ │ [Status Badge] │ │ │ └───────────────────────────────┘ │ │ │ ├─────────────────────────────────────┤ │ [Tab1] [Tab2] [Tab3] [Tab4] │ (Tab Bar) └─────────────────────────────────────┘
**Components**:
**Navigation Bar**:
- Title: "Home" (.largeTitle when scrolled to top, .inline when scrolling)
- Leading: Back button (if navigated from elsewhere)
- Trailing:
- [+] Add button → Navigate to Add Item
- [⚙️] Settings button → Navigate to Settings
**Search Bar** (Optional, based on requirements):
- Placeholder: "Search [items]..."
- Style: .searchable modifier
- Clear button when text entered
- Dismiss on scroll down
**Card List**:
- Container: SwiftUI List with .plain style
- Card spacing: 12pt between cards
- Card style: See "Card Component" section below
**Tab Bar** (if using tabs):
- 3-5 tabs maximum
- Active tab: Primary color
- Inactive tabs: Secondary/gray color
- Badge: Red dot for notifications (if applicable)
**Interactions**:
- **Tap card** → Navigate to Item Detail Screen
- **Swipe left on card** → Show Delete action (red background, trash icon)
- **Swipe right on card** → Show Archive action (if applicable)
- **Pull down** → Refresh content (show activity indicator)
- **Tap [+]** → Present Add Item sheet
- **Tap [⚙️]** → Navigate to Settings
- **Search** → Filter list in real-time as user types
- **Scroll to top** → Navigation bar title animates to large title
**States**:
**Empty State**:
┌─────────────────────────────────────┐ │ │ │ │ │ [Empty Icon] │ │ (tray) │ │ │ │ No Items Yet │ │ │ │ Get started by tapping the │ │ + button above │ │ │ │ │ └─────────────────────────────────────┘
- Use ContentUnavailableView (iOS 17+)
- Icon: SF Symbol related to content type
- Title: Friendly, not error-focused
- Description: Clear call to action
**Loading State**:
┌─────────────────────────────────────┐ │ │ │ │ │ [Loading Spinner] │ │ Loading... │ │ │ │ │ └─────────────────────────────────────┘
- Full-screen overlay with .ultraThinMaterial background
- Activity indicator (.large size)
- Optional "Loading..." text below
**Error State**:
┌─────────────────────────────────────┐ │ │ │ │ │ [Error Icon] (exclamationmark) │ │ │ │ Couldn't load items │ │ │ │ [Error description message] │ │ │ │ [Try Again Button] │ │ │ └─────────────────────────────────────┘
- ContentUnavailableView with error styling
- Clear error message (user-friendly, not technical)
- Retry button to attempt reload
**Skeleton/Shimmer State** (Alternative to loading spinner):
┌─────────────────────────────────────┐ │ ┌───────────────────────────────┐ │ │ │ [▓▓▓] ▓▓▓▓▓▓▓▓▓▓ │ │ (Shimmer effect) │ │ ▓▓▓▓▓▓▓ • ▓▓▓ │ │ │ └───────────────────────────────┘ │ │ ┌───────────────────────────────┐ │ │ │ [▓▓▓] ▓▓▓▓▓▓▓▓▓▓ │ │ │ │ ▓▓▓▓▓▓▓ • ▓▓▓ │ │ │ └───────────────────────────────┘ │ └─────────────────────────────────────┘
- Show 3-5 placeholder cards
- Animated shimmer effect (subtle, not distracting)
- Replace with real content when loaded
**Data Requirements**:
- Array of items: [Item]
- Each item needs: id, title, subtitle, thumbnailURL, status, metadata
- Sorting: By date (newest first) or user preference
- Filtering: Based on search query
**Accessibility**:
- VoiceOver label for each card: "[Title], [Subtitle], [Status]"
- Add button: "Add new item"
- Settings button: "Settings"
- Swipe actions announced: "Swipe right or left for actions"
- Search bar: "Search [items]"
---
### 3.3 Item Detail Screen
**Purpose**: Display full details of a single item
**Layout**:
┌─────────────────────────────────────┐ │ ← [Item Title] [Edit] │ (Navigation Bar) ├─────────────────────────────────────┤ │ │ │ [Hero Image/Thumbnail] │ │ (Full width) │ │ │ ├─────────────────────────────────────┤ │ │ │ [Item Title] │ │ [Subtitle or Category] │ │ │ │ [Status Badge] [Metadata] │ │ │ ├─────────────────────────────────────┤ │ │ │ Description │ │ ───────────── │ │ [Full description text, can be │ │ multiple paragraphs. Lorem ipsum │ │ dolor sit amet...] │ │ │ ├─────────────────────────────────────┤ │ │ │ Details │ │ ──────── │ │ Created: [Date] │ │ Modified: [Date] │ │ Author: [Name] │ │ │ ├─────────────────────────────────────┤ │ │ │ Related Items │ │ ────────────── │ │ ┌─────────┐ ┌─────────┐ │ │ │ [Item1] │ │ [Item2] │ │ │ └─────────┘ └─────────┘ │ │ │ ├─────────────────────────────────────┤ │ │ │ [Primary Action] │ │ [Secondary Action] │ │ │ └─────────────────────────────────────┘
**Components**:
- **Navigation Bar**: Item title (truncate if > 25 chars), Edit button
- **Hero Image**: Full-width, 16:9 aspect ratio, 250pt height
- **Title Section**: .title2 bold, subtitle .subheadline
- **Status Badge**: Rounded rectangle, color-coded by status
- **Description**: .body text, full paragraph formatting
- **Details Section**: Key-value pairs, .callout size, secondary color
- **Related Items**: Horizontal scroll, 100pt width cards
- **Action Buttons**: Primary (full width), Secondary (outlined)
**Interactions**:
- Tap "Edit" → Navigate to Edit Screen
- Tap Hero Image → Open full-screen image viewer (pinch to zoom)
- Tap Related Item → Navigate to that item's detail screen
- Swipe back → Return to Home
- Scroll → Navigation bar collapses to inline title
**States**:
- **Loading**: Show skeleton for all sections while data loads
- **Error**: Show error message inline (not full-screen)
- **No Image**: Show placeholder icon/gradient
- **No Description**: Hide section entirely (don't show empty)
- **No Related Items**: Hide section
---
### 3.4 Add/Edit Item Screen
**Purpose**: Create new item or modify existing
**Layout** (Sheet/Modal):
┌─────────────────────────────────────┐ │ [Cancel] Add Item [Save] │ (Modal Header) ├─────────────────────────────────────┤ │ │ │ ┌───────────────────────────────┐ │ │ │ + Add Photo │ │ (Image Picker) │ └───────────────────────────────┘ │ │ │ │ Title │ │ ┌───────────────────────────────┐ │ │ │ [Enter title...] │ │ │ └───────────────────────────────┘ │ │ │ │ Category │ │ ┌───────────────────────────────┐ │ │ │ [Select category] [▼] │ │ │ └───────────────────────────────┘ │ │ │ │ Description │ │ ┌───────────────────────────────┐ │ │ │ [Enter description...] │ │ │ │ │ │ │ │ │ │ │ └───────────────────────────────┘ │ │ │ │ Status │ │ ○ Active ○ Pending ○ Done │ │ │ │ │ │ [Delete Item] │ (Edit only) │ │ └─────────────────────────────────────┘
**Components**:
- **Modal Header**: Cancel (leading), Title (center), Save (trailing, blue)
- **Image Picker**: Dashed border, + icon, tap to open photo picker
- **Text Fields**: Native iOS style, floating labels
- **Dropdown**: Chevron indicator, opens picker/sheet
- **Text Area**: Multiline, min 3 lines
- **Radio Group**: Horizontal segmented control or vertical radio buttons
- **Delete Button**: Destructive style, bottom of form (edit mode only)
**Interactions**:
- Tap "Cancel" → Dismiss sheet, confirm if unsaved changes
- Tap "Save" → Validate, save, dismiss, return to previous screen
- Tap Image Picker → Present photo picker (Camera + Photo Library)
- Tap Dropdown → Present selection sheet/picker
- Typing → Show character count if there's a limit
- Tap Delete → Show destructive alert confirmation
**Validation**:
- Required fields: Show red outline + error message on save attempt
- Character limits: Show count (e.g., "120/150 characters")
- Invalid format: Show inline error below field
**States**:
- **Add Mode**: Empty fields, no Delete button
- **Edit Mode**: Pre-filled fields, Delete button visible
- **Saving**: Disable Save button, show spinner
- **Error**: Show alert with error message, keep form open
---
### 3.5 Settings Screen
**Purpose**: App preferences and account management
**Layout**:
┌─────────────────────────────────────┐ │ ← Settings │ ├─────────────────────────────────────┤ │ │ │ Account │ │ ┌───────────────────────────────┐ │ │ │ [👤] [User Name] [>] │ │ │ │ [email protected] │ │ │ └───────────────────────────────┘ │ │ │ │ Preferences │ │ ┌───────────────────────────────┐ │ │ │ Notifications [○●] │ │ (Toggle) │ │ Dark Mode [○●] │ │ │ │ Language [>] │ │ │ └───────────────────────────────┘ │ │ │ │ Data & Privacy │ │ ┌───────────────────────────────┐ │ │ │ Privacy Policy [>] │ │ │ │ Terms of Service [>] │ │ │ │ Delete Account [>] │ │ │ └───────────────────────────────┘ │ │ │ │ About │ │ ┌───────────────────────────────┐ │ │ │ App Version 1.0.0 │ │ │ │ Rate Us [>] │ │ │ │ Contact Support [>] │ │ │ └───────────────────────────────┘ │ │ │ │ │ │ [Sign Out] │ │ │ └─────────────────────────────────────┘
**Components**:
- **Section Headers**: .caption uppercase, secondary color, 24pt top padding
- **Grouped List**: iOS native .insetGrouped style
- **Row**: 44pt min height, tap area full width
- **Toggle**: iOS native switch, tappable label + switch
- **Disclosure**: Chevron on trailing edge
- **Sign Out Button**: Destructive style, centered, bottom
**Interactions**:
- Tap row with [>] → Navigate to detail screen
- Tap toggle → Immediate change (save to UserDefaults/CloudKit)
- Tap "Sign Out" → Show confirmation alert
---
## 4. Component Library
### 4.1 Buttons
#### Primary Button
**Style**:
- Background: Brand Primary color
- Text: White, .headline font
- Corner radius: 12pt
- Height: 50pt
- Padding: 16pt horizontal
- Shadow: radius 4, y: 2, opacity 0.1
**States**:
- Normal: Full color
- Pressed: Opacity 0.8 + scale 0.98
- Disabled: Opacity 0.5, no interaction
- Loading: Show spinner, disable interaction
**Code Example**:
```swift
.font(.headline)
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.frame(height: 50)
.background(Color.brandPrimary)
.cornerRadius(12)
.shadow(radius: 4, y: 2)
Style:
States: Same as Primary Button
Style:
States:
Style:
Layout:
┌───────────────────────────────┐
│ [Thumbnail] Title │
│ (60x60pt) Subtitle │
│ Metadata │
└───────────────────────────────┘
Spacing:
Differences from Standard:
Style:
States:
With Label:
With Error:
Usage Guide:
| Gesture | Use Case | Feedback | |---------|----------|----------| | Tap | Primary action, select, open | Visual (highlight), Haptic (light) | | Long Press | Context menu, preview | Visual (scale up), Haptic (medium) | | Swipe Left | Delete, remove | Visual (red background), Haptic (none) | | Swipe Right | Archive, mark done | Visual (green background), Haptic (success) | | Pull Down | Refresh content | Visual (activity indicator), Haptic (light when triggered) | | Pinch | Zoom image, map | Visual (scale), Haptic (none) | | Edge Swipe | Back navigation | Visual (screen slide), Haptic (none) |
When to Use:
Implementation:
import CoreHaptics
// Light impact
let impact = UIImpactFeedbackGenerator(style: .light)
impact.impactOccurred()
// Success notification
let notification = UINotificationFeedbackGenerator()
notification.notificationOccurred(.success)
Requirements:
Examples:
Testing:
Support all text sizes: Extra Small to Accessibility XXL
Implementation:
Text("Title")
.font(.title) // Automatically scales
.lineLimit(nil) // Allow wrapping
.minimumScaleFactor(0.8) // If must fit one line
Layout Adjustments:
Testing:
WCAG AA Standards:
Testing Tools:
Common Issues:
Respect System Setting:
@Environment(\.accessibilityReduceMotion) var reduceMotion
if reduceMotion {
// Use fade instead of slide
// Reduce animation duration
// Remove complex animations
}
Alternatives:
Semantic Colors (Automatic):
.primary, .secondary for text.background, .secondaryBackground for surfacesCustom Colors:
Techniques:
Do:
Don't:
Layouts to Support:
Adaptation Strategy:
Layout Changes:
Keyboard Support:
Portrait: Default, all screens optimized Landscape:
| State | User Experience | System Behavior | |-------|-----------------|-----------------| | No connection | "No internet connection. Showing saved data." banner at top | Use cached data, disable sync, queue changes | | Slow connection | Loading indicator, timeout after 30s | Show skeleton screens, async load images | | Connection restored | "Back online" banner (auto-dismiss 3s) | Trigger sync, upload queued changes |
| State | User Experience | System Behavior | |-------|-----------------|-----------------| | Empty list | ContentUnavailableView with CTA | Hide list, show empty state | | Single item | Show list with one item (not special case) | Same as multiple items | | Thousands of items | Paginated, lazy loading, search encouraged | Load 50 at a time, virtual scrolling |
| Invalid Input | User Experience | Prevention | |---------------|-----------------|------------| | Empty required field | Red outline, "This field is required" below | Disable Save button | | Invalid email | "Please enter a valid email" | Real-time validation | | Duplicate entry | Alert: "Item already exists. Continue?" | Check on save, allow override |
| Permission | Denied | Granted | |------------|--------|---------| | Camera | Show alert: "Camera access needed. Open Settings?" | Open camera picker | | Photos | Show placeholder, "Tap to allow photo access" | Open photo picker | | Notifications | App works without, show banner once | Send notifications | | Location | Feature disabled, show upgrade message | Show map with location |
Follow These Patterns:
Avoid:
Adopt New APIs:
Test on iOS 17+: Set minimum deployment target to iOS 17.0
Before considering UX spec complete:
Use this template for additional screens not detailed above:
### X.X [Screen Name]
**Purpose**: [One sentence describing what user accomplishes]
**Layout** (ASCII wireframe):
┌─────────────────────────────────────┐ │ [Navigation Bar] │ ├─────────────────────────────────────┤ │ │ │ [Main Content Area] │ │ │ ├─────────────────────────────────────┤ │ [Bottom Bar / Actions] │ └─────────────────────────────────────┘
**Components**:
- [Component 1]: [Description]
- [Component 2]: [Description]
**Interactions**:
- Tap [Element] → [Result]
- Swipe [Direction] → [Action]
**States**:
- Empty: [How it looks]
- Loading: [How it looks]
- Error: [How it looks]
**Data Requirements**:
- [Data needed from backend/storage]
**Accessibility**:
- [VoiceOver labels]
- [Special considerations]
Document History: | Version | Date | Author | Changes | |---------|------|--------|---------| | 1.0.0 | [Date] | UX Designer AI | Initial UX specification |
### 2. docs/DESIGN_SYSTEM.md
```markdown
# Design System: [App Name]
**Version**: 1.0.0
**Last Updated**: [Date]
**Platform**: iOS 17+
**Language**: SwiftUI
---
## 1. Brand Identity
### 1.1 Brand Personality
**From Positioning**: [e.g., "Professional, trustworthy, modern with a human touch"]
**Visual Expression**:
- **Modern**: Clean lines, generous whitespace, sans-serif typography
- **Professional**: Muted color palette, consistent spacing, subtle animations
- **Approachable**: Friendly copy, helpful empty states, forgiving errors
### 1.2 Voice & Tone
**Voice** (Consistent):
- Clear and concise
- Helpful, not bossy
- Human, not robotic
- Confident, not arrogant
**Tone** (Context-dependent):
- **Success**: Encouraging ("Great job!")
- **Error**: Apologetic and helpful ("Oops! Let's try that again.")
- **Onboarding**: Excited and welcoming ("Welcome! Let's get started.")
- **Empty state**: Optimistic ("Ready to create your first item?")
---
## 2. Color Palette
### 2.1 Primary Colors
**Brand Primary** (Main CTA, key actions):
```swift
// Light mode
static let brandPrimary = Color(hex: "#007AFF") // iOS Blue
// Dark mode
static let brandPrimaryDark = Color(hex: "#0A84FF") // Lighter blue
Brand Secondary (Accents, highlights):
// Light mode
static let brandSecondary = Color(hex: "#5856D6") // Purple
// Dark mode
static let brandSecondaryDark = Color(hex: "#5E5CE6")
Brand Accent (Destructive actions, warnings):
// Light mode
static let brandAccent = Color(hex: "#FF3B30") // Red
// Dark mode
static let brandAccentDark = Color(hex: "#FF453A")
Success (Confirmation, completion):
static let success = Color(hex: "#34C759") // Green
Warning (Caution, attention needed):
static let warning = Color(hex: "#FF9500") // Orange
Error (Errors, destructive actions):
static let error = Color(hex: "#FF3B30") // Red (same as accent)
Info (Tips, information):
static let info = Color(hex: "#007AFF") // Blue (same as primary)
Backgrounds:
// Light mode
static let background = Color.white
static let secondaryBackground = Color(hex: "#F2F2F7") // iOS gray
static let tertiaryBackground = Color(hex: "#FFFFFF")
// Dark mode
static let backgroundDark = Color(hex: "#000000")
static let secondaryBackgroundDark = Color(hex: "#1C1C1E")
static let tertiaryBackgroundDark = Color(hex: "#2C2C2E")
Text:
// Light mode
static let textPrimary = Color(hex: "#000000")
static let textSecondary = Color(hex: "#3C3C43", opacity: 0.6) // iOS secondary label
static let textTertiary = Color(hex: "#3C3C43", opacity: 0.3)
// Dark mode
static let textPrimaryDark = Color(hex: "#FFFFFF")
static let textSecondaryDark = Color(hex: "#EBEBF5", opacity: 0.6)
static let textTertiaryDark = Color(hex: "#EBEBF5", opacity: 0.3)
Borders & Dividers:
static let separator = Color(hex: "#3C3C43", opacity: 0.36)
static let border = Color(hex: "#C7C7CC")
Primary Color:
Secondary Color:
Accent Color:
Backgrounds:
iOS Dynamic Type (Automatically scales with user preference):
// Display
.largeTitle // 34pt, Bold - Screen titles
.title // 28pt, Bold - Major sections
.title2 // 22pt, Bold - Subsections, cards
.title3 // 20pt, Semibold - Groups
// Body
.headline // 17pt, Semibold - Emphasized text, button labels
.body // 17pt, Regular - Default body text
.callout // 16pt, Regular - Secondary content
// Supporting
.subheadline // 15pt, Regular - Subtitles, labels
.footnote // 13pt, Regular - Timestamps, metadata
.caption // 12pt, Regular - Section headers (uppercase)
.caption2 // 11pt, Regular - Fine print
Default: System Font (SF Pro)
.ultraLight // Avoid, too thin
.thin // Avoid
.light // Occasional use
.regular // Default for body text
.medium // Occasional emphasis
.semibold // Headings, emphasized text
.bold // Titles, important actions
.heavy // Avoid, too thick
.black // Avoid
Default: SwiftUI handles automatically Custom (if needed):
.lineSpacing(4) // Add 4pt between lines (for dense text)
Paragraph Spacing: 12pt between paragraphs
| Element | Style | Use Case | |---------|-------|----------| | Screen Title | .largeTitle | Home, Settings navigation bar | | Section Header | .title2 | "Recent Items", "Profile" | | Card Title | .headline | Item name in list | | Body Text | .body | Descriptions, paragraphs | | Metadata | .footnote | Timestamps, "Created 2h ago" | | Button Label | .headline | All buttons | | Input Label | .caption | Above text fields | | Placeholder | .body + opacity 0.5 | Text field placeholder |
Base Unit: 4pt
enum Spacing {
static let xxs: CGFloat = 2 // Tight spacing (icon to text)
static let xs: CGFloat = 4 // Very small (between related elements)
static let s: CGFloat = 8 // Small (list item internal padding)
static let m: CGFloat = 12 // Medium (between components)
static let l: CGFloat = 16 // Large (card padding, margins) ⭐ Default
static let xl: CGFloat = 24 // Extra large (section spacing)
static let xxl: CGFloat = 32 // Screen margins, major sections
static let xxxl: CGFloat = 48 // Between major screen areas
}
Padding (Inside components):
Margins (Between components):
Stack Spacing:
VStack(spacing: 12) { ... } // Default
HStack(spacing: 8) { ... } // Tight
VStack(spacing: 24) { ... } // Loose
Columns: 12-column grid (flexible) Gutter: 16pt between columns Margins: 16pt (iPhone), 20pt (iPad)
Usage:
| Device | Width (pt) | Columns | Margins | |--------|-----------|---------|---------| | iPhone SE | 375 | 1 | 16pt | | iPhone 15 | 393 | 1 | 16pt | | iPhone Pro Max | 430 | 1 | 16pt | | iPad Portrait | 768 | 2 | 20pt | | iPad Landscape | 1024 | 3 | 20pt |
Primary: SF Symbols (native, 4000+ icons) Custom: Only if SF Symbols doesn't have the icon
.small // 16x16pt - Inline with text
.medium // 20x20pt - Default
.large // 24x24pt - Emphasized
.xlarge // 28x28pt - Hero icons
Match text weight:
Image(systemName: "star.fill")
.font(.title2) // Size
.fontWeight(.semibold) // Weight
Default: Match text color Tinted: Brand primary for active/selected Multicolor: SF Symbols multicolor variant (sparingly)
| Use Case | SF Symbol | Alternative | |----------|-----------|-------------| | Add | plus | plus.circle.fill | | Delete | trash | trash.fill | | Edit | pencil | pencil.circle | | Search | magnifyingglass | - | | Settings | gearshape | gearshape.fill | | Profile | person | person.circle | | Home | house | house.fill | | Favorites | star | star.fill | | Share | square.and.arrow.up | - | | Close | xmark | xmark.circle.fill | | Back | chevron.left | arrow.left | | Success | checkmark.circle.fill | - | | Error | xmark.circle.fill | exclamationmark.triangle.fill | | Warning | exclamationmark.triangle | - | | Info | info.circle | questionmark.circle |
Level 1 (Cards, subtle elevation):
.shadow(color: .black.opacity(0.1), radius: 8, y: 2)
Level 2 (Modals, popovers):
.shadow(color: .black.opacity(0.15), radius: 16, y: 4)
Level 3 (Dropdowns, tooltips):
.shadow(color: .black.opacity(0.2), radius: 24, y: 8)
When to Use Shadows:
When NOT to Use:
Replace shadows with borders:
if colorScheme == .dark {
.border(Color.white.opacity(0.1), width: 1)
} else {
.shadow(radius: 8, y: 2)
}
enum CornerRadius {
static let xs: CGFloat = 4 // Very subtle (badges)
static let s: CGFloat = 8 // Small (buttons, inputs)
static let m: CGFloat = 12 // Medium (cards) ⭐ Default
static let l: CGFloat = 16 // Large (modals, sheets)
static let xl: CGFloat = 24 // Extra large (hero cards)
static let full: CGFloat = 9999 // Fully rounded (pills, avatars)
}
| Element | Radius | Rationale | |---------|--------|-----------| | Buttons | 12pt | iOS standard, comfortable | | Text inputs | 10pt | Slightly less than buttons (visual distinction) | | Cards | 12pt | Consistent with buttons | | Modals/Sheets | 16pt | Larger surface, more prominence | | Badges | 8pt or full | Depends on style (rounded or pill) | | Avatars | full | Circular | | Thumbnails | 8pt | Subtle rounding |
enum AnimationDuration {
static let fast: Double = 0.2 // Button press, toggle
static let standard: Double = 0.3 // Screen transition, fade ⭐ Default
static let slow: Double = 0.5 // Complex animations
static let verySlow: Double = 1.0 // Onboarding, celebration
}
Default: .easeInOut (most natural)
.animation(.easeInOut(duration: 0.3), value: someValue)
Spring: For playful, bouncy effects
.animation(.spring(response: 0.3, dampingFraction: 0.7), value: someValue)
Linear: For progress bars, loaders
.animation(.linear(duration: 2.0), value: progress)
Fade In/Out:
.opacity(isVisible ? 1 : 0)
.animation(.easeInOut(duration: 0.3), value: isVisible)
Slide In:
.offset(y: isVisible ? 0 : 50)
.opacity(isVisible ? 1 : 0)
.animation(.easeOut(duration: 0.4), value: isVisible)
Scale (Button Press):
.scaleEffect(isPressed ? 0.95 : 1.0)
.animation(.easeInOut(duration: 0.1), value: isPressed)
Rotation:
.rotationEffect(.degrees(isLoading ? 360 : 0))
.animation(.linear(duration: 1.0).repeatForever(autoreverses: false), value: isLoading)
Respect accessibility setting:
@Environment(\.accessibilityReduceMotion) var reduceMotion
if reduceMotion {
// Use fade instead of slide
.opacity(isVisible ? 1 : 0)
} else {
// Use full animation
.offset(y: isVisible ? 0 : 50)
.opacity(isVisible ? 1 : 0)
}
struct PrimaryButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.font(.headline)
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.frame(height: 50)
.background(Color.brandPrimary)
.cornerRadius(12)
.scaleEffect(configuration.isPressed ? 0.98 : 1.0)
.animation(.easeInOut(duration: 0.1), value: configuration.isPressed)
}
}
// Usage:
Button("Continue") { }
.buttonStyle(PrimaryButtonStyle())
extension View {
func cardStyle() -> some View {
self
.padding(16)
.background(Color(.secondarySystemBackground))
.cornerRadius(12)
.shadow(color: .black.opacity(0.1), radius: 8, y: 2)
}
}
// Usage:
VStack { ... }
.cardStyle()
struct CustomTextFieldStyle: TextFieldStyle {
func _body(configuration: TextField<Self._Label>) -> some View {
configuration
.padding(16)
.background(Color(.secondarySystemBackground))
.cornerRadius(10)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.brandPrimary.opacity(0.3), lineWidth: 1)
)
}
}
// Usage:
TextField("Enter name", text: $name)
.textFieldStyle(CustomTextFieldStyle())
Location: Assets.xcassets/Colors/
Naming Convention:
BrandPrimary (with Light + Dark appearances)BrandSecondaryTextPrimaryBackgroundSecondaryLocation: Assets.xcassets/Images/
Naming Convention:
icon-name (lowercase, hyphen-separated)illustration-onboarding-1placeholder-avatarFormats:
No need to add to assets - use directly by name:
Image(systemName: "star.fill")
For developers: centralized constants file
// DesignTokens.swift
import SwiftUI
enum DesignSystem {
// Colors
enum Colors {
static let brandPrimary = Color("BrandPrimary")
static let brandSecondary = Color("BrandSecondary")
static let textPrimary = Color.primary
static let textSecondary = Color.secondary
static let success = Color.green
static let error = Color.red
static let warning = Color.orange
}
// Typography
enum Typography {
static let largeTitle = Font.largeTitle
static let title = Font.title
static let headline = Font.headline
static let body = Font.body
static let caption = Font.caption
}
// Spacing
enum Spacing {
static let xs: CGFloat = 4
static let s: CGFloat = 8
static let m: CGFloat = 12
static let l: CGFloat = 16
static let xl: CGFloat = 24
static let xxl: CGFloat = 32
}
// Corner Radius
enum Radius {
static let small: CGFloat = 8
static let medium: CGFloat = 12
static let large: CGFloat = 16
static let full: CGFloat = 9999
}
// Shadows
enum Shadow {
static let card = (color: Color.black.opacity(0.1), radius: CGFloat(8), y: CGFloat(2))
static let modal = (color: Color.black.opacity(0.15), radius: CGFloat(16), y: CGFloat(4))
}
// Animation
enum Animation {
static let fast = 0.2
static let standard = 0.3
static let slow = 0.5
}
}
// Usage:
Text("Hello")
.font(DesignSystem.Typography.headline)
.foregroundColor(DesignSystem.Colors.brandPrimary)
.padding(DesignSystem.Spacing.l)
Before considering design system complete:
Document History: | Version | Date | Author | Changes | |---------|------|--------|---------| | 1.0.0 | [Date] | UX Designer AI | Initial design system |
---
## Execution Instructions
When activated, follow these steps:
1. **Locate and Read Input Documents**
2. **Extract Key Information**
From inputs, extract:
- All screens needed (from PRD features)
- Navigation structure (from architecture)
- Brand personality (from positioning)
- Core features to design (from PRD MVP scope)
- Platform (iOS/macOS)
3. **Design Information Architecture**
- List all screens
- Determine navigation pattern (tabs, stack, sidebar)
- Create navigation flow diagram
- Prioritize screens (P0, P1, P2)
4. **Create Wireframes for Core Screens**
For each P0 screen:
- Draw ASCII wireframe (clear layout)
- Specify all components
- Document all interactions
- Define states (empty, loading, error)
- List data requirements
5. **Define Design System**
Based on brand personality:
- Choose color palette (align with positioning)
- Define typography scale (iOS standard)
- Set spacing system (8pt grid)
- Specify component styles (buttons, cards, inputs)
- Document animation timings
6. **Generate Output Files**
Write comprehensive UX_SPEC.md to: docs/UX_SPEC.md Write complete DESIGN_SYSTEM.md to: docs/DESIGN_SYSTEM.md
7. **Present to User**
After generating files, present summary:
✅ UI/UX Specifications generated!
📱 UX_SPEC.md Summary:
🎨 DESIGN_SYSTEM.md Summary:
Next Steps:
Would you like me to make any changes to the UI design?
8. **Iterate Based on Feedback**
If user requests changes:
- Ask clarifying questions
- Update UX_SPEC.md and/or DESIGN_SYSTEM.md
- Maintain consistency across both documents
---
## Quality Guidelines
When generating the UX specifications:
1. **Be Visual**: ASCII wireframes should be clear and detailed
- BAD: Just describe "A list of items"
- GOOD: Draw the actual layout with boxes, labels, and spacing
2. **Be Specific**: Every interaction should be defined
- BAD: "User can tap the button"
- GOOD: "Tap 'Save' button → Validate fields → Show loading spinner → Dismiss sheet → Return to Home with success toast"
3. **Be Complete**: Cover all states
- Happy path (default)
- Empty state (no data)
- Loading state (data fetching)
- Error state (failed to load)
- Skeleton/placeholder (progressive loading)
4. **Follow iOS HIG**: Use native patterns
- NavigationStack for hierarchical navigation
- TabView for flat navigation
- Sheet for forms/modals
- List for collections
- SwiftUI standard components
5. **Design for Accessibility**: Not an afterthought
- VoiceOver labels for every element
- Dynamic Type support
- Color contrast 4.5:1 minimum
- Reduced motion alternatives
6. **Be Consistent**: Reference design system
- Use defined colors (don't introduce new ones)
- Use spacing scale (not random values)
- Use typography scale (not arbitrary sizes)
- Reuse components (don't create one-offs)
---
## Example Activation
**User**: "Generate UX spec from PRD"
**You**:
1. Read docs/PRD.md
2. Read docs/ARCHITECTURE.md
3. Read product-plan-*.md for positioning
4. Extract all features needing screens
5. Design wireframes for each screen (ASCII art)
6. Create comprehensive design system
7. Write both documents to docs/
8. Present summary with next steps
---
## Integration with Workflow
This skill is typically:
- **Third step** in specification generation
- Activated after PRD and Architecture are complete
- Followed by implementation-guide, test-spec, release-spec
The UX specifications serve as the visual blueprint that developers use to implement the UI.
---
## Notes
- Focus on iOS patterns - don't reinvent the wheel
- ASCII wireframes are sufficient - no need for high-fidelity mockups
- Provide enough detail that a developer can implement without guessing
- Reference Apple's Human Interface Guidelines throughout
- Ensure all specifications are actionable and unambiguous
- Keep design system tokens consistent across both documents
- Test all designs mentally for accessibility before documenting
development
US web checkout via the StoreKit External Purchase Link entitlement — currently 0% Apple commission (litigation ongoing), how to ship it safely, and how to architect for a commission flip so a future ruling is a config change, not a rewrite. Use when adding external purchase links, weighing web checkout vs IAP, or planning US-storefront pricing strategy.
tools
Revenue beyond the single-app price tag — own-app bundles, Family Sharing as a conversion lever, cross-developer bundles & suites, and institutional licensing via Group Purchases / Apple School & Business Manager. Use when a developer has multiple apps, a subscription worth sharing, complementary indie partners, or school/clinic/business buyers.
testing
Run a structured accessibility audit on an iOS/macOS app — automated XCUITest audits, Accessibility Inspector, manual VoiceOver/Dynamic Type passes, and App Store Accessibility Nutrition Label evaluation. Use before release, when preparing Nutrition Label declarations, or for EU Accessibility Act compliance.
tools
Stage-by-stage audit of an app's App Store growth machinery against a 54-item P0–P9 playbook — every item scored from an App Store Connect MCP call, a codebase check, or an explicit question to the user, then routed to the skill or command that fixes it. Read-only on App Store Connect. Use for a growth audit or scorecard, a pre-launch growth plan, a quarterly re-audit, or "which growth levers am I missing."