From apple-dev
Use when the user says 'validate tokens', 'check design tokens', or after modifying design-related code. Validates SwiftUI code compliance with Design Token standards for spacing, colors, fonts, and corner radius.
npx claudepluginhub n0rvyn/indie-toolkit --plugin apple-devThis skill uses the workspace's default tool permissions.
Check SwiftUI code for Design Token compliance.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Designs, implements, and audits WCAG 2.2 AA accessible UIs for Web (ARIA/HTML5), iOS (SwiftUI traits), and Android (Compose semantics). Audits code for compliance gaps.
Check SwiftUI code for Design Token compliance.
filePaths: List of View files to checkPre-check: Read deployment target
Grep("IPHONEOS_DEPLOYMENT_TARGET", "*.xcodeproj/project.pbxproj", output_mode="content")
ℹ️ Deprecated API check skipped — deployment target {X} < iOS 18Search for .cornerRadius(:
.clipShape(.rect(cornerRadius: N)) or RoundedRectangle(cornerRadius: N)Search for .foregroundColor(:
.foregroundStyle()Search for NavigationView {:
NavigationStack / NavigationSplitView)Search for:
.padding(N) where N is not on the AppSpacing scale (2/4/8/12/16/24/32/48/64).spacing(N) where N is not on the AppSpacing scaleValid patterns:
.padding(AppSpacing.md).padding(16) // matches AppSpacing.sm.spacing(AppSpacing.xs).padding(.horizontal, AppLayout.marginCompact)Invalid patterns:
.padding(15) // Not on scale.padding(10) // Not on scaleSearch for:
.padding(.horizontal, 16) and .padding(.horizontal, 20) in files whose View struct name ends with View, Screen, or PageFlag ALL occurrences with: "If this is a page-level margin, use AppLayout.marginCompact / AppLayout.marginRegular instead."
Valid patterns:
.padding(.horizontal, AppLayout.marginCompact).padding(.horizontal, AppLayout.marginRegular).frame(maxWidth: AppLayout.maxContentWidth)Invalid patterns (🟡 advisory):
.padding(.horizontal, 16) → If page margin, suggest AppLayout.marginCompact.padding(.horizontal, 20) → If page margin, suggest AppLayout.marginRegularHeuristic: Check if VStack/HStack spacing values match the hierarchy level of their children.
| Children type | Expected spacing range | Suggested tokens |
|---|---|---|
| Text, Image, Label (leaf elements) | 4–12pt | AppSpacing._3xs ~ .xs |
| Card, Section, Group (containers) | 24–48pt | AppSpacing.md ~ .xl |
Search for:
Color(hex:)Color(red:green:blue:)Color.blue, Color.red (should use semantic)Valid patterns:
Color.appPrimaryColor.appBackgroundColor.primary, Color.secondaryInvalid patterns:
Color(hex: "#FF0000")Color.blue → ⚠️ Context-dependent
Color.appPrimarySearch for:
.font(.system(size:))Valid patterns:
.font(.body).font(.headline).font(.largeTitle)Invalid patterns:
.font(.system(size: 17)) // Should use .font(.body)Search for:
cornerRadius: values in .clipShape(.rect(cornerRadius:)) or RoundedRectangle(cornerRadius:) where N is hardcodedValid patterns:
.clipShape(.rect(cornerRadius: AppCornerRadius.medium))RoundedRectangle(cornerRadius: AppCornerRadius.medium).clipShape(.rect(cornerRadius: 12)) // If matches AppCornerRadius.mediumInvalid patterns:
.clipShape(.rect(cornerRadius: 15)) // Not in standard setSearch for:
.shadow(color:radius:x:y:) with opacity > 0.08Valid patterns:
.appShadow(.subtle).shadow(color: .black.opacity(0.04), ...)Invalid patterns:
.shadow(color: .black.opacity(0.5), ...) // Too heavyPurpose: Detect same-type components with inconsistent sizing/frame behavior.
Process:
filePaths, extract the View struct nameGrep("struct \\w+{suffix}", glob: "*.swift")
| Attribute | Search Pattern | Consistency Rule |
|---|---|---|
| Width behavior | .frame(maxWidth: / .frame(width: / no frame | All same-type must use identical width strategy |
| Padding | .padding( | Same directions, same values or same token |
| Background | .background( | Same color/material type |
| Corner radius | .clipShape(.rect(cornerRadius: / RoundedRectangle(cornerRadius: | Same value or same token |
| Shadow | .shadow( | Same parameters |
Valid patterns (consistent):
// InsightCard — full width
struct InsightCard: View {
var body: some View {
VStack { ... }
.padding(AppSpacing.sm)
.frame(maxWidth: .infinity)
.background(.background.secondary)
.clipShape(.rect(cornerRadius: AppCornerRadius.medium))
}
// ExpenseCard — matches InsightCard
struct ExpenseCard: View {
var body: some View {
VStack { ... }
.padding(AppSpacing.sm)
.frame(maxWidth: .infinity)
.background(.background.secondary)
.clipShape(.rect(cornerRadius: AppCornerRadius.medium))
}
Invalid patterns (inconsistent):
// InsightCard — full width
struct InsightCard: View {
var body: some View {
VStack { ... }
.padding(16)
.frame(maxWidth: .infinity) // expanding
}
// ExpenseCard — content hugging (MISMATCH)
struct ExpenseCard: View {
var body: some View {
VStack { ... }
.padding(12) // different padding
// no .frame(maxWidth:) // hugging vs expanding
}
🔴 Must Fix:
- HomeView.swift:42 - Hardcoded spacing .padding(15)
Suggestion: Use .padding(AppSpacing.sm)
- CardView.swift:18 - Hardcoded color Color(hex: "#FF0000")
Suggestion: Define as semantic color Color.appError
🟡 Consider:
- ProfileView.swift:67 - System color Color.blue
Suggestion: If brand color, use Color.appPrimary
✅ Compliant:
- SettingsView.swift - All spacing uses Design Token
- DashboardView.swift - All colors use semantic tokens