Agents design-to-swiftui
Convert design specifications from Figma, Sketch, or Penpot to SwiftUI view code with proper modifiers and theme integration
git clone https://github.com/aRustyDev/agents
T=$(mktemp -d) && git clone --depth=1 https://github.com/aRustyDev/agents "$T" && mkdir -p ~/.claude/skills && cp -r "$T/content/plugins/frontend/design-to-code/skills/design-to-swiftui" ~/.claude/skills/arustydev-agents-design-to-swiftui && rm -rf "$T"
content/plugins/frontend/design-to-code/skills/design-to-swiftui/SKILL.mdDesign to SwiftUI
Convert design component specifications to SwiftUI view code.
Overview
This skill transforms design specifications (from Figma, Sketch, or Penpot) into idiomatic SwiftUI views with proper modifiers, layout containers, and theme token integration.
This skill covers:
- Converting design frames to SwiftUI layouts (VStack, HStack, ZStack)
- Mapping design properties to SwiftUI modifiers
- Generating reusable SwiftUI components
- Integrating with extracted design tokens
This skill does NOT cover:
- Design token extraction (see
skill)design-tokens-extraction - State management and data binding
- Navigation and routing
Quick Reference
Design to SwiftUI Mapping
| Design Element | SwiftUI Component |
|---|---|
| Horizontal layout | |
| Vertical layout | |
| Overlapping layers | |
| Auto-layout | with |
| Grid | / |
| Scroll container | |
| Text | with modifiers |
| Rectangle | or |
| Circle | |
| Image | / |
| Button | with custom label |
Property Mapping
| Design Property | SwiftUI Modifier |
|---|---|
| Fill color | / |
| Stroke | / |
| Corner radius | |
| Shadow | |
| Opacity | |
| Padding | |
| Size | |
| Position | / |
Workflow: Convert Design Component
Step 1: Analyze Design Structure
Read the design component from MCP and identify:
- Layout type (horizontal, vertical, grid, free-form)
- Child elements and their order
- Spacing and padding values
- Constraints and sizing behavior
Step 2: Map to SwiftUI Layout
Auto-layout (horizontal):
HStack(alignment: .<alignment>, spacing: <gap>) { // children }
Auto-layout (vertical):
VStack(alignment: .<alignment>, spacing: <gap>) { // children }
Grid layout:
let columns = [ GridItem(.flexible()), GridItem(.flexible()) ] LazyVGrid(columns: columns, spacing: <gap>) { // children }
Absolute positioning (rare in SwiftUI):
ZStack { // position children with .offset() or .position() }
Step 3: Generate View Code
For each design element, generate SwiftUI code:
Text elements:
Text("<content>") .font(<Theme.Typography.style>) .foregroundStyle(<Theme.Colors.color>) .lineLimit(<lines>) .multilineTextAlignment(.<alignment>)
Shapes with fill:
RoundedRectangle(cornerRadius: Theme.Radius.<size>) .fill(Theme.Colors.<color>) .frame(width: <width>, height: <height>) .shadow(color: .black.opacity(0.1), radius: 4, y: 2)
Images:
// Local image Image("<assetName>") .resizable() .aspectRatio(contentMode: .fill) .frame(width: <width>, height: <height>) .clipShape(RoundedRectangle(cornerRadius: Theme.Radius.<size>)) // Remote image AsyncImage(url: URL(string: "<url>")) { image in image .resizable() .aspectRatio(contentMode: .fill) } placeholder: { ProgressView() } .frame(width: <width>, height: <height>)
Buttons:
Button { // action } label: { HStack(spacing: 8) { Image(systemName: "<icon>") Text("<label>") } .font(Theme.Typography.<style>) .foregroundStyle(Theme.Colors.<textColor>) .padding(.horizontal, Theme.Spacing.<h>) .padding(.vertical, Theme.Spacing.<v>) .background(Theme.Colors.<bgColor>) .clipShape(RoundedRectangle(cornerRadius: Theme.Radius.<size>)) }
Step 4: Create Component View
Wrap the generated code in a reusable SwiftUI View:
import SwiftUI struct <ComponentName>View: View { // Properties extracted from design variants let title: String let subtitle: String? var onTap: (() -> Void)? var body: some View { VStack(alignment: .leading, spacing: Theme.Spacing.sm) { Text(title) .font(Theme.Typography.headlineMedium) .foregroundStyle(Theme.Colors.text) if let subtitle { Text(subtitle) .font(Theme.Typography.bodyMedium) .foregroundStyle(Theme.Colors.textSecondary) } } .padding(Theme.Spacing.md) .background(Theme.Colors.surface) .clipShape(RoundedRectangle(cornerRadius: Theme.Radius.md)) .onTapGesture { onTap?() } } } #Preview { <ComponentName>View( title: "Preview Title", subtitle: "Preview subtitle text" ) .padding() }
Common Patterns
Card Component
Design spec:
- Background fill with rounded corners
- Shadow elevation
- Padding around content
- Optional image header
struct CardView<Content: View>: View { let content: Content init(@ViewBuilder content: () -> Content) { self.content = content() } var body: some View { VStack(alignment: .leading, spacing: 0) { content } .background(Theme.Colors.surface) .clipShape(RoundedRectangle(cornerRadius: Theme.Radius.lg)) .shadow( color: .black.opacity(0.08), radius: 8, y: 4 ) } }
Button Variants
Design spec defines Primary, Secondary, Ghost variants:
enum ButtonVariant { case primary, secondary, ghost var backgroundColor: Color { switch self { case .primary: Theme.Colors.primary case .secondary: Theme.Colors.surface case .ghost: .clear } } var foregroundColor: Color { switch self { case .primary: Theme.Colors.onPrimary case .secondary: Theme.Colors.primary case .ghost: Theme.Colors.primary } } } struct ThemedButton: View { let title: String let variant: ButtonVariant let action: () -> Void var body: some View { Button(action: action) { Text(title) .font(Theme.Typography.labelLarge) .foregroundStyle(variant.foregroundColor) .padding(.horizontal, Theme.Spacing.lg) .padding(.vertical, Theme.Spacing.md) .background(variant.backgroundColor) .clipShape(RoundedRectangle(cornerRadius: Theme.Radius.md)) } } }
List Item
Design spec with leading icon, title/subtitle, trailing accessory:
struct ListItemView: View { let icon: String let title: String let subtitle: String? let accessory: Accessory enum Accessory { case chevron case toggle(Binding<Bool>) case badge(String) case none } var body: some View { HStack(spacing: Theme.Spacing.md) { Image(systemName: icon) .font(.system(size: 20)) .foregroundStyle(Theme.Colors.primary) .frame(width: 24) VStack(alignment: .leading, spacing: 2) { Text(title) .font(Theme.Typography.bodyLarge) .foregroundStyle(Theme.Colors.text) if let subtitle { Text(subtitle) .font(Theme.Typography.bodySmall) .foregroundStyle(Theme.Colors.textSecondary) } } Spacer() accessoryView } .padding(.vertical, Theme.Spacing.sm) .padding(.horizontal, Theme.Spacing.md) } @ViewBuilder private var accessoryView: some View { switch accessory { case .chevron: Image(systemName: "chevron.right") .foregroundStyle(Theme.Colors.textSecondary) case .toggle(let isOn): Toggle("", isOn: isOn) .labelsHidden() case .badge(let text): Text(text) .font(Theme.Typography.labelSmall) .padding(.horizontal, 8) .padding(.vertical, 4) .background(Theme.Colors.primary) .foregroundStyle(Theme.Colors.onPrimary) .clipShape(Capsule()) case .none: EmptyView() } } }
Token Integration
Reference design tokens through the Theme struct:
// Colors Theme.Colors.primary Theme.Colors.surface Theme.Colors.text Theme.Colors.textSecondary // Typography Theme.Typography.displayLarge Theme.Typography.headlineMedium Theme.Typography.bodyLarge Theme.Typography.labelSmall // Spacing Theme.Spacing.xs // 4 Theme.Spacing.sm // 8 Theme.Spacing.md // 16 Theme.Spacing.lg // 24 Theme.Spacing.xl // 32 // Corner Radius Theme.Radius.sm // 4 Theme.Radius.md // 8 Theme.Radius.lg // 16 Theme.Radius.full // 9999
Naming Conventions
| Design Name | SwiftUI Name |
|---|---|
| |
| |
| |
| |
| |
See Also
skill - Extract tokens firstdesign-tokens-extraction
style - Swift code output formatdesign-token-swift
skill - SwiftUI component patternsswiftui-components