Stitch-kit stitch-swiftui-components
Converts Stitch mobile designs into SwiftUI views for native iOS apps — VStack/HStack/ZStack layout mapping, Color asset tokens with dark mode, NavigationStack/TabView routing, and Xcode project structure.
git clone https://github.com/gabelul/stitch-kit
T=$(mktemp -d) && git clone --depth=1 https://github.com/gabelul/stitch-kit "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/stitch-swiftui-components" ~/.claude/skills/gabelul-stitch-kit-stitch-swiftui-components && rm -rf "$T"
skills/stitch-swiftui-components/SKILL.mdStitch → SwiftUI (Native iOS)
You are a Swift/SwiftUI engineer. You convert Stitch mobile designs (deviceType: MOBILE) into native iOS SwiftUI views —
.swift files that build and run in Xcode. You follow Apple's Human Interface Guidelines and produce code that feels like it belongs on iOS.
When to use this skill
Use this skill when:
- The user wants native iOS output from a Stitch design
- The user mentions "SwiftUI", "Xcode", "iOS", "native iOS app"
- The design was generated with
deviceType: MOBILE
Note: This skill targets iOS 16+ with SwiftUI. For cross-platform (iOS + Android), use
stitch-react-native-components instead.
Prerequisites
- Stitch design with
deviceType: MOBILE - Xcode 15+ on macOS
- Swift 5.9+
Step 1: Retrieve the design
→ find Stitch MCP prefixlist_tools
→ fetch metadata[prefix]:get_screen- Download HTML:
bash scripts/fetch-stitch.sh "[htmlCode.downloadUrl]" "temp/source.html" - Check
— confirm mobile layout before convertingscreenshot.downloadUrl
Only convert MOBILE designs. Desktop Stitch designs don't map well to SwiftUI without significant layout rethinking.
Step 2: Xcode project structure
MyApp/ ├── MyApp.swift ← @main entry point ├── ContentView.swift ← Root view (TabView or NavigationStack) ├── Theme/ │ ├── ThemeTokens.swift ← Design token constants │ └── Color+App.swift ← Color extension with semantic names ├── Views/ │ ├── [ScreenName]View.swift ← One file per Stitch screen │ └── Components/ │ └── [Name]View.swift ← Reusable component views ├── Models/ │ └── MockData.swift ← Static preview data └── Assets.xcassets/ └── Colors/ ← Color assets for light/dark mode
Step 3: The HTML/CSS → SwiftUI layout mapping
This is the core translation. Apply these rules to every element in the Stitch HTML:
Layout containers
| HTML/CSS pattern | → SwiftUI |
|---|---|
| |
| |
| pattern |
overlay | with layered views |
(2-column) | |
| |
| Repeated list of items | or inside |
bottom nav | (preferred) or explicit with |
Spacing mapping
SwiftUI uses points (1pt ≈ 1dp on non-retina, 2px on Retina @2x):
// Spacing from Tailwind → SwiftUI points // p-1(4px)→4 p-2(8px)→8 p-3(12px)→12 p-4(16px)→16 // p-6(24px)→24 p-8(32px)→32 p-12(48px)→48 p-16(64px)→64
Geometry mapping
// Tailwind rounded- → SwiftUI cornerRadius // rounded-sm → .cornerRadius(4) // rounded-md → .cornerRadius(8) // rounded-lg → .cornerRadius(12) // rounded-xl → .cornerRadius(16) // rounded-full → .clipShape(Capsule()) or .cornerRadius(9999)
Content elements
| HTML | → SwiftUI |
|---|---|
, , text | |
| |
| |
| |
body | |
/ caption | |
| for remote, for asset |
primary | |
secondary | |
ghost/text | |
| |
| |
| |
/ checkbox | |
| Icon-only button | |
Navigation patterns
| Pattern | SwiftUI implementation |
|---|---|
| Bottom tab bar | with |
| Stack navigation | |
| Modal / sheet | |
| Full screen modal | |
| Back navigation | Automatic with |
| Action sheet | |
Step 4: Design tokens in SwiftUI
Color extension (semantic tokens)
// Theme/Color+App.swift import SwiftUI extension Color { // Extract these hex values from the Stitch HTML's tailwind.config // Backgrounds static let appBackground = Color("AppBackground") // Asset catalog static let appSurface = Color("AppSurface") // Brand static let appPrimary = Color("AppPrimary") static let appPrimaryFg = Color("AppPrimaryForeground") // Text static let appText = Color("AppText") static let appTextMuted = Color("AppTextMuted") // Borders static let appBorder = Color("AppBorder") }
Color asset catalog (light + dark)
Create named Color Sets in
Assets.xcassets/Colors/:
For each color (e.g.,
AppPrimary):
- Any Appearance:
(the light mode value)#6366F1 - Dark Appearance:
(lighter shade for dark bg)#818CF8
Alternatively, define programmatically (no asset catalog needed):
// Theme/ThemeTokens.swift import SwiftUI struct ThemeTokens { let background: Color let surface: Color let primary: Color let primaryFg: Color let text: Color let textMuted: Color let border: Color static let light = ThemeTokens( background: Color(hex: "#FFFFFF"), surface: Color(hex: "#F4F4F5"), primary: Color(hex: "#6366F1"), primaryFg: Color(hex: "#FFFFFF"), text: Color(hex: "#09090B"), textMuted: Color(hex: "#71717A"), border: Color(hex: "#E4E4E7") ) static let dark = ThemeTokens( background: Color(hex: "#09090B"), surface: Color(hex: "#18181B"), primary: Color(hex: "#818CF8"), // Lightened for dark bg primaryFg: Color(hex: "#09090B"), text: Color(hex: "#FAFAFA"), textMuted: Color(hex: "#A1A1AA"), border: Color(hex: "#27272A") ) } // Convenience: Color from hex string extension Color { init(hex: String) { let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) var int: UInt64 = 0 Scanner(string: hex).scanHexInt64(&int) let r = Double((int & 0xFF0000) >> 16) / 255 let g = Double((int & 0x00FF00) >> 8) / 255 let b = Double(int & 0x0000FF) / 255 self.init(red: r, green: g, blue: b) } }
Environment-based theme access
// Anywhere in a view — automatic dark mode @Environment(\.colorScheme) var colorScheme var theme: ThemeTokens { colorScheme == .dark ? .dark : .light } // Usage Text("Hello") .foregroundStyle(theme.text) .background(theme.surface)
Step 5: Component template
// Views/Components/StitchComponentView.swift import SwiftUI /// StitchComponent — [describe purpose in one sentence] struct StitchComponentView: View { // MARK: - Properties (equivalent to props) let title: String var description: String = "" var onAction: (() -> Void)? = nil // MARK: - Environment @Environment(\.colorScheme) private var colorScheme private var theme: ThemeTokens { colorScheme == .dark ? .dark : .light } // MARK: - State @State private var isPressed = false // MARK: - Body var body: some View { VStack(alignment: .leading, spacing: 8) { Text(title) .font(.headline) .foregroundStyle(theme.text) if !description.isEmpty { Text(description) .font(.subheadline) .foregroundStyle(theme.textMuted) } if let action = onAction { Button("Action", action: action) .buttonStyle(.borderedProminent) .tint(theme.primary) } } .padding(16) .frame(maxWidth: .infinity, alignment: .leading) .background(theme.surface) .clipShape(RoundedRectangle(cornerRadius: 12)) .overlay( RoundedRectangle(cornerRadius: 12) .stroke(theme.border, lineWidth: 1) ) // Minimum touch target — 44pt Apple HIG requirement .frame(minHeight: 44) } } // MARK: - Preview #Preview { VStack(spacing: 16) { StitchComponentView(title: "Card Title", description: "Supporting text") StitchComponentView(title: "With Action", description: "Tap the button", onAction: {}) } .padding() }
Step 6: Main app entry point
// MyApp.swift import SwiftUI @main struct MyApp: App { var body: some Scene { WindowGroup { ContentView() } } } // ContentView.swift — root with TabView struct ContentView: View { var body: some View { TabView { HomeView() .tabItem { Label("Home", systemImage: "house") } ProfileView() .tabItem { Label("Profile", systemImage: "person") } } } }
Step 7: Accessibility in SwiftUI
SwiftUI handles much of this automatically, but always verify:
// Image accessibility Image("hero-photo") .accessibilityLabel("Team collaborating in a modern office") // Decorative images (screen reader skips) Image(decorative: "background-pattern") // Buttons — label is automatic if using Text inside Button("Sign In") { ... } // VoiceOver reads "Sign In, button" // Custom accessibility label when button label is ambiguous Button { deleteItem() } label: { Image(systemName: "trash") } .accessibilityLabel("Delete item") // Group elements (treats as single unit) VStack { Text("Sarah Johnson") Text("Product Designer") } .accessibilityElement(children: .combine) // Dynamic type support — always use semantic fonts Text("Headline") .font(.headline) // ✅ Scales with user's text size // NOT .font(.system(size: 17, weight: .semibold)) // ❌ Fixed size
Step 8: SwiftUI animations
SwiftUI has excellent built-in animations — use them for the micro-interactions:
// Button press spring Button(action: primaryAction) { Text("Get Started") .padding(.horizontal, 24) .padding(.vertical, 14) .background(theme.primary) .foregroundStyle(theme.primaryFg) .clipShape(Capsule()) .scaleEffect(isPressed ? 0.96 : 1.0) .animation(.spring(response: 0.2, dampingFraction: 0.6), value: isPressed) } .simultaneousGesture( DragGesture(minimumDistance: 0) .onChanged { _ in isPressed = true } .onEnded { _ in isPressed = false } ) // Card appear transition VStack { /* card content */ } .transition(.move(edge: .bottom).combined(with: .opacity)) // Respect reduced motion @Environment(\.accessibilityReduceMotion) var reduceMotion var animation: Animation { reduceMotion ? .none : .spring(response: 0.3, dampingFraction: 0.7) }
Execution steps
- Verify the Stitch design uses
deviceType: MOBILE - Create Xcode project — File → New → App, SwiftUI interface, Swift language
- Data layer — create
from static content in the designModels/MockData.swift - Theme — create
with extracted hex values, andTheme/ThemeTokens.swiftColor+App.swift - Components — convert the Stitch HTML sections to SwiftUI views, file by file
- Navigation — wire up
(tab bar) orTabView
(stack)NavigationStack - Build and run — in Xcode, Cmd+R. Test on both light and dark mode (⌃⌘A toggles appearance in Simulator)
Troubleshooting
| Issue | Fix |
|---|---|
| View overflows screen | Add + parent |
| Text truncates unexpectedly | Add or |
| Color looks wrong in dark mode | Ensure the Color Set in Assets.xcassets has a Dark appearance set |
| Image not loading | For , check URL is valid. For local images, file must be in Assets.xcassets |
| TabView items don't show label | Content must be directly inside — no wrapping views |
| Sheet not dismissible | Add and call in the sheet |
| Preview crashes | Check has valid mock data — never optional-unwrap without fallback |
References
— Boilerplate SwiftUI viewresources/component-template.swift
— Full HTML/CSS → SwiftUI referenceresources/layout-mapping.md
— Pre-ship checklistresources/architecture-checklist.md
— Reliable GCS HTML downloaderscripts/fetch-stitch.sh