# Swiftui Craft

> Production SwiftUI craft and native iOS/HIG compliance — make an app feel native and match Apple Human Interface Guidelines: spacing grid, typography and Dynamic Type, semantic colors and dark mode, safe areas and Dynamic Island, tab bars/sheets/modals, haptics, iOS accessibility (VoiceOver), SF Symbols, WidgetKit visual rules, and iPhone/iPad layout adaptation. Use when reviewing or improving Sw…

- **Type:** Skill
- **Install:** `agentstack add skill-dndungu-agent-skills-swiftui-craft`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [dndungu](https://agentstack.voostack.com/s/dndungu)
- **Installs:** 0
- **Category:** [Search](https://agentstack.voostack.com/c/search)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [dndungu](https://github.com/dndungu)
- **Source:** https://github.com/dndungu/agent-skills/tree/main/swiftui-craft

## Install

```sh
agentstack add skill-dndungu-agent-skills-swiftui-craft
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# swiftui-craft — production SwiftUI craft rules

Consolidates the retired standalone skills `swiftui-design-principles`
(v1.1.1, arjitj2, MIT), `swiftui-design-skill` (v1.0.0, wholiver, MIT), and
`ios-hig-design` (v1.5.0, wondelai, MIT). Load when REVIEWING existing SwiftUI
UI quality, when an approved design system is being implemented in a real app,
or when auditing native-idiom / Apple HIG compliance. These are
implementation-time rules: what separates an app that feels "right" and native
from one where margins, spacing, text sizes, or platform conventions look
"off". The HIG native-idiom framework (safe areas, Dynamic Type, Dark Mode,
gestures/haptics, accessibility, SF Symbols, the 10/10 scoring rubric) lives in
`references/hig-overview.md`; the topic router below points into it.

**Does not do:** greenfield design exploration, mockups, or design systems —
that is /ios-design (direction → tokens → component cards → rendered
screens). Widget plumbing (timelines, intents, entitlements, push reloads) →
/widgetkit. Liquid Glass API work → /liquid-glass. Design-to-production
validation pipelines → /design-fidelity.

**Reconciliation with the mockup lane.** Two rules below (semantic system
colors, NavigationStack) are correct in a real app but wrong in the headless
mockup lane (RENDERER.md fidelity limits). Resolution: mockups use `Theme`
tokens and custom chrome; at implementation time each `Theme` token becomes
an asset-catalog color (light+dark variants) or maps onto the closest
semantic color, and custom mockup chrome becomes real `NavigationStack` /
`TabView` chrome styled by the system. Apps WITHOUT a design system default
to semantic colors outright.

Core philosophy: **restraint over decoration** — fewer colors, fewer font
sizes, fewer spacing values, fewer words, used consistently. **Attention is
scarce** — one clear headline and one compact supporting block beat repeated
explanation scattered across title, subtitle, body, and footer.

## 1. Spacing: one grid, no arbitrary values

Allowed: `4 8 12 16 20 24 32 40 48`. Values like 14, 18, 26, 34 create
visual dissonance the eye registers even when the reader can't name it.

- Outer content padding: 16–20pt horizontal.
- Between major sections: 24–32pt vertical.
- Within grouped components: 4–12pt.
- Card/row internal padding: 12–16pt vertical, 16pt horizontal (never 4pt vertical).

## 2. Typography: hierarchy through weight, not just size

- ≤ 5 distinct font sizes per app, each with a named purpose. Lighter
  weights at large sizes (elegant), regular/medium at small sizes.
- Example scale for a data app: hero 36–42 `.light`, secondary stat 20–24
  `.light`, body 15 `.regular`, section header 11 `.medium` uppercase with
  tracking, caption 11–13 `.regular`.
- ONE font design everywhere — app AND widgets. Never `.monospaced` in the
  app but `.rounded` on the lock screen.
- Tracking: at most 2 values, only on uppercase labels (e.g. 1.5 for section
  labels, 3 for toolbar titles). Never 3+ near-identical kerning values.
- Identifiers (years, IDs) must not locale-group: `Text(String(year))` or
  `.number.grouping(.never)` — never `Text("\(year)")` rendering "2,026".
- No `minimumScaleFactor` hacks — fix the layout instead.
- At most 2 weights per family per screen; one hierarchy channel — size OR
  weight — per screen. Any runtime-updating number uses monospaced/tabular
  digits (no reflow jitter). Identity test: the app should be recognizable
  in a desaturated screenshot — typography carries the brand.
- Content text (body, labels, captions) uses semantic styles — `.font(.body)`,
  `.title`, `.caption` — so Dynamic Type scales it; reserve the custom scale
  above for hero/display/stat text only. Test every layout at the largest
  Dynamic Type size (`references/hig-typography.md` for the color mapping).

## 3. Color: semantic first, tokens when a system exists

- No design system → semantic colors: `Color(.systemBackground)`,
  `.secondarySystemBackground`, `.separator`, `.primary`, `.secondary`,
  `.tertiary`. They handle light/dark and accessibility for free.
- Design system → `Theme` tokens with explicit light+dark values (the
  mockup lane already enforces this); wire them through the asset catalog.
- Never a ladder of hardcoded `Color.white.opacity(0.08 … 0.94)` values.
  If you need opacity at all, limit to 2–3 values with clear purposes
  (≈0.15 subtle strokes, ≈0.3 separators).
- Default blue tint nowhere — define an accent.
- Dark mode is designed, not inverted; semantic/adaptive colors tested in
  both appearances. Dark carries measurable hue — no pure #000 background
  or #FFF text in either scheme; light/dark are one hue at two lightnesses.
- Hue budget: at most 4 saturated hues per screen, each with exactly one
  documented meaning; ~70% of visible surface stays neutral — saturation
  lives in content and state, never chrome.

## 4. Component sizing: proportional, consistent strokes

- Progress rings: ~200pt in a main view, ~90pt in a small widget, thin
  strokes (≈3pt). Never a 260pt ring with 8–9pt strokes dominating the screen.
- Background and foreground strokes of the same element use the SAME
  `lineWidth` — 9 behind 8 reads as misalignment.
- Toggle rows: natural height from 12pt vertical padding, 15pt label, and
  `Toggle`'s built-in label — never `.labelsHidden()` plus a manual HStack,
  never `.frame(height: 70)`.
- Tap targets ≥ 44×44pt, always.

## 5. Cards & grouped content: system patterns

- Corner radius 10pt for cards/groups (iOS system style); never 22pt+.
  Radii nest concentrically (inner ~ half of outer) and vary by role —
  "everything 16pt-rounded on a gray field" is the canonical AI-slop tell.
- Background `Color(.secondarySystemBackground)` (or the system's surface
  token) — no decorative gradients or overlay borders on standard cards.
- Dividers: system `Divider().padding(.leading, 16)` — never custom divider
  structs.

## 6. Navigation & layout structure

- `NavigationStack` with a real toolbar — not a bare `ZStack` with a
  manually placed title Text.
- Layout: `VStack`/`HStack`/`LazyVGrid`; paged content via `ScrollView` +
  `.scrollTargetBehavior(.paging)`. Prefer `containerRelativeFrame` (iOS 17+)
  over `GeometryReader`; never `UIScreen.main.bounds`. `Color`, not
  `UIColor`/`NSColor`.
- Not one giant VStack — break into semantic sections with clear hierarchy.
- Native navigation model: tab bar for 2–5 primary destinations (never a
  hamburger menu), push `NavigationStack` for drill-down, modal sheets for
  focused tasks (dismiss by swipe-down or explicit close). Back-button text is
  the previous screen's title, not "Back". Full model in
  `references/hig-navigation.md`.
- Respect safe areas: interactive content never under the notch, Dynamic
  Island, or home indicator; `.ignoresSafeArea()` for backgrounds only. Design
  smallest-screen-first (375pt) and verify on iPhone SE through Pro Max.

## 7. WidgetKit

- Lock-screen circular/linear indicators: `Gauge` with `.accessoryCircular`
  / `.linearCapacity` — never manual circle drawing.
- Background: `.containerBackground(.fill.tertiary, for: .widget)` — never a
  hardcoded color.
- Support all relevant families (`accessoryCircular/Rectangular/Inline`,
  `systemSmall/Medium/Large`); medium and large share the same structural
  hierarchy (header / progress / footer) unless a hard size constraint
  forces otherwise.
- Explicit internal padding (≈12pt) on home-screen widgets to avoid clipping
  at rounded edges.
- Memory budget ~30 MB: dense visualizations (365-dot grids) draw in one
  `Canvas` pass, never hundreds of nested `ZStack`s in a `LazyVGrid`.
- Timeline refresh matches data granularity: `.after(midnight)` for daily
  data, ~15-min periodic for time-of-day fills — never minute-level for
  static daily data.

## 8. Interactive elements

- `Toggle(isOn:){label}` with a single accent `.tint` — built-in label, not
  hidden.
- Mutually exclusive options = ONE selected enum value rendered as a
  checklist/picker, never N independent toggles that allow contradictory
  state.
- Changing numbers animate: `.contentTransition(.numericText())`.
- Motion carries state: no animation without a state/data trigger (the
  remove-it test — if meaning survives removal, remove it). One signature
  animation per workflow; everything else ≤0.35s sharing at most two
  curves; no idle shimmer or bounce garnish.
- Density bimodality: a screen is either dense (≥3:1 type contrast, one
  oversized anchor) or calm (few elements, generous margins) — medium
  density with medium contrast is the slop zone.

## 9. Interactive editors (crops, collages, canvases)

- Present from payload state: `.sheet(item: $activeRequest)` — never
  `Bool` + separately managed optional data that can race.
- ONE shared geometry model for live preview and export — preview and
  render must not each invent their own pan/zoom/crop math. Revealed
  background on zoom-out is part of that model or it doesn't happen.
- Gestures (tap / long-press-drag / pinch) share one interaction state
  machine with explicit priority — not scattered booleans.
- No-scroll editor screens budget height top-down through named regions
  (header / canvas stage / settings / toolbar) with the math in one place.
- Custom headers: know whether the parent already respects the safe area —
  never add `safeAreaInsets.top` reflexively (double-counting = dead space).
  Keep them compact: chrome, not a content section.
- Multi-mode editors show ONE active settings surface at a time; the canvas
  stays visually dominant.

## 10. Shared data model (app + widgets)

One model, one calculation, used by both the app views and the widget
`TimelineProvider` — never duplicated snapshot structs with re-derived date
math. If the UI implies live progress, include time-of-day in the shared
fraction.

## 11. Assets & placeholders

- SF Symbols or a real icon set — never emoji as functional icons, never
  AI-drawn SVG clipart or CSS-silhouette illustrations.
- Can't source a real asset? Clean, labeled gray placeholder and ask the
  user for materials. A rough honest layout beats a polished generic one.

## 12. Native iOS idiom (HIG) — accessibility, gestures, haptics

- Every interactive element carries an `.accessibilityLabel` (noun: "Favorite");
  `.accessibilityValue` for state, `.accessibilityHint` for effect (action:
  "Shares this item"); group with `.accessibilityElement(children: .combine)`;
  hide decorative images with `.accessibilityHidden(true)`. A VoiceOver user
  must be able to complete every task. Never convey meaning by color alone.
- Never override system gestures: swipe-from-edge (back), swipe-down (dismiss
  modal), pull-down (refresh). Row swipe reveals actions; long-press shows
  context menus.
- Haptics are subtle and meaningful, never constant: impact for physical
  actions, notification (`.success`/`.warning`/`.error`) for outcomes, selection
  for UI changes; `.prepare()` before triggering.
- Native controls over custom reimplementations (they carry accessibility and
  haptics for free); system confirmations (`.alert()` / `.confirmationDialog()`),
  `.swipeActions` on rows, keyboard type paired with `.textContentType`.

Full HIG framework, scoring rubric (10/10), and per-topic depth in
`references/hig-overview.md`.

## Topic → reference router

| Topic | Reference file |
|-------|----------------|
| HIG framework, three pillars, 10/10 scoring, common mistakes, quick diagnostic | `references/hig-overview.md` |
| Nav/tab bar dimensions, large-title behavior, split view | `references/hig-navigation.md` |
| Semantic text hex values, Dark Mode text mapping | `references/hig-typography.md` |
| Extra color-layering tokens, full WCAG contrast table | `references/hig-colors-depth.md` |
| Button/list/section/picker/confirmation-dialog specifics | `references/hig-components.md` |
| Keyboard types, input accessory views, hardware shortcuts | `references/hig-keyboard-input.md` |
| VoiceOver patterns, pre-ship accessibility checklist | `references/hig-accessibility.md` |
| App-icon sizes, squircle math, iOS 18 light/dark/tinted variants | `references/hig-app-icons.md` |
| Reserved gestures, haptic recipes, animation timing/curves | `references/hig-gestures.md` |
| Permission timing, priming screens, usage strings, denied recovery | `references/hig-privacy-permissions.md` |
| Widget/App Clip/Live Activity/extension sizes + constraints | `references/hig-widgets-extensions.md` |
| Siri/Shortcuts, Handoff, drag-and-drop, universal links, Spotlight | `references/hig-system-integration.md` |

## 13. Review checklist (run for review mode; score honestly)

Spacing on the grid · ≤5 font sizes, one design incl. widgets · semantic or
token colors, no opacity ladders · equal stroke widths · 10pt card radius +
system Divider · Toggle built-in labels · exclusive choices are one value ·
NavigationStack present · Gauge in lock-screen widgets ·
`.containerBackground(.fill.tertiary)` · widget families consistent +
padded · Canvas for dense widget visuals · timeline matches granularity ·
no locale-grouped identifiers · tracking ≤2 values · no minimumScaleFactor ·
editors present from payload state · one geometry model preview+export · no
double-counted safe areas · height budgeted centrally on fixed screens · one
settings surface at a time · 44pt targets · dark mode designed · content text
scales with Dynamic Type · interactive elements have accessibility labels and a
VoiceOver-completable flow · native navigation (tab bar not hamburger,
NavigationStack, modal sheets) · safe areas respected on SE→Pro Max · system
gestures not overridden, haptics subtle · SF Symbols + correct app-icon shape ·
no banned slop patterns (~/.claude/skills/ios-design/DIRECTION.md §4) · one
signature detail per screen.

Report review findings as file:line + rule + minimal fix, most severe first.

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [dndungu](https://github.com/dndungu)
- **Source:** [dndungu/agent-skills](https://github.com/dndungu/agent-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-dndungu-agent-skills-swiftui-craft
- Seller: https://agentstack.voostack.com/s/dndungu
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
