Swiftui design consultation
Skill Paretofilm/superpowers-gstack/skills/swiftui-design-consultation
Apple-canon design system for SwiftUI projects: produces DESIGN.md + a Swift Package starter (semantic colors, SF Pro, Liquid Glass, motion, accessibility). Use when starting or refreshing a SwiftUI design system.From its SKILL.md
npx -y skills add Paretofilm/superpowers-gstack --skill swiftui-design-consultationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
34.7 KB, ~9.1k tokens by cl100k_base, as published. Nobody here has run it
/superpowers-gstack:swiftui-design-consultation
Apple-canon design system consultation for SwiftUI projects. Output:
DESIGN.md + DesignSystem/ Swift Package + DESIGN.html (via htmlify).
Locating this skill's files: templates/, schema/ and bin/ live NEXT TO this
SKILL.md, not in the user's project. Set SKILL_DIR to this skill's base
directory (shown when the skill loads) and reference every asset as
$SKILL_DIR/templates/…, $SKILL_DIR/schema/…, $SKILL_DIR/bin/…. This
resolves both in the plugin repo and in a marketplace install — never assume
cwd contains skills/.
Phase numbering is historical: former Phases 2, 4 and 5 were folded into Phases 3 and 6 during earlier revisions. The remaining numbers (0, 1, 3, 6) are kept stable because cross-references cite them.
Phase 0 — Setup
Step 0.0: Initialize gstack helpers (set $SLUG)
This skill writes per-project state under ~/.gstack/projects/$SLUG/,
so $SLUG (the gstack project identifier derived from the git remote)
must be set before any other Phase reads or writes it.
# Pull gstack-slug into the current shell. Sets $SLUG = repo-slug
# from `git remote get-url origin` per gstack convention.
#
# CAREFUL: `eval "$(... 2>/dev/null)" || fallback` does NOT work — if
# gstack-slug is missing, command substitution returns empty, `eval ""`
# succeeds (exit 0), and the fallback never runs. Capture output first
# and check non-empty explicitly.
SLUG_OUTPUT=$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)
if [ -n "$SLUG_OUTPUT" ]; then
eval "$SLUG_OUTPUT"
fi
if [ -z "${SLUG:-}" ]; then
# Fallback: derive slug from git toplevel basename, or pwd basename
# if not in a git repo.
SLUG="$(basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)")"
export SLUG
fi
if [ -z "$SLUG" ] || [ "$SLUG" = "/" ]; then
echo "FATAL: could not derive SLUG. Aborting." >&2
exit 1
fi
mkdir -p ~/.gstack/projects/"$SLUG"
echo "SLUG: $SLUG"
$SLUG is referenced throughout subsequent phases for state-file paths
(~/.gstack/projects/$SLUG/design-proposal-{ts}.md,
~/.gstack/projects/$SLUG/swiftui-consultation-state.json, etc.).
Step 0.1: Track self-bootstrap (inline platform-question)
mkdir -p .gstack
if [ ! -f .gstack/track ] || [ -z "$(cat .gstack/track 2>/dev/null | tr -d '[:space:]')" ]; then
echo "TRACK_MISSING"
else
echo "TRACK: $(cat .gstack/track | tr -d '[:space:]')"
fi
If output is TRACK_MISSING, ask the platform question inline. Since
v2.3.0 there is no separate swiftui-track skill — the platform
question lives here (when invoked directly) and in
/superpowers-gstack:office-hours-track-aware Phase 3 (when reached
via the standard office-hours flow).
AskUserQuestion brief:
D0 — Which platform target does this project ship to?
Project/branch/task: declaring SwiftUI track for direct invocation
ELI10: I need to know iPhone/iPad, Mac, or both — so generated
Package.swift and design defaults match. The marker is set once
per project and read by all future swiftui-* skills.
Stakes if we pick wrong: Package.swift declares wrong platforms;
easy to re-run later. No permanent damage.
Recommendation: iOS only (most common for new projects)
Note: options differ in kind, not coverage — no completeness score.
Pros / cons:
A) iOS only (recommended)
✅ Most common case; iPadOS works via iOS target automatically
✅ Smallest Package.swift surface
❌ No macOS companion — add later by re-running
B) macOS only
✅ Menu-bar tools, productivity apps, system utilities
✅ Liquid Glass strategy tuned for macOS chrome
❌ Narrows reach — no phone/tablet
C) Both iOS + macOS
✅ Cross-platform Swift Package; max reach
❌ More #if os() surfaces; longer compile times
Net: pick where you'll first ship.
Write the chosen value to .gstack/track:
echo "$CHOICE" > .gstack/track
After write, re-read:
TRACK=$(cat .gstack/track | tr -d '[:space:]')
echo "TRACK: $TRACK"
$TRACK must be one of ios | macos | both to continue. If it is
anything else (corrupted marker, user typed invalid value), STOP and
report BLOCKED — invalid .gstack/track value.
Step 0.2: Detect project mode
MODE="greenfield"
if find . -maxdepth 3 -type f \( -name "*.xcodeproj" -o -name "Package.swift" -o -name "*.swift" \) 2>/dev/null | head -1 | grep -q .; then
MODE="existing"
fi
echo "MODE: $MODE"
Step 0.3: Verify MCP surface
Call mcp__swiftui-rag__corpus_info (no arguments). Verify the
returned JSON has:
runtime_version(string, e.g., "1.3.1")sample_count(integer, > 0)hig_rules(array with at least 11 entries)accessibility_rules(array with at least 3 entries)
If any field is missing or the call fails, STOP and report:
BLOCKED — swiftui-rag MCP unavailable. Run /sync-gbrain or check pipx install.
Step 0.3.1: Verify MCP parameter schemas (defensive)
Tool NAMES are only half the contract. Before any later phase
invokes these tools, verify the actual parameter NAMES match what
the plan assumes. The tool schemas are visible in Claude Code's
tool list (the same list this skill's frontmatter allowed-tools
references). Read them and confirm:
| Tool | Expected primary param | If schema differs, use the actual name |
|---|---|---|
mcp__swiftui-rag__index_project | path (string) | Look for repo_path, project_path, dir, etc. |
mcp__swiftui-rag__search_project | query (string) | Look for q, text, pattern |
mcp__swiftui-rag__search_swiftui_corpus | question (string) — verified live | Optional: platform, cluster_tag, hyde_doc, top_k |
mcp__swiftui-rag__swift_typecheck | swift_code (string), target_versions (array of strings) | Verify both names |
mcp__swiftui-rag__review_macos_hig | swift_code (string) | Verify name |
mcp__swiftui-rag__review_liquid_glass | swift_code (string) | Verify name |
mcp__swiftui-rag__review_accessibility | swift_code (string) | Verify name |
If a tool's actual schema differs from the table above, the agent executing this plan should ADJUST its invocations accordingly — do not blindly use the names above if the schema disagrees. This is a runtime-correctness gate; the plan's prose uses the most likely names but the schema is authoritative.
If any tool is missing from the tool list (e.g., MCP server not
attached), STOP and report BLOCKED — required MCP tool not available.
Step 0.4: Existing-project indexing
If MODE is existing, call mcp__swiftui-rag__index_project with
the repo root path. Cache the returned project ID for Phase 1 lookups.
Step 0.5: Read existing DESIGN.md if present
if [ -f DESIGN.md ]; then
echo "DESIGN_MD_EXISTS"
fi
If exists, read it. We may be doing a refresh consultation, in which case Phase 3 will surface "keep / replace / refine" choices per pillar.
Phase 1 — Product Context
Single combined AskUserQuestion brief. Three fields (platform is
already in .gstack/track, no need to re-ask).
Step 1.1: Pre-fill from project context
If README.md or CLAUDE.md exist, read them and extract: product
name, audience, space/industry. Pre-fill the AskUserQuestion with what
you found, asking the user to confirm or refine.
If MODE is existing and search_project was indexed, call
mcp__swiftui-rag__search_project with these queries:
"Color extension"— surface existing semantic-color decisions"Font.system\|Font.custom"— surface existing typography"\.material\|\.glassEffect"— surface existing material usage"Color(red:"— flag non-semantic colors (a quality signal)
Cache the counts: N Color extensions, M Font declarations, K material usages, X non-semantic colors flagged. These appear in the Phase 1
prompt.
Step 1.2: Ask the combined Phase 1 question
AskUserQuestion brief:
D1 — Product context for the SwiftUI design system
Project/branch/task: starting design consultation for this $TRACK project
ELI10: I need three things before I can propose a design system:
what the product IS, what feeling it should evoke (so every later
design choice serves one anchor), and how strict the HIG conformance
bar is. The platform is already set ($TRACK from .gstack/track).
Stakes if we pick wrong: a memorable-thing answer that's too vague
produces a generic-feeling app; a too-loose budget lets POLISH
findings accumulate; an unclear audience leads to mis-tuned
defaults (e.g., productivity-app density vs reader-app whitespace).
Field 1 — Product context confirm/refine
[Pre-filled from README/CLAUDE.md if found. User confirms or rewrites.]
Field 2 — Memorable Apple thing
"What's the one Apple-native quality you want this app to be remembered
for after a user opens it for the first time?" Examples: "as fluid as
Things", "as sharp as Linear-mac", "as quiet as Reeder", "as confident
as Stripe-mac". One sentence.
Field 3 — HIG conformance budget
A) Recommended: 0 CRITICAL · 0 SIGNIFICANT · ≤5 POLISH (recommended)
B) Strict: 0 / 0 / 0 (zero tolerance — for apps that must feel Apple-native on first launch)
C) Relaxed: 0 / 3 / 10 (early prototype; some POLISH and limited SIGNIFICANT acceptable)
If MODE is existing, prepend to the brief:
I indexed your project. Current patterns I found: $N Color extensions,
$M Font declarations, $K material usages, $X non-semantic colors
flagged. We can keep, replace, or refine each in Phase 3.
Step 1.3: Store the answer
Cache the user's three answers as JSON in
~/.gstack/projects/$SLUG/swiftui-consultation-state.json:
{
"track": "ios",
"product_context": "...",
"memorable_thing": "...",
"budget": { "critical": 0, "significant": 0, "polish": 5 },
"mode": "greenfield",
"existing_patterns": null
}
This state survives /clear, so a long consultation can resume.
Phase 3 — Complete Proposal (with htmlify preview)
Build the internal DesignProposal data model, serialize to a structured proposal Markdown, /htmlify it for Safari preview, then ask user to approve / drill / change.
Step 3.1: Build the data model
Query mcp__swiftui-rag__search_swiftui_corpus per pillar to ground
each choice in canonical patterns + HIG citations:
- Typography: query
"typography roles SF Pro semantic font" - Color: query
"semantic color asset catalog dark mode adaptive" - Materials: query
"Liquid Glass material chrome content layer" - Motion: query
"spring animation snappy smooth named easing" - Accessibility: query
"VoiceOver Dynamic Type contrast accessibility"
Each returns 3-5 corpus samples + HIG-page citations. Use these as grounding for proposing the design.
Build the DesignProposal object as a structured YAML document
matching $SKILL_DIR/schema/proposal.schema.yaml.
The schema defines every required field across all eight pillars
(metadata, track, typography, color, materials, motion, spacing,
accessibility, platforms, budget, decisions_log) with the exact
types Phase 6 generators expect.
See $SKILL_DIR/schema/proposal.example.yaml
for a fully populated canonical example. New proposals SHOULD start
from that template and only modify fields where the actual design
differs.
The structured proposal becomes the single source of truth — both DESIGN.md and the DesignSystem/* Swift Package are generated as pure functions of this YAML object in Phase 6. Drift between the two outputs is structurally impossible because they read the same parsed data.
Step 3.2: Serialize and /htmlify the proposal
Pin a single timestamp so the files written here and the paths
referenced when invoking htmlify match exactly (do NOT call
$(date ...) twice; the two calls would produce different
timestamps when seconds tick):
TS=$(date +%Y%m%d-%H%M%S)
PROPOSAL_PATH=~/.gstack/projects/"$SLUG"/design-proposal-"$TS".md
PROPOSAL_YAML=~/.gstack/projects/"$SLUG"/design-proposal-"$TS".yaml
Write both files with the same timestamp suffix:
$PROPOSAL_YAML— the structured proposal matchingproposal.schema.yaml. This is what Phase 6 reads.$PROPOSAL_PATH— the human-readable proposal Markdown for /htmlify preview. Derived from the same data, but formatted for human review.
Both files MUST be consistent — same values in both. The YAML is authoritative; the MD is its presentation. If you regenerate one, regenerate the other from the same source.
Structure the file as rich Markdown with:
- H1: "Design Proposal: $PRODUCT_NAME"
- H2 sections per pillar (Aesthetic, Typography, Color, Materials, Spacing, Motion, Accessibility)
- For each pillar: rationale + HIG citation links + table of choices
- Final H2: "HIG Conformance Budget" + "Decisions Log"
Build a /htmlify v2 plan JSON at /tmp/design-proposal-plan.json with:
flowchart-svgfor the Materials section (shows where glass goes / doesn't)comparison-matrixfor Typography (display vs title vs body vs caption vs code)stats-barfor the HIG Budget panel- Color swatches as a custom inline HTML block (via
section-cardbody) feedback_panelwith premises drawn from the actual proposal andApprove / Drill into X / Change Y / Start overradio
Invoke htmlify via the Skill tool (NOT a direct bin path — the htmlify skill's base directory differs per install location: dev repo vs marketplace cache vs vendored). Use the Skill invocation pattern that Claude Code resolves automatically:
Skill(skill="superpowers-gstack:htmlify",
args="~/.gstack/projects/$SLUG/design-proposal-$TS.md --plan /tmp/design-proposal-plan.json --open")
If the Skill-tool dispatch is unavailable (rare; only in spawned sessions without the htmlify plugin), fall back to locating htmlify's bin from the plugin cache:
HTMLIFY_BIN=""
for VERSION_DIR in ~/.claude/plugins/cache/paretofilm-plugins/superpowers-gstack/*/; do
CANDIDATE="${VERSION_DIR}skills/htmlify/bin/htmlify"
[ -x "$CANDIDATE" ] && HTMLIFY_BIN="$CANDIDATE"
done
# Or repo dev path:
[ -z "$HTMLIFY_BIN" ] && [ -x "$(git rev-parse --show-toplevel 2>/dev/null)/skills/htmlify/bin/htmlify" ] \
&& HTMLIFY_BIN="$(git rev-parse --show-toplevel)/skills/htmlify/bin/htmlify"
[ -z "$HTMLIFY_BIN" ] && { echo "htmlify not found"; exit 1; }
"$HTMLIFY_BIN" ~/.gstack/projects/$SLUG/design-proposal-$TS.md \
--plan /tmp/design-proposal-plan.json --open
Prefer the Skill tool path; fall back to bin only if needed.
Step 3.3: Ask user approve/drill/change
AskUserQuestion brief:
D2 — Does the proposed design system look right?
Project/branch/task: SwiftUI design consultation, Phase 3 proposal review
ELI10: I opened a visual preview in Safari (typography specimens, color
swatches with light/dark toggle, material samples, motion previews,
HIG budget panel, decisions log). Tell me which path: approve and
ship, drill into a specific pillar to tighten it, change something
specific, or restart the proposal from scratch.
A) Approve — write DESIGN.md + Swift Package now (recommended if the preview feels right)
B) Drill into [pillar] — tighten one pillar without committing the others
C) Change [pillar] — different direction for one pillar
D) Start over — rebuild the whole proposal with different constraints
If A: proceed to Phase 6.
If B or C: AskUserQuestion which pillar, then re-query that
pillar's corpus, rewrite that section of the proposal MD, re-htmlify,
loop back to D2.
If D: go back to Phase 1 to refine constraints.
Step 3.4: Cache approved proposal
When user picks A, copy both the structured YAML and the
human-readable MD to canonical names (without the timestamp suffix):
cp "$PROPOSAL_YAML" ~/.gstack/projects/"$SLUG"/swiftui-consultation-state.proposal.yaml
cp "$PROPOSAL_PATH" ~/.gstack/projects/"$SLUG"/swiftui-consultation-state.proposal.md
The YAML is what Phase 6 generators read — it is the structured source of truth. The MD is kept alongside for human inspection, debugging, and audit trails (e.g. "what did we approve last Tuesday?"). They must stay consistent.
Phase 6 — Write Artifacts (with native-review chain)
Paired generation. DESIGN.md and DesignSystem/* are both written from the approved proposal YAML. Then run conformance review against the HIG budget; iterate up to 2 times if over budget, with severity monotonicity guard between iterations.
Step 6.0: Load and validate the structured proposal
Before generating any artifact, read the cached structured proposal and validate it against the schema:
PROPOSAL_YAML=~/.gstack/projects/"$SLUG"/swiftui-consultation-state.proposal.yaml
SCHEMA="$SKILL_DIR/schema/proposal.schema.yaml"
[ -f "$PROPOSAL_YAML" ] || { echo "ERROR: no cached proposal — Phase 3 must complete first."; exit 1; }
Then LLM-side validation:
- Read both
$PROPOSAL_YAMLand the schema file into context. - Walk every
requiredfield in the schema. Confirm it is present in the proposal with the right type. (For arrays withminItems, confirm the item count. For strings withpattern, confirm the regex matches.) - Confirm
schema_versionin the proposal equals the schema'sversionfield (currently1). Mismatch is a hard STOP — surface to user with explicit upgrade guidance, do not attempt to regenerate against a schema the proposal doesn't claim. - Confirm
trackin the proposal matches$TRACK(the value from.gstack/track). Mismatch is a hard STOP — the proposal claims one platform target, the project marker claims another. Surface the discrepancy and AskUserQuestion which is authoritative.
If validation passes, the parsed proposal object is the single source of truth for every token substitution in Steps 6.1 and 6.2 below. If any required field is missing or any type mismatches, STOP and surface the schema gap to the user — do NOT silently substitute empty strings.
Step 6.1: Generate DESIGN.md from template (with overwrite-safety)
Before writing, check if <repo>/DESIGN.md already exists:
if [ -f DESIGN.md ]; then
BACKUP="DESIGN.md.backup-$(date +%Y%m%d-%H%M%S)"
cp DESIGN.md "$BACKUP"
echo "Existing DESIGN.md backed up to $BACKUP"
fi
This is the refresh-mode safety net: any prior DESIGN.md is preserved under a timestamp suffix before overwrite. Same pattern for the Swift Package in Step 6.2.
Then read $SKILL_DIR/templates/DESIGN.md.template.
Substitute all 15 tokens ({{DATE}}, {{PRODUCT_CONTEXT}}, etc.)
from the parsed proposal YAML object loaded in Step 6.0 — NOT
from the human-readable proposal MD. The MD is for human review; the
YAML is the data source. Write to <repo>/DESIGN.md.
Token-to-YAML-field mapping:
| Template token | Proposal YAML path |
|---|---|
{{DATE}} | metadata.date |
{{PRODUCT_CONTEXT}} | metadata.product_context |
{{MEMORABLE_THING}} | metadata.memorable_thing |
{{AESTHETIC_DIRECTION}} | metadata.aesthetic_direction |
{{TYPOGRAPHY_PROSE}} | typography.prose |
{{COLOR_PROSE}} | color.prose |
{{MATERIALS_PROSE}} | materials.prose |
{{MOTION_PROSE}} | motion.prose |
{{SPACING_PROSE}} | spacing.prose |
{{ACCESSIBILITY_PROSE}} | accessibility.prose |
{{PLATFORMS_PROSE}} | platforms.prose |
{{BUDGET_CRITICAL}} | budget.critical |
{{BUDGET_SIGNIFICANT}} | budget.significant |
{{BUDGET_POLISH}} | budget.polish |
{{DECISIONS_LOG}} | decisions_log (rendered as bulleted list) |
Step 6.2: Generate Swift Package (with overwrite-safety)
Before writing, back up any existing DesignSystem/ directory (refresh-mode safety net, companion to Step 6.1's DESIGN.md backup):
if [ -d DesignSystem ]; then
BACKUP_DIR="DesignSystem.backup-$(date +%Y%m%d-%H%M%S)"
mv DesignSystem "$BACKUP_DIR"
echo "Existing DesignSystem/ moved to $BACKUP_DIR"
fi
Then create the directory layout:
mkdir -p DesignSystem/Sources/DesignSystem/Resources
mkdir -p DesignSystem/Tests/DesignSystemTests
For each template in $SKILL_DIR/templates/:
- Read it
- Substitute tokens from the parsed proposal YAML object loaded in Step 6.0 (NOT from the human-readable proposal MD)
- Write to the corresponding path under
DesignSystem/
Token-to-YAML-field mapping for the Swift Package templates:
| Template token | Proposal YAML path |
|---|---|
{{PLATFORMS}} | derived from track (ios → .iOS(.v26), macos → .macOS(.v26), both → .iOS(.v26), .macOS(.v26)) |
{{DECLARED_TRACK}} | track |
{{TYPE_ROLES}} | typography.roles (rendered as Swift type-role enum + Font.TextStyle map) |
{{BRAND_COLORS}} | color.brand (rendered as Swift Color extensions + asset-catalog entries) |
{{SEMANTIC_COLORS}} | color.semantic (rendered as Swift Color extensions mapping to system colors) |
{{MOTION_PRESETS}} | motion.presets (rendered as static let X: Animation = ... declarations) |
{{SPACING_CONSTANTS}} | spacing.constants (rendered as static let X: CGFloat = ...) |
{{RADIUS_CONSTANTS}} | spacing.radius (rendered as static let X: CGFloat = ...) |
{{PLATFORM_ASSERTIONS}} | platforms.assertions (rendered as #if os(...) ... #endif test guards) |
Because both DESIGN.md (Step 6.1) and the Swift Package (Step 6.2) read the same parsed YAML object, the prose in DESIGN.md and the declarations in Swift code are guaranteed to refer to the same typography roles, colors, motion presets, etc. Drift is structurally impossible.
Mapping:
Package.swift.template→DesignSystem/Package.swift- Substitute
{{PLATFORMS}}per$TRACK:ios→.iOS(.v26)macos→.macOS(.v26)both→.iOS(.v26), .macOS(.v26)
- Substitute
Colors.swift.template→DesignSystem/Sources/DesignSystem/Colors.swiftTypography.swift.template→DesignSystem/Sources/DesignSystem/Typography.swiftSpacing.swift.template→DesignSystem/Sources/DesignSystem/Spacing.swiftMotion.swift.template→DesignSystem/Sources/DesignSystem/Motion.swiftMaterials.swift.template→DesignSystem/Sources/DesignSystem/Materials.swiftHIGBudgetTests.swift.template→DesignSystem/Tests/DesignSystemTests/HIGBudgetTests.swiftPlatformsTests.swift.template→DesignSystem/Tests/DesignSystemTests/PlatformsTests.swift
For brand colors with hex values, also create asset-catalog entries
under DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/.
Each brand color entry uses Xcode's color-set format with light, dark,
and high-contrast variants.
WCAG contrast check (via $SKILL_DIR/bin/contrast-check.sh): for each brand hex
provided, call $SKILL_DIR/bin/contrast-check.sh
once against the proposed light-mode background and once against the
proposed dark-mode background. Parse the returned JSON:
# Example call from inside the skill — handle exit code explicitly
# so a missing `bc` or invalid hex doesn't silently pass the check.
CONTRAST_BIN="$SKILL_DIR/bin/contrast-check.sh"
if RESULT_LIGHT=$("$CONTRAST_BIN" "$BRAND_HEX" "$BG_LIGHT_HEX" 2>&1); then
PASS_LIGHT=$(echo "$RESULT_LIGHT" | grep -o '"pass_aa_normal": [a-z]*' | awk '{print $2}')
else
PASS_LIGHT="error"
echo "WARN: contrast-check failed for $BRAND_HEX vs $BG_LIGHT_HEX (exit $?). Skipping; surfacing to user." >&2
fi
if RESULT_DARK=$("$CONTRAST_BIN" "$BRAND_HEX" "$BG_DARK_HEX" 2>&1); then
PASS_DARK=$(echo "$RESULT_DARK" | grep -o '"pass_aa_normal": [a-z]*' | awk '{print $2}')
else
PASS_DARK="error"
echo "WARN: contrast-check failed for $BRAND_HEX vs $BG_DARK_HEX (exit $?). Skipping; surfacing to user." >&2
fi
If either result is error (helper script failed — likely bc missing
or invalid hex), surface the warning to the user and ask explicitly
whether to accept the brand color without verified contrast. Do NOT
treat empty/missing PASS as a pass.
If either PASS_LIGHT or PASS_DARK is false, surface to the user:
- The actual ratio (from
RESULT_LIGHT.ratio/RESULT_DARK.ratio) - A flag: "Brand color $BRAND_HEX fails WCAG AA against $WHICH background"
- AskUserQuestion: keep this hex (override), or pick a different one?
The skill does NOT auto-adjust the hex (the spec said L*-rotation in OKLCH but that requires a full color-space library — out of scope for v1's bash helper). Manual adjustment by the user is the v1 fallback; auto-suggestion can be added in v1.1.
Step 6.3: Type-check generated Swift
Call mcp__swiftui-rag__swift_typecheck with the generated Swift code.
Target versions per $TRACK:
ios:["iOS-26.0"]macos:["macOS-26.0"]both:["iOS-26.0", "macOS-26.0"]
If typecheck fails: fix the offending file (likely a missing #if os()
guard for platform-specific API) and retry. Hard cap: 3 typecheck
attempts. If still failing, STOP and report the diagnostic.
Step 6.4: Generate DESIGN.html via Skill tool (auto-copy to repo root)
Invoke htmlify via the Skill tool (same pattern as Step 3.2; do NOT use a hardcoded bin path):
Skill(skill="superpowers-gstack:htmlify", args="<repo>/DESIGN.md")
No --plan needed — v1 rendering is fine for the human-readable spec; no feedback panel needed for the final DESIGN.html.
htmlify writes to <repo>/.superpowers-html/DESIGN.html by default,
but .superpowers-html/ is typically gitignored. The spec promises
DESIGN.html lives next to DESIGN.md at repo root and ships
committed. After htmlify completes, copy the file:
mkdir -p .superpowers-html # ensure htmlify output dir exists
if [ -f .superpowers-html/DESIGN.html ]; then
cp .superpowers-html/DESIGN.html DESIGN.html
echo "Copied DESIGN.html to repo root for commit"
else
echo "WARN: htmlify did not produce expected output at .superpowers-html/DESIGN.html" >&2
# Don't fail the skill — DESIGN.md is the source of truth; HTML is auxiliary.
fi
The repo-root DESIGN.html is what Step 6.8 commits. The
.superpowers-html/DESIGN.html is the htmlify-managed copy (refreshed
on every re-htmlify; remains gitignored). Both exist intentionally;
they have different lifecycles.
Step 6.5: Chain to the native review for the project's track (on DESIGN.md)
The native-review skills read the artifact from context, not from a path
argument. Pick the reviewer(s) by $TRACK — an iOS project must get
ios-native-review, not a guaranteed-N/A macOS pass:
$TRACK | Invoke |
|---|---|
macos | superpowers-gstack:macos-native-review |
ios | superpowers-gstack:ios-native-review |
both | both skills, sequentially (each reviews its own platform's surfaces) |
For each reviewer to run:
-
Read DESIGN.md into context via the Read tool (once is enough even when both reviewers run):
Read(file_path="<absolute path>/DESIGN.md") -
Invoke the reviewer via the Skill tool. The just-loaded DESIGN.md content is now in the model's context, so the skill's Phase 0 (platform signal detection) and its category review can read it directly:
Skill(skill="superpowers-gstack:<track-appropriate>-native-review", args="Review the DESIGN.md just loaded into context. It is the design system spec for a SwiftUI project on track=$TRACK; budget is $BUDGET_CRITICAL/$BUDGET_SIGNIFICANT/$BUDGET_POLISH.")
Capture each skill's verdict and findings list by severity (CRITICAL,
SIGNIFICANT, POLISH). These feed into the budget check at Step 6.7 —
for both, aggregate the two findings lists.
(History: before v2.23.0 this step invoked macos-native-review unconditionally and told iOS projects to treat its N/A as "expected behavior" — so iOS DESIGN.md never got a native review at all.)
Step 6.6: Chain to review-* tools (on each .swift file)
The three review-* tools are complementary, not overlapping. Each has its own ruleset; running all three is the only way to get complete coverage:
| Tool | What it lints | Rules |
|---|---|---|
mcp__swiftui-rag__review_macos_hig | Full macOS HIG ruleset | C1, C2, S1–S9 (11 rules) |
mcp__swiftui-rag__review_accessibility | Accessibility-only subset | A1, A2, A3 (3 rules) |
mcp__swiftui-rag__review_liquid_glass | Liquid Glass-only subset of HIG | C1, S7, S8 (subset) |
For each .swift file under DesignSystem/Sources/DesignSystem/,
invoke all three tools in parallel (they are independent and
read-only). Each call passes the same swift_code argument.
Aggregate findings across all three tools by severity, deduplicating
by (rule_id, line) — review_liquid_glass returns a subset of
HIG findings, so the same S7-material-on-content finding can appear
in both its output and review_macos_hig's. Count it once.
Known limitation (as of swiftui-rag v1.4.0 / corpus v1.3.3): the
C1-glass-on-content rule does not always fire when the .glassEffect
modifier is separated from its shape primitive by other method calls.
Pattern that DOES fire: Circle().glassEffect(). Pattern that has been
observed NOT to fire: Circle().fill(...).frame(...).glassEffect().
If your generated code uses shape-then-modifier-then-glassEffect chains
on content layers, manually verify against Materials HIG. Track
upstream fix at swiftui-rag-pipeline issue tracker.
Step 6.7: Budget check + iteration
Aggregate findings from all three review- tools* combined
(review_macos_hig + review_accessibility + review_liquid_glass),
deduplicated by (rule_id, line), plus the track-appropriate native-review verdict(s):
- CRITICAL count
- SIGNIFICANT count
- POLISH count
Compare against the Phase 1 budget. If within budget:
- Commit the artifacts (one commit, message format below)
- Print summary: "✅ DESIGN.md + DesignSystem/ + DESIGN.html committed. 0 CRITICAL · 0 SIGNIFICANT · 3 POLISH (within budget of 0/0/5)."
- Done.
If over budget:
- List findings ranked by severity with file:line + proposed fix
- Update the proposal YAML to absorb the proposed fixes (single source of truth — never edit DESIGN.md or DesignSystem/* files directly during iteration; they are regenerated from the YAML)
- Regenerate all artifacts (Step 6.1 onwards) from the updated YAML
- Re-run reviews
- Severity monotonicity guard (see below) must pass before accepting the iteration; otherwise rollback
- Hard cap: 2 iterations. If still over budget after iteration 2, STOP and AskUserQuestion: "Findings exceed budget after 2 iterations. Choose: (A) ship anyway, (B) override budget to actual numbers, (C) refine manually now."
Severity monotonicity guard (between iterations)
After each iteration N (N ≥ 1), compare findings against iteration N-1 (the baseline is the post-Phase-3-approval state, before any HIG-driven edits). The fix for one finding must not introduce new findings of equal or higher severity. Specifically:
- No NEW CRITICAL — iteration N must have zero CRITICAL findings that were not present in iteration N-1. A fix that turns SIGNIFICANT into CRITICAL is worse than the original.
- SIGNIFICANT count must not increase — N's SIGNIFICANT count ≤ N-1's SIGNIFICANT count. Same logic.
- POLISH count may drift up or down — POLISH is the most tolerant category; tighter conformance often surfaces additional POLISH gaps that were masked by SIGNIFICANT issues. This is fine.
Comparison procedure:
- Parse findings from iteration N (output of the three review-* tools + the track-appropriate native review(s), aggregated and deduplicated per Step 6.7 as written).
- For each iteration-N finding, look it up by
(rule_id, file:line)in iteration N-1. NEW findings are those not present in N-1. - Count NEW CRITICAL and NEW SIGNIFICANT.
- If NEW CRITICAL > 0 OR NEW SIGNIFICANT > 0 (rolled-up count
increased):
- ROLLBACK the proposal YAML to its iteration N-1 state (keep an in-memory backup of the YAML before each iteration's edit so rollback is mechanical, not LLM-judgment).
- AskUserQuestion:
"Iteration $N introduced $NEW_CRITICAL new CRITICAL and $NEW_SIGNIFICANT new SIGNIFICANT findings while fixing the original. Choose: (A) accept iteration $N anyway (override monotonicity), (B) accept iteration $N-1 state (forgo the attempted fix), (C) refine manually now (drop into AskUserQuestion- driven editing)."
This prevents the failure mode where the LLM trades one CRITICAL for another, or fixes a SIGNIFICANT by introducing two new SIGNIFICANTs. Without the guard, "iteration 2" can leave the user with a strictly worse artifact than the one they approved in Phase 3.
Why this matters: budget conformance is a necessary condition, not a sufficient one. An artifact with zero CRITICAL findings that was originally zero CRITICAL is fine. An artifact with zero CRITICAL findings that had one CRITICAL in iteration N-1 and one different CRITICAL in iteration N has churned, not improved — the monotonicity check catches that.
Step 6.8: Commit (including .gstack/track marker)
The spec requires .gstack/track committed to the repo (project-level
decision, not per-developer). If the project's .gitignore already
excludes .gstack/, we need to force-add the marker AND record an
exception in .gitignore so future runs don't silently drop it.
# Ensure .gstack/track is committable
if git check-ignore -q .gstack/track 2>/dev/null; then
# Project ignores .gstack/ — add an exception for the marker
if ! grep -q '^!\.gstack/track$' .gitignore 2>/dev/null; then
echo '!.gstack/track' >> .gitignore
echo "Added exception for .gstack/track to .gitignore"
fi
git add .gitignore
git add -f .gstack/track # force-add since ignore rule still matches dir-walk
else
git add .gstack/track
fi
git add DESIGN.md DESIGN.html DesignSystem/
git commit -m "feat: scaffold design system via swiftui-design-consultation
Generated by /superpowers-gstack:swiftui-design-consultation.
- DESIGN.md: design source of truth (HIG-cited)
- DesignSystem/: Swift Package with semantic colors, SF Pro typography,
Liquid Glass material discipline, named motion presets, accessibility
baseline
- DesignSystemTests: HIGBudgetTests + PlatformsTests enforce the
conformance budget at CI time
Track: $TRACK
HIG findings: $CRITICAL CRITICAL · $SIGNIFICANT SIGNIFICANT · $POLISH POLISH (within budget)
Co-Authored-By: Claude <[email protected]>"
What ships with it: 12 files
24.2 KB alongside SKILL.md, 1 of them executable
bin/
- contrast-check.shruns2.8 KB
schema/
- proposal.example.yaml6.0 KB
- proposal.schema.yaml9.0 KB