swiftui-style-driven-components
SKILL.md
SwiftUI Style-Driven Components
Overview
This skill provides expert guidance on building, reviewing, and extending SwiftUI components following the style-driven architecture pattern. This pattern emphasizes extensibility, maintainability, and Apple-style API design, making it ideal for building reusable component libraries.
Agent Behavior Contract
- Before creating a new component, verify it needs 2+ meaningfully different styles. If not, skip the style protocol.
- Follow Apple's patterns (
ButtonStyle,LabelStyle) as the gold standard. - Use nested type-erased views in configuration (like
LabelStyleConfiguration.Title). - Never expose configuration initializers as
public. - Wrap
style.makeBodyinAnyViewfor type erasure. - Use
DynamicPropertyfor style protocols, notSendable. - Read styles from environment using existential type (
any [Component]Style).
Core Architecture Principles
- Protocol:
[Component]Style— defines HOW to render - Configuration:
[Component]StyleConfiguration— contains WHAT to render - Component: Creates configuration internally, delegates rendering to style
- Environment: Enables subtree styling without modifying components
When to Use Style Protocol
Use style protocol when:
- 2+ meaningfully different visual styles needed
- Environment-based style cascading desired
- Building reusable component library
- Styles need environment access (
@Environment)
Skip style protocol when:
- Single visual representation
- Simple, static appearance
- One-off internal component
- No style customization needed
Quick Decision Tree
Building a new component?
- Needs multiple styles? → If no, skip style protocol
- Create structure → See
references/component-structure.md - Define protocol → See
references/style-protocol.md - Create configuration → Nested type-erased views pattern
- Implement default style → Basic rendering
- Add environment key → See
references/environment-keys.md - Add convenience accessors → See
references/common-patterns.md
Adding a new style?
- Create struct conforming to
[Component]Style - Implement
makeBody(configuration:) - Add convenience accessor (
.myStyle) - No component changes needed!
Reviewing component architecture?
- Check protocol uses
DynamicProperty,@ViewBuilder @MainActor - Verify configuration uses nested type-erased views
- Confirm configuration init is
internal - Check component wraps
style.makeBodyinAnyView - Validate environment key uses existential (
any [Component]Style) - Ensure new styles don't require component changes
Triage Playbook
- Creating component file structure →
references/component-structure.md - Defining style protocol →
references/style-protocol.md - Creating configuration with content →
references/style-protocol.md - Setting up environment keys →
references/environment-keys.md - Adding convenience initializers →
references/common-patterns.md - Parameterized or adaptive styles →
references/common-patterns.md - Documenting public APIs →
references/documentation.md - Using design tokens →
references/design-system.md - Writing tests →
references/testing.md - Organizing previews →
references/previews.md - Accessibility requirements →
references/accessibility.md - Advanced: state machines, dual protocols, result builders →
references/advanced-patterns.md
Quick Reference
Component Structure
[Component]/
├── EnvironmentKeys/[Component]StyleKey.swift
├── Styles/
│ ├── [Component]Style.swift
│ ├── [Component]StyleConfiguration.swift
│ └── Default[Component]Style.swift
└── [Component].swift
Pattern Summary
- Style Protocol:
DynamicProperty,@ViewBuilder @MainActor func makeBody - Configuration: Nested
struct Content: Viewwithprivate let _body: () -> AnyView - Configuration Init:
internal(notpublic),@MainActor - Component Body:
AnyView(style.makeBody(configuration: .init(...))) - Environment Key:
@Entry public var style: any [Component]Style = .automatic - View Modifier:
func style(_ style: some [Component]Style) -> some View
Best Practices
DO
- Use
DynamicPropertyfor style protocols - Use nested type-erased views in configuration
- Make configuration initializers
internal - Wrap
style.makeBodyinAnyView - Use design tokens, not magic numbers
- Provide convenience accessors (
.compact,.outlined)
DON'T
- Add explicit
Sendableto style protocols - Make configuration initializers
public - Pass configuration to component initializer
- Put content properties in style protocol
- Use style protocol for single-variant components
- Use
AnyViewdirectly as configuration property type - Use
foregroundColor()(deprecated — useforegroundStyle()) - Use
.cornerRadius()(deprecated — useclipShape())
Review Checklist
Architecture
- Style protocol uses
DynamicProperty -
makeBodyhas@ViewBuilder @MainActor - Configuration uses nested type-erased views
- Configuration init is
internal - Component wraps
style.makeBodyinAnyView - New styles don't require component changes
Environment
- Uses
@Entrymacro - Stores as existential (
any [Component]Style) - Modifier uses opaque parameter (
some [Component]Style) - Default value provided (
.automatic)
Quality
- All public symbols have
///doc comments - Component doc comment includes usage example
- Previews cover all styles (see
references/previews.md) - Snapshot tests exist (see
references/testing.md) - Accessibility supported (see
references/accessibility.md) - Design tokens used (see
references/design-system.md)
Reference Files
references/component-structure.md— File organization, MARK conventionsreferences/documentation.md— Doc comments following Apple conventionsreferences/style-protocol.md— Protocol, configuration, anti-patternsreferences/environment-keys.md— Environment injection, usage patternsreferences/common-patterns.md— Convenience inits, parameterized styles, bindings, rolesreferences/advanced-patterns.md— State machines, dual protocols, result buildersreferences/testing.md— Snapshots, config tests, test helpersreferences/previews.md— Preview structure, stateful helpers, snapshot mappingreferences/design-system.md— Design tokens, typography, colorsreferences/accessibility.md— Labels, identifiers, Dynamic Type
Philosophy
Style-driven components prioritize:
- Extensibility — Add styles without modifying components
- Apple Patterns — Follow
ButtonStyle,LabelStyleconventions - Simplicity — Skip style protocol for single-variant components
- Type Safety — Generic APIs with internal type erasure
- Consistency — All components follow same patterns
Weekly Installs
21
Repository
ahmadbrkt/swift…ts-skillGitHub Stars
3
First Seen
Jan 27, 2026
Security Audits
Installed on
cursor21
opencode18
gemini-cli18
github-copilot18
codex18
cline16