Get the FREE Ultimate OpenClaw Setup Guide →

ax-design

npx machina-cli add skill Kasempiternal/axiom-v2/ax-design --openclaw
Files (1)
SKILL.md
11.7 KB

Apple Design & SF Symbols

Quick Patterns

Background Color Decision

Is your app media-focused (photos, videos, music)?
+-- Yes --> permanent dark appearance (.preferredColorScheme(.dark))
+-- No  --> system backgrounds (respect user Light/Dark preference)
         systemBackground (main), systemGroupedBackground (Settings-style)

Label Hierarchy

Text("Title").foregroundStyle(.primary)
Text("Subtitle").foregroundStyle(.secondary)
Text("Detail").foregroundStyle(.tertiary)
Text("Disabled").foregroundStyle(.quaternary)

SF Symbol Rendering Mode Selection

Need depth from ONE color?        --> Hierarchical
Need specific colors per layer?   --> Palette
Want Apple's curated colors?      --> Multicolor
Just need a tinted icon?          --> Monochrome (default)

Symbol Effect Selection

User tapped something             --> .bounce (discrete)
Draw attention to change          --> .wiggle (discrete, iOS 18+)
Ongoing process / loading         --> .pulse, .breathe, .variableColor (indefinite)
Rotation indicates progress       --> .rotate (indefinite, iOS 18+)
Show/hide symbol                  --> .appear / .disappear (transition)
Swap between two symbols          --> .replace (content transition)
Hand-drawn entry/exit             --> .drawOn / .drawOff (iOS 26+)
Progress along path               --> Variable Draw (iOS 26+)

Bounce on Tap

@State private var count = 0

Image(systemName: "arrow.down.circle")
    .symbolEffect(.bounce, value: count)

Content Transition (Symbol Swap)

Image(systemName: isFavorite ? "star.fill" : "star")
    .contentTransition(.symbolEffect(.replace))
    .accessibilityLabel(isFavorite ? "Remove from favorites" : "Add to favorites")

Decision Tree

What design question do you have?
|
+-- Background color?
|   +-- Media-focused app --> permanent dark
|   +-- Everything else --> systemBackground (adapts to light/dark)
|   +-- Grouped content --> systemGroupedBackground
|
+-- Color choice?
|   +-- UI elements --> semantic colors (label, secondaryLabel, etc.)
|   +-- Brand color needed --> Color Set in asset catalog (light + dark + high contrast)
|   +-- Never hardcode --> Color.black / Color.white
|
+-- Font weight?
|   +-- Avoid: Ultralight, Thin, Light (legibility issues)
|   +-- Headers: Semibold or Bold
|   +-- Body: Regular or Medium
|
+-- SF Symbol rendering?
|   +-- Single tint --> Monochrome (default)
|   +-- Depth from one hue --> Hierarchical
|   +-- Brand/status colors --> Palette (explicit per-layer)
|   +-- Apple's curated colors --> Multicolor (not all symbols support it)
|
+-- Symbol animation?
|   +-- Tap feedback --> Bounce (discrete, value: trigger)
|   +-- Loading/waiting --> Pulse or Breathe (indefinite, isActive: bool)
|   +-- WiFi/signal cycling --> Variable Color (indefinite)
|   +-- Processing spinner --> Rotate (indefinite, iOS 18+)
|   +-- Attention shake --> Wiggle (discrete, iOS 18+)
|   +-- Swap symbols --> Replace content transition
|   +-- Pen-drawn entry --> Draw On (iOS 26+)
|
+-- Light/Dark mode?
|   +-- Always support both, never create app-specific toggle
|   +-- Use semantic colors that adapt automatically
|
+-- Touch targets?
    +-- iOS/iPadOS: 44x44 points minimum
    +-- macOS: 20x20 points minimum
    +-- Contrast: 4.5:1 minimum, 7:1 for small text

Anti-Patterns

HIG Violations

  • Hardcoded Color.black/.white -- use semantic colors (systemBackground, label)
  • App-specific dark mode toggle -- users expect systemwide preference honored
  • Logo on every screen / navigation bar -- wastes space, violates content-first principle
  • Ultralight/Thin/Light font weights -- legibility failures, accessibility risk
  • Custom brand color for all text -- may fail 4.5:1 contrast requirement
  • Splash screen with branding -- launch screens cannot include branding per HIG

SF Symbol Mistakes

  • .foregroundColor() with Multicolor mode -- overrides Apple's curated colors
  • Palette with only 1 color -- equivalent to Monochrome, provide colors for each layer
  • Assuming all symbols support Multicolor -- unsupported fall back to Monochrome
  • Hierarchical for status indicators where layers need distinct meaning -- use Palette
  • Bounce effect for loading state -- one-shot, doesn't convey "ongoing"; use Pulse/Breathe
  • Pulse for tap feedback -- too subtle; use Bounce
  • Missing .accessibilityLabel on symbol images -- VoiceOver reads raw symbol name
  • Using iOS 18+ effects without version check -- crashes on iOS 17

Rendering Mode + Effect Conflicts

  • Multiple .symbolEffect() on same view can conflict -- use single effect or combine with options
  • .tint vs .foregroundStyle confusion in UIKit -- tintColor for Mono/Hierarchical, paletteColors for Palette

Deep Patterns

Four Rendering Modes

Monochrome (default)

Image(systemName: "cloud.rain.fill")
    .foregroundStyle(.blue)
// All layers same color. Use for: toolbar items, tab bar, matching text.

Hierarchical

Image(systemName: "cloud.rain.fill")
    .symbolRenderingMode(.hierarchical)
    .foregroundStyle(.blue)
// Layers at different opacities from single color. Most common for polished UI.

Palette

Image(systemName: "cloud.rain.fill")
    .symbolRenderingMode(.palette)
    .foregroundStyle(.blue, .cyan)
// Explicit per-layer colors. Fewer colors than layers: last color reused.

Multicolor

Image(systemName: "cloud.rain.fill")
    .symbolRenderingMode(.multicolor)
// Apple's fixed curated colors. Cannot customize. Not all symbols support it.

Discrete Effects (Fire Once on Value Change)

// Bounce
.symbolEffect(.bounce, value: count)
.symbolEffect(.bounce.up, value: count)

// Wiggle (iOS 18+)
.symbolEffect(.wiggle, value: notificationCount)
.symbolEffect(.wiggle.forward, value: count)  // RTL-aware

// Rotate (discrete, iOS 18+)
.symbolEffect(.rotate, value: refreshCount)

Indefinite Effects (Continuous While Active)

// Pulse -- subtle opacity pulse for waiting
.symbolEffect(.pulse, isActive: isConnecting)

// Variable Color -- layer cycling for signal/progress
.symbolEffect(.variableColor.iterative, isActive: isSearching)
.symbolEffect(.variableColor.cumulative, isActive: isLoading)

// Scale
.symbolEffect(.scale.up, isActive: isRecording)

// Breathe (iOS 18+) -- rhythmic scale
.symbolEffect(.breathe, isActive: isMonitoring)

// Rotate (indefinite, iOS 18+)
.symbolEffect(.rotate, isActive: isProcessing)

Effect Options

.symbolEffect(.bounce, options: .repeat(3), value: count)
.symbolEffect(.pulse, options: .speed(2.0), isActive: true)
.symbolEffect(.bounce, options: .repeat(5).speed(1.5), value: count)

Transitions (View Enter/Leave)

if showSymbol {
    Image(systemName: "checkmark.circle.fill")
        .transition(.symbolEffect(.appear))
}
// .appear.up, .appear.down, .disappear.up, .disappear.down

Content Transitions (Symbol Swap)

Image(systemName: isFavorite ? "star.fill" : "star")
    .contentTransition(.symbolEffect(.replace))

// Replace variants: .replace.downUp, .replace.upUp, .replace.offUp
// Magic Replace automatic for structurally similar pairs (star/star.fill)

Draw Animations (iOS 26+, SF Symbols 7)

Draw On / Draw Off

Image(systemName: "checkmark.circle")
    .symbolEffect(.drawOn, isActive: isComplete)

Image(systemName: "star.fill")
    .symbolEffect(.drawOff, isActive: isHidden)

Playback Modes

.symbolEffect(.drawOn.byLayer, isActive: show)        // Staggered (default)
.symbolEffect(.drawOn.wholeSymbol, isActive: show)     // All at once
.symbolEffect(.drawOn.individually, isActive: show)    // Sequential

Variable Draw (Progress)

Image(systemName: "thermometer.high", variableValue: temperature)
    .symbolVariableValueMode(.draw)  // Path-based progress

Gradient Rendering (iOS 26+)

Image(systemName: "star.fill")
    .symbolColorRenderingMode(.gradient)
    .foregroundStyle(.red)

Version-Safe Effect Usage

struct BellEffect: ViewModifier {
    let count: Int
    func body(content: Content) -> some View {
        if #available(iOS 18, *) {
            content.symbolEffect(.wiggle, value: count)
        } else {
            content.symbolEffect(.bounce, value: count)
        }
    }
}

Custom Symbol Draw Annotation

  1. Open custom symbol in SF Symbols 7 app
  2. Select path layer, add guide points (start, end, corner, bidirectional)
  3. Minimum 2 guide points per path
  4. Test in Preview panel across weight variants

HIG Core Principles

  1. Clarity -- content paramount, interface defers, every element has purpose
  2. Consistency -- standard gestures, platform conventions, system colors/fonts
  3. Deference -- UI doesn't compete with content, subtle backgrounds, restrained branding

Pre-Ship Checklist

  • Light Mode + Dark Mode tested
  • Increased Contrast + Reduce Transparency tested
  • Dynamic Type scales to 200% without truncation
  • No light font weights (Regular minimum)
  • Contrast ratio >= 4.5:1 (7:1 for small text)
  • Touch targets >= 44x44 points
  • Information conveyed by more than color alone
  • VoiceOver labels on all interactive elements + symbols
  • Reduce Motion respected (symbol effects auto-handled)
  • RTL language support, no hardcoded strings in images

Platform Quick Tips

PlatformKey Design Notes
iOSPortrait-first, one-handed reach, bottom tab bar, swipe back
iPadOSSidebar-adaptable, split view, pointer, arbitrary windows (iOS 26+)
macOSMenu bar commands, dense layouts, pointer-first, window chrome
watchOSGlanceable, full-bleed, Digital Crown
tvOSFocus-based, 10-foot distance, large targets
visionOSSpatial layout, glass materials, comfortable depth

Diagnostics

Symbol Effect Not Playing

  1. Check iOS version -- Bounce/Pulse/Scale: iOS 17+, Wiggle/Rotate/Breathe: iOS 18+, Draw: iOS 26+
  2. Check Reduce Motion setting -- most effects auto-suppressed when enabled
  3. Check trigger type -- discrete needs value: that changes, indefinite needs isActive: true
  4. Check symbol compatibility in SF Symbols app Animation inspector
  5. Check for conflicting .symbolEffect() modifiers on same view

Wrong Symbol Colors

  1. Rendering mode not set -- add .symbolRenderingMode(.hierarchical) or .palette
  2. .foregroundStyle from parent overriding -- apply rendering mode directly on Image
  3. Multicolor not supported by symbol -- falls back to Monochrome silently

Custom Symbol Weight Mismatch

Symbol weight follows applied .font() weight. Ensure custom symbol has 9 weight variants exported from SF Symbols app. Use .imageScale() for size, .font() for weight matching.

Draw Animation Not Working on Custom Symbol

Needs Draw annotation with >= 2 guide points per path. Guide points on stroked paths, not fills. Requires SF Symbols 7+ app for annotation.

Defending HIG Decisions

When stakeholders request HIG violations (logo everywhere, light fonts, custom dark toggle):

  1. Show Apple's specific guidance
  2. Demonstrate accessibility/rejection risk
  3. Offer HIG-compliant alternatives (brand tint color, accent color for actions, onboarding branding)
  4. If overruled, document decision and risks in writing

Related

  • ax-design-ref -- comprehensive HIG API reference, typography scales, Liquid Glass design specs
  • ax-swiftui -- SwiftUI patterns and decision guidance
  • ax-swiftui-ref -- SwiftUI API reference including animation system

Source

git clone https://github.com/Kasempiternal/axiom-v2/blob/main/axiom-plugin/skills/ax-design/SKILL.mdView on GitHub

Overview

This skill covers applying Apple Human Interface Guidelines and SF Symbols, including rendering modes, symbol effects/animations, and accessibility. It also addresses decision-making patterns and defending HIG choices in design reviews for consistent, accessible iOS interfaces.

How This Skill Works

Follow the provided decision tree and patterns to decide background color, label hierarchy, and SF Symbol rendering. Apply symbol effects such as bounce, wiggle, pulse, rotate, and draw animations (iOS 26) along with content transitions, ensuring accessibility and proper contrast throughout.

When to Use It

  • Deciding background color strategy for light/dark/system backgrounds in a new app
  • Choosing SF Symbol rendering mode to achieve depth or color accuracy
  • Animating symbols for tap feedback, loading, or transitions
  • Setting label hierarchy for title, subtitle, and detail text
  • Defending HIG-based decisions during design reviews

Quick Start

  1. Step 1: Review Apple HIG and SF Symbols docs to understand background, color, and symbol guidelines
  2. Step 2: Define your background color strategy, font weights, and SF Symbol rendering mode
  3. Step 3: Implement symbol effects and transitions in SwiftUI (e.g., .symbolEffect, .contentTransition, .drawOn/.drawOff) and test accessibility

Best Practices

  • Use semantic colors (systemBackground, label, secondaryLabel) instead of hardcoded Color.black/Color.white
  • Honor the system light/dark mode; avoid app-specific toggles that bypass user preferences
  • Choose SF Symbol rendering mode (Monochrome, Hierarchical, Palette, Multicolor) based on need for depth and color
  • Apply symbol effects judiciously (bounce for tap, pulse/wiggle for attention, replace for content swaps)
  • Ensure accessibility with proper contrast, Dynamic Type support, and meaningful accessibility labels

Example Use Cases

  • Background color decision: a media-focused app uses permanent dark appearance to preserve media fidelity
  • Label hierarchy: Text("Title").foregroundStyle(.primary) / Text("Subtitle").foregroundStyle(.secondary) / Text("Detail").foregroundStyle(.tertiary)
  • SF Symbol rendering mode selection: choosing Monochrome for simple icons vs Hierarchical for depth or Palette for per-layer colors
  • Symbol effects: .bounce on tap and .contentTransition(.symbolEffect(.replace)) for smooth transitions
  • Draw animations: hand-drawn entry/exit using Draw On / Draw Off (iOS 26+) to emphasize state changes

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers