ahmadbrkt/swiftui-style-driven-components-skill
Overview
This skill provides expert guidance for building extensible SwiftUI components using a style-driven architecture that follows Apple patterns like ButtonStyle and LabelStyle. It helps you decide when to create style protocols, how to structure configuration objects, and how to wire environment-based styling for reusable component libraries. The guidance focuses on maintainability, type safety, and predictable extension points.
How this skill works
The skill inspects component intent and determines whether a style protocol is warranted (requires two or more meaningful visual variants). It prescribes a architecture: a DynamicProperty style protocol, an internal configuration with nested type-erased views, an environment-stored existential style, and components that delegate rendering to style.makeBody wrapped in AnyView. It includes checklists, file structure recommendations, and a triage playbook for creating, adding, and reviewing styles.
When to use it
- Building a reusable component library that needs multiple visual variants
- Creating components that should inherit styles via the SwiftUI environment
- Implementing components that must support pluggable, parameterized styles
- Reviewing component architecture for extensibility and Apple-style API conformance
- Adding new visual styles without changing component implementation
Best practices
- Only add a style protocol when there are 2+ meaningful styles
- Make style protocols conform to DynamicProperty and use @ViewBuilder @MainActor for makeBody
- Keep configuration initializers internal and use nested type-erased views for content
- Wrap style.makeBody(configuration:) in AnyView inside the component body for consistent type erasure
- Expose styles via an environment key stored as an existential (any MyComponentStyle) and provide a view modifier taking some MyComponentStyle
- Use design tokens and snapshot tests; include previews for every style variant
Example use cases
- A button family with .primary, .secondary, and .iconOnly styles that can be swapped at runtime
- A labeled control where layout differs greatly across platforms and needs environment-driven defaults
- A card component library where consumers register brand-specific styles via the environment
- Adding an outlined variant to an existing component without touching its source code
- Reviewing a component to ensure configuration init is internal and styles don’t leak implementation details
FAQ
Skip the protocol for single-variant or one-off internal components where no meaningful style variations are required.
Why wrap makeBody in AnyView?
Wrapping ensures type erasure across different styles so component bodies remain a stable opaque type and new styles don’t force component changes.