From kiln
This skill should be used when the user asks to "audit iOS code", "review SwiftUI", "check HIG compliance", "critique SwiftUI design", "verify Human Interface Guidelines", "audit SwiftUI screen", "review iOS UI", "check Apple guidelines", "validate iOS design", "fix SwiftUI anti-patterns", "improve SwiftUI performance", "review iOS screen", "audit Swift UI", "check safe areas", "verify accessibility iOS", or mentions "Human Interface Guidelines", "HIG", "SwiftUI", "iOS UI", "SwiftUI patterns", "view identity", "property wrappers", "iOS accessibility", "WCAG iOS", "Dynamic Type". This skill should also be used when the user reports symptoms like "Why is my list slow?", "Scroll stutters", "Make this feel like a native iOS app", "Safe area problems", "Build a settings screen", "SwiftUI performance", "View keeps updating", or "Memory leak in my view". Provides comprehensive Apple HIG specifications, SwiftUI anti-patterns database, typography scale, semantic colors, safe areas, and severity-based audit checklists.
npx claudepluginhub moonlightbyte/kilnThis skill uses the workspace's default tool permissions.
Expert knowledge of Apple Human Interface Guidelines for SwiftUI applications. This skill enforces design excellence, accessibility compliance, and iOS-native feel.
Guides strict Test-Driven Development (TDD): write failing tests first for features, bugfixes, refactors before any production code. Enforces red-green-refactor cycle.
Guides systematic root cause investigation for bugs, test failures, unexpected behavior, performance issues, and build failures before proposing fixes.
Guides A/B test setup with mandatory gates for hypothesis validation, metrics definition, sample size calculation, and execution readiness checks.
Expert knowledge of Apple Human Interface Guidelines for SwiftUI applications. This skill enforces design excellence, accessibility compliance, and iOS-native feel.
accessibilityLabel on all interactive elements| Style | Size (Default) | Weight | Use Case |
|---|---|---|---|
.largeTitle | 34pt | Regular | Hero headlines |
.title | 28pt | Regular | Page titles |
.title2 | 22pt | Regular | Section headers |
.title3 | 20pt | Regular | Subsection headers |
.headline | 17pt | Semibold | Emphasized body |
.body | 17pt | Regular | Main content |
.callout | 16pt | Regular | Secondary content |
.subheadline | 15pt | Regular | Supporting text |
.footnote | 13pt | Regular | Metadata |
.caption | 12pt | Regular | Captions |
.caption2 | 11pt | Regular | Small labels |
// GOOD - System text styles
Text("Welcome")
.font(.largeTitle)
// BAD - Hardcoded sizes
Text("Welcome")
.font(.system(size: 34))
// Automatic scaling with system styles
Text("Body text")
.font(.body)
// Custom sizes that scale
@ScaledMetric(relativeTo: .body) var iconSize: CGFloat = 24
Image(systemName: "star.fill")
.frame(width: iconSize, height: iconSize)
| Token | Light | Dark | Use Case |
|---|---|---|---|
.label | Black | White | Primary text |
.secondaryLabel | Gray | Light gray | Secondary text |
.tertiaryLabel | Light gray | Darker gray | Tertiary text |
.quaternaryLabel | Lighter gray | Darkest gray | Disabled text |
.systemBackground | White | Black | Primary background |
.secondarySystemBackground | Light gray | Dark gray | Grouped content |
.tertiarySystemBackground | White | Darker gray | Elevated content |
.separator | Light gray | Dark gray | Dividers |
.link | System blue | System blue | Interactive links |
| Color | Use Case |
|---|---|
.accentColor | Primary interactive elements |
.red | Destructive actions, errors |
.orange | Warnings |
.yellow | Caution |
.green | Success, positive |
.blue | Default interactive |
.purple | Special features |
.pink | Playful accents |
// GOOD - Semantic colors
Text("Primary content")
.foregroundColor(.label)
VStack {
// content
}
.background(Color(.systemBackground))
// BAD - Hardcoded colors
Text("Primary content")
.foregroundColor(.black)
VStack {
// content
}
.background(Color.white)
// GOOD - System button provides proper touch target
Button(action: { /* action */ }) {
Image(systemName: "xmark")
}
// GOOD - Custom with proper frame
Image(systemName: "xmark")
.frame(width: 44, height: 44)
.contentShape(Rectangle())
.onTapGesture { /* action */ }
// BAD - Too small
Image(systemName: "xmark")
.font(.system(size: 16))
.onTapGesture { /* action */ }
// GOOD - Content respects safe areas
ScrollView {
content
}
.safeAreaInset(edge: .bottom) {
bottomBar
}
// Full-bleed backgrounds that extend into safe areas
ZStack {
Color.blue.ignoresSafeArea()
VStack {
content // Content still respects safe areas
}
}
| Device | Top Safe Area | Bottom Safe Area |
|---|---|---|
| iPhone (notch) | 47pt | 34pt |
| iPhone (Dynamic Island) | 59pt | 34pt |
| iPhone SE | 20pt | 0pt |
| iPad | 20pt | 0pt |
Base unit: 4pt
| Size | pt | Use Case |
|---|---|---|
| xs | 4pt | Inline spacing |
| sm | 8pt | Related elements |
| md | 12pt | Group spacing |
| lg | 16pt | Section spacing |
| xl | 20pt | Major divisions |
| 2xl | 24pt | Page margins (compact) |
| 3xl | 32pt | Large gaps |
| Context | Margin |
|---|---|
| Compact width | 16pt |
| Regular width | 20pt |
| List insets | 16pt leading, 20pt trailing |
DO: Use @StateObject for owned objects
struct ContentView: View {
@StateObject private var viewModel = ContentViewModel()
var body: some View {
Text(viewModel.text)
}
}
DON'T: Use @ObservedObject for owned objects
struct ContentView: View {
@ObservedObject var viewModel = ContentViewModel() // Re-created on every view update!
var body: some View {
Text(viewModel.text)
}
}
DO: Use LazyVStack/List for long content
ScrollView {
LazyVStack {
ForEach(items, id: \.id) { item in
ItemRow(item: item)
}
}
}
DON'T: Use VStack for many items
ScrollView {
VStack { // All items created at once
ForEach(items, id: \.id) { item in
ItemRow(item: item)
}
}
}
DO: Provide stable IDs in ForEach
ForEach(items, id: \.id) { item in
ItemRow(item: item)
}
DON'T: Use indices or miss id parameter
ForEach(items.indices, id: \.self) { index in // Breaks on reorder
ItemRow(item: items[index])
}
DO: Use @ViewBuilder or concrete types
@ViewBuilder
var content: some View {
if condition {
Text("A")
} else {
Image(systemName: "star")
}
}
DON'T: Use AnyView excessively
var content: AnyView { // Kills performance
if condition {
return AnyView(Text("A"))
} else {
return AnyView(Image(systemName: "star"))
}
}
DO: Background before padding, frame before overlay
Text("Hello")
.padding()
.background(Color.blue) // Includes padding in background
Text("World")
.frame(maxWidth: .infinity)
.overlay(alignment: .trailing) { // Overlay spans full width
Button("Edit") { }
}
DON'T: Wrong order causes unexpected results
Text("Hello")
.background(Color.blue) // Only behind text
.padding() // Padding outside background
Text("World")
.overlay(alignment: .trailing) { // Overlay only covers text width
Button("Edit") { }
}
.frame(maxWidth: .infinity)
When auditing, classify issues as:
| Level | Icon | Description | Example |
|---|---|---|---|
| Critical | ๐ด | Blocks accessibility or causes crashes | Missing accessibilityLabel |
| Important | ๐ | Violates HIG or hurts UX | Touch target < 44pt |
| Warning | ๐ก | Suboptimal but functional | Hardcoded color |
| Suggestion | ๐ต | Polish opportunities | Non-standard spacing |