agentsclimarketplace

Ui enhancer radar

Skill Terryc21/radar-suite/skills/ui-enhancer-radar

Systematic iOS/SwiftUI UI audit with design intent interview, 14-domain analysis (including Color Audit with adaptive Color Profile, iPad Sheet Sizing caller-side audit, Button Hit Region three-factor interaction audit, and Silent Picker menu-presentation audit), element compaction, cross-view consistency checks, layout reorganization, design-aware push-back, App Store guardrails, and incremental apply with revert safety. Run /ui-enhancer-radar help for all commands. Triggers: "enhance this UI", "ui enhancer radar", "improve this view", "screen review", "ux audit", "ipad sheet truncation", "button not tappable on iPad", "picker does nothing".From its SKILL.md

Install
npx -y skills add Terryc21/radar-suite --skill ui-enhancer-radar

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 19 stars19 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.

What its file declares

Copied from the file, not written here

The file declares its own license as Apache-2.0. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

138.3 KB, as published. Nobody here has run it

UI Enhancer Radar

Quick Ref: Screenshot + code analysis of any iOS/SwiftUI view. Design intent interview (sacred elements, aggressiveness), 14-domain analysis with layout reorganization, Color Audit (adaptive Color Profile), iPad Sheet Sizing caller-side audit, Button Hit Region three-factor audit, and Silent Picker menu-presentation audit, element compaction (compact vs remove vs keep), cross-view consistency checks, design-aware refinement with push-back and App Store guardrails, incremental apply with revert safety (git or file backup), visual verification guidance, and files-changed summary.

<ui-enhancer-radar>

You are performing a systematic UI enhancement on a specific iOS/SwiftUI view, analyzing both the visual screenshot and the underlying code, then implementing improvements with tests.

Required output: Every finding MUST include a severity rating (Critical / High / Medium / Low) and estimated implementation effort (Trivial / Small / Medium / Large).

Quick Commands

CommandDescription
/ui-enhancer-radarFull audit with interview
/ui-enhancer-radar spaceSpace efficiency analysis only
/ui-enhancer-radar hierarchyVisual hierarchy analysis only
/ui-enhancer-radar densityInformation density analysis only
/ui-enhancer-radar interactionInteraction patterns analysis only
/ui-enhancer-radar accessibilityAccessibility audit only
/ui-enhancer-radar higHIG compliance check only
/ui-enhancer-radar dark-modeDark mode audit only
/ui-enhancer-radar performancePerformance impact analysis only
/ui-enhancer-radar design-systemDesign system compliance only
/ui-enhancer-radar colorColor audit only (inventory, flatness, consistency)
/ui-enhancer-radar ipad-sheetsiPad sheet sizing audit only (caller-side .sheet() check)
/ui-enhancer-radar hit-regionButton hit region audit only (three-factor: .plain + Form/List + trailing chevron)
/ui-enhancer-radar silent-pickerSilent Picker audit only (menu Picker, no .pickerStyle, custom non-Form container)
/ui-enhancer-radar compareCompare before/after screenshots for progress
/ui-enhancer-radar revertUndo all changes back to last checkpoint
/ui-enhancer-radar batch [path]Audit all views in a directory, rank by severity
--show-suppressedShow findings suppressed by known-intentional entries
--accept-intentionalMark current finding as known-intentional (not a bug)
/ui-enhancer-radar --captureCapture screenshot from running simulator (optional)
/ui-enhancer-radar --devicesAnalyze layout across device sizes (optional)
/ui-enhancer-radar fix-deferredResolve items deferred from a previous run
/ui-enhancer-radar verifyRe-check previous findings without full re-audit (~5 min)

Help Command

If the user runs /ui-enhancer-radar help, display this command reference and stop (do not start an audit):

UI Enhancer — Available Commands

FULL AUDIT
  /ui-enhancer-radar              Full 14-domain audit with interview

SINGLE DOMAIN (skip interview, run one domain)
  /ui-enhancer-radar space        Space efficiency analysis
  /ui-enhancer-radar hierarchy    Visual hierarchy analysis
  /ui-enhancer-radar density      Information density analysis
  /ui-enhancer-radar interaction  Interaction patterns analysis
  /ui-enhancer-radar accessibility  Accessibility audit
  /ui-enhancer-radar hig          HIG compliance check
  /ui-enhancer-radar dark-mode    Dark mode audit
  /ui-enhancer-radar performance  Performance impact analysis
  /ui-enhancer-radar design-system  Design system compliance
  /ui-enhancer-radar color        Color audit (inventory, flatness, consistency)
  /ui-enhancer-radar ipad-sheets  iPad sheet sizing audit (caller-side `.sheet()` check)
  /ui-enhancer-radar hit-region   Button hit region audit (.plain + chevron + Form/List)

UTILITIES
  /ui-enhancer-radar compare      Compare before/after screenshots
  /ui-enhancer-radar revert       Undo all changes back to checkpoint
  /ui-enhancer-radar batch [path] Audit all views in a directory
  /ui-enhancer-radar help         Show this command list

OPTIONS (combine with any command)
  --capture                 Capture screenshot from running simulator
  --devices                 Analyze layout across device sizes

First-Run Hint

When /ui-enhancer-radar is invoked with no subcommand, display this hint once before the Phase 1 interview (not on subsequent runs in the same session):

Tip: Run /ui-enhancer-radar help to see all available commands, or continue for a full audit.

Then proceed directly to Phase 1.


Skill Introduction (MANDATORY — run before anything else)

This section replaces radar-suite-core.md § Session Setup for the ui-enhancer-radar entry point. Do NOT also run core's 4-question Session Setup — its questions are consolidated below. Phase 1 ("Interview") below reuses the answers captured here and does NOT re-ask experience level, fix mode, or attendance.

On first invocation, ask all setup questions in a single AskUserQuestion call:

Question 1: "What's your experience level with Swift/SwiftUI?"

  • Beginner — New to Swift. Plain language, analogies, define terms on first use.
  • Intermediate — Comfortable with SwiftUI basics. Standard terms, explain non-obvious patterns.
  • Experienced (Recommended) — Fluent with SwiftUI. Concise findings, no definitions.
  • Senior/Expert — Deep expertise. Terse, file:line only, skip explanations.

Question 2: "How should fixes be handled?"

  • Auto-fix safe items (Recommended) — Apply isolated, low-blast-radius fixes automatically. Present cross-cutting fixes and design decisions for approval first.
  • Review first — Present all findings with ratings, then ask before making any changes. Fixes still happen — you just approve each wave first.
  • Batch mode — Approve all fixes in each wave at once.

IMPORTANT: All three modes lead to fixes. "Review first" means the user sees the plan before code changes — it does NOT mean "skip fixes and jump to handoff." After presenting findings, ALWAYS offer to fix them regardless of which mode was selected. (Exception: Hands-Free mode overrides this — see Question 3.)

Question 3: "Will you be stepping away during the audit?"

  • I'll be here (Recommended) — Normal mode. Permission prompts may appear for writes/edits.
  • Hands-Free (walk away safe) — Read-only analysis only (Read, Grep, Glob). No edits, no Bash, no prompts. Hands-Free overrides Question 2: all fixes are deferred regardless of FIX_MODE. Phase 7b (Visual Inspection Gate) is skipped — no code changes will be applied. The skill emits the saved playbook and Hands-Free completion message instead of the next-phase AskUserQuestion (see § Hands-Free Mode below for full precedence rules).
  • Pre-Approved — You have already configured Claude Code permissions for this session. Run at full speed without restriction.

Store as: USER_EXPERIENCE, FIX_MODE, PRESENCE_MODE. Apply to ALL output for the session, per radar-suite-core.md § Experience-Level Output Rules. Also persist to .radar-suite/session-prefs.yaml per radar-suite-core.md § Session Persistence.

Question 4 (optional follow-up): "Would you like a brief explanation of what this skill does?"

  • No, let's go (Recommended) — Skip explanation, proceed to Phase 1 (Interview).
  • Yes, explain it — Show one of the explanations below adapted to experience level, then proceed.

Experience-adapted explanations for UI Enhancer:

  • Beginner: "UI Enhancer is like having a professional designer review every screen in your app. It checks 14 different things — spacing, colors, accessibility, layout efficiency, iPad sheet sizing, button hit regions, and more — then suggests specific improvements. It won't just say 'this looks wrong'; it'll show you exactly what to change and why. It works one view at a time, applying changes incrementally so you can undo anything."

  • Intermediate: "UI Enhancer performs a 14-domain analysis of SwiftUI views: layout, spacing, color accessibility, typography, element compaction, cross-view consistency, iPad sheet sizing, button hit regions, and more. It interviews you about design intent first, then audits against Apple HIG and your app's design system. Changes are applied incrementally with revert safety."

  • Experienced: "14-domain SwiftUI UI audit with design intent interview, adaptive color profiles, element compaction, cross-view consistency checks, iPad sheet sizing caller-side audit, button hit region three-factor audit, silent picker menu-presentation audit, layout reorganization, App Store guardrails, and incremental apply with revert safety. Run /ui-enhancer-radar help for the full command list."

  • Senior/Expert: "14-domain view audit: layout, color, typography, spacing, compaction, consistency, accessibility, iPad sheet sizing, button hit region, silent picker. Interview → analyze → apply incrementally."

Store the experience level as USER_EXPERIENCE and apply to ALL output for the session.

User impact explanations: Can be toggled at any time with --explain / --no-explain. When enabled, each finding gets a 3-line companion explanation (what's wrong, fix, user experience before/after). See the shared rating system doc for format and rules. Store as EXPLAIN_FINDINGS (default: false).

Experience-level auto-apply: If USER_EXPERIENCE = Beginner, auto-set EXPLAIN_FINDINGS = true and default sort to impact. If Senior/Expert, default sort to effort. Apply all output rules from Experience-Level Output Rules table in radar-suite-core.md.


Shared Patterns

See radar-suite-core.md for: Tier System, Pipeline UX Enhancements, Table Format, Plain Language Communication, Work Receipts, Contradiction Detection, Finding Classification, Audit Methodology, Context Exhaustion, Progress Banner, Issue Rating Tables, Handoff YAML schema, Known-Intentional Suppression, Pattern Reintroduction Detection, Experience-Level Output Rules, Implementation Sort Algorithm, short_title requirement.

Pre-Scan Startup (MANDATORY — before any domain scan)

  1. Known-intentional suppression: Run the protocol in radar-suite-core.md § Known-Intentional Suppression. Core owns this — do not restate the steps here.

  2. Pattern reintroduction detection: Run the protocol in radar-suite-core.md § Pattern Reintroduction Detection. Core owns this.


Phase 1: Interview

Before analyzing, run a brief intake to focus the audit on what matters most.

Experience level was already captured in § Skill Introduction (Question 1). Do NOT re-ask it here. The questions below are content-specific to ui-enhancer-radar's audit shape — focus, priority, design intent, aggressiveness.

Display this instruction before the first set of questions:

To answer, type the option labels (e.g., "General polish, All domains, Moderate") or use numbers (e.g., "1, 1, 1"). You can answer all questions at once or one at a time.

questions:
[
  {
    "question": "What's the main reason for this review?",
    "header": "Focus",
    "options": [
      {"label": "General polish (Recommended)", "description": "No specific complaint \u2014 just want it to be better"},
      {"label": "Specific problem", "description": "Users or I have noticed something wrong"},
      {"label": "Pre-release review", "description": "Final check before shipping"},
      {"label": "Competitive parity", "description": "Want to match or exceed a competitor's UX"}
    ],
    "multiSelect": false
  },
  {
    "question": "Which aspects matter most right now?",
    "header": "Priority",
    "options": [
      {"label": "All domains (Recommended)", "description": "Full 14-domain analysis"},
      {"label": "Visual \u2014 layout and hierarchy", "description": "Space, visual weight, information density"},
      {"label": "Interaction \u2014 usability", "description": "Touch targets, discoverability, feedback"},
      {"label": "Technical \u2014 performance and compliance", "description": "Dark mode, perf, HIG, design system"}
    ],
    "multiSelect": false
  }
]

If "Specific problem", follow up:

questions:
[
  {
    "question": "What's the specific issue?",
    "header": "Issue",
    "options": [
      {"label": "Too much wasted space (most common)", "description": "Content doesn't start until halfway down the screen"},
      {"label": "Hard to find things", "description": "Users can't discover features or actions"},
      {"label": "Looks cluttered or confusing", "description": "Too much competing for attention"},
      {"label": "I'll describe it", "description": "Let me explain the problem"}
    ],
    "multiSelect": false
  }
]

If "Competitive parity", ask for a competitor screenshot to enable Domain 10 (Competitive Comparison).

Design Intent (always ask)

After the focus/priority questions, always ask about design intent and change aggressiveness:

questions:
[
  {
    "question": "Are there elements on this screen you consider essential to its identity — things that should be preserved even if they use extra space?",
    "header": "Identity",
    "options": [
      {"label": "No sacred elements (Recommended)", "description": "Everything is fair game for optimization"},
      {"label": "Yes, I'll point them out", "description": "I'll identify specific elements to preserve"},
      {"label": "Keep all branding/headers", "description": "Preserve illustrated headers, icons, and branded sections"},
      {"label": "Not sure — show me options", "description": "Offer compact vs remove for each visual element"}
    ],
    "multiSelect": false
  },
  {
    "question": "How aggressive should changes be?",
    "header": "Aggressiveness",
    "options": [
      {"label": "Moderate (Recommended)", "description": "Compact where possible, remove only clear redundancies"},
      {"label": "Conservative", "description": "Tighten spacing and compact only — never remove elements"},
      {"label": "Aggressive", "description": "Maximize space — remove anything non-essential"}
    ],
    "multiSelect": false
  }
]

Attendance

Attendance mode (PRESENCE_MODE) was already captured in § Skill Introduction (Question 3). Do NOT re-ask here. The Hands-Free Mode section below documents precedence rules for write-tool deferral and AskUserQuestion suppression.

Hands-Free Mode (precedence rules — load-bearing)

When PRESENCE_MODE = Hands-Free:

  1. Hands-Free overrides FIX_MODE. Even if Question 2 was answered Auto-fix safe items, Review first, or Batch mode, no fixes are applied in Hands-Free mode. All findings are emitted with Status: Deferred (hands-free) in the Issue Rating table. The Fix Plan, Report (Phase 6), and Playbook (Phase 7) are still produced — they're held in the conversation for the user to act on when they return.
  2. Phase 7b (Visual Inspection Gate) is skipped. Since no code changes will be applied, the gate has nothing to protect. The skill emits the saved playbook to .agents/ui-enhancer-radar/[date]-[view]-playbook.md and prints the completion message below.
  3. Phase 7c-7f are deferred. Guided Visual Review, Apply Approved Changes, Pattern Sweep, and Refinement Loop all require interactive user prompts (AskUserQuestion) and/or write tools. None of these run in Hands-Free.
  4. BLOCKING progress banner exception. The "CRITICAL — BLOCKING requirement" for the next-phase AskUserQuestion (§ Phase Progress Banner) applies in Normal and Pre-Approved modes only. In Hands-Free, the progress banner still prints after each completed phase, but the AskUserQuestion is suppressed in favor of the completion message.
  5. Handoff YAML emitted inline. Writing .agents/ui-audit/ui-enhancer-radar-handoff.yaml and .radar-suite/ledger.yaml requires Edit/Write — forbidden in Hands-Free. The skill emits these as fenced YAML blocks inline in the conversation so the user can persist them on return.

Hands-Free completion message:

⏱ Hands-free audit complete through Phase 5 (Domain Analysis) + Phase 6 (Report).
  Phases skipped (deferred until you return): 7a-7f (apply), 8 (tests), 9 (summary).
  Findings deferred: [count] (no fixes applied — Hands-Free mode)
  Playbook saved: .agents/ui-enhancer-radar/[date]-[view]-playbook.md
  Handoff YAML + ledger entries: emitted inline above; copy to .agents/ui-audit/ and .radar-suite/ when you return
  Reply to continue with supervised phases.

When PRESENCE_MODE = Pre-Approved: full speed, no restrictions. Assumes permissions are configured per the Permission Setup guide below. All phases run normally.

Permission Setup (for unattended runs)

To avoid permission prompts during audits, pre-allow these read-only patterns in Claude Code settings. Safe to auto-approve — they cannot modify your codebase:

# Already safe by default (no setup needed):
Read, Grep, Glob — always auto-approved

# Add these for unattended Bash scans:
Bash(find:*)
Bash(wc:*)
Bash(stat:*)

Do NOT auto-approve (keep prompted — they modify state):

Edit, Write — file modifications
Bash(rm:*), Bash(git:*) — destructive operations

After all questions, always offer:

Anything else I should know? (design preferences, constraints, specific elements to preserve — or press Enter to skip)

Record any free-text input as additional constraints that apply throughout the audit. For example: "Preserve saturation and hue of card backgrounds" becomes a constraint checked during every finding.

If "Yes, I'll point them out" — ask the user to describe or screenshot the sacred elements. Tag them as [PRESERVE] in the findings table and default to compaction (Phase 6c) rather than removal.

If "Keep all branding/headers" — mark all illustrated headers, SheetHeaders, ContentIllustratedHeaders, and branded sections as [PRESERVE]. Only recommend compaction for these, never removal.

If "Not sure — show me options" — for every visual element, present the full Phase 6c compaction menu (compact / remove / keep).

Aggressiveness calibration

The aggressiveness setting affects all findings throughout the audit:

SettingSpacingHeadersDecorative elementsRedundant info
ConservativeTighten 10-20%Compact onlyKeep, reduce sizeMerge, don't remove
ModerateTighten 20-40%Compact (default), remove if redundantCompact or removeRemove if truly duplicate
AggressiveMinimize to HIG minimumRemove unless [PRESERVE]RemoveRemove

The interview determines:

  • Which domains to run (all 14, or a focused subset)
  • Severity weighting (user-reported problems get Critical minimum)
  • Whether to include competitive comparison
  • Which elements are sacred ([PRESERVE] tag)
  • How aggressive to be with changes (Conservative / Moderate / Aggressive)
  • How to pitch explanations (experience level)

Experience-Level Adaptation

Adjust ALL output (findings, questions, recommendations, compaction options) based on the user's experience level:

  • Beginner: Plain language, real-world analogies. "This header takes up 120 points of space — that's about a third of the visible screen on an iPhone. Compacting it would let users see their content sooner." Define SwiftUI terms on first use. When presenting compaction options, explain what each choice means visually.
  • Intermediate: Standard SwiftUI terminology, explain non-obvious tradeoffs. "The SheetHeader uses 120pt — compacting to inline icon+title saves 80pt and keeps the visual identity. The .stuffolioCard() modifier handles the styling." Explain architectural choices but not basic concepts.
  • Experienced (default): Concise findings. "SheetHeader: 120pt → 40pt inline. Saves 80pt above fold." No definitions, focus on measurements and tradeoffs.
  • Senior/Expert: Minimal. "SheetHeader 120→40pt. .stuffolioCard() handles styling. 3 files touched." Skip design rationale — just the change, the impact, and the blast radius.

Phase 2: Gather Input

questions:
[
  {
    "question": "How would you like to provide the view to analyze?",
    "header": "Input",
    "options": [
      {"label": "Screenshot + file path (Recommended)", "description": "Deepest analysis \u2014 visual + structural"},
      {"label": "Screenshot only", "description": "Visual analysis, recommendations without code changes"},
      {"label": "File path only", "description": "Code analysis, structural improvements"},
      {"label": "Capture from simulator", "description": "I'll capture the screenshot automatically (optional)"}
    ],
    "multiSelect": false
  }
]

If "Capture from simulator" (optional feature):

xcrun simctl io booted screenshot /tmp/ui-enhancer-radar-capture.png

Then read the captured screenshot.


Phase 2b: View Type Classification

Before analyzing, classify the view type. This determines severity weighting, compensation strategies, and domain-specific heuristics throughout the audit.

Classify by reading the code and/or screenshot. Do NOT ask the user — infer from evidence:

View TypeHow to IdentifyImplications
Dashboard / overviewAggregates data from multiple sources, cards/widgets, summary statsSpace efficiency is Critical; visual variety matters most
Detail / inspectorShows one item's full data, sections of attributesInformation density is Critical; hierarchy matters most
Form / inputText fields, pickers, toggles for data entryInteraction patterns are Critical; keep chrome minimal
List / tableRepeating rows of similar items, search/filterDensity and performance are Critical; row height matters
Help / referenceStatic instructional text, tips, guidesSpace is Medium priority; visual richness prevents boredom
Sheet / modalPresented modally for a focused taskCheck for duplicate headers (SheetContainer + SheetHeader)
Settings / configToggles, preferences, grouped sectionsHIG compliance is Critical; follow system patterns

Record the classification at the top of the report (e.g., "View type: Help / reference (sheet)"). Reference it when:

  • Choosing severity levels (Domain 1-11)
  • Offering compaction alternatives (Phase 6c)
  • Recommending compensation techniques (Phase 6d)
  • Deciding whether a finding is worth fixing
  • Applying platform-specific design heuristics (see below)

Platform-Specific Design Heuristics

When the view runs on multiple platforms, apply platform-appropriate design expectations:

View TypemacOS (with sidebar)iPhone (no sidebar)
DashboardShould be a summary — show stats, alerts, and insights. Navigation lives in sidebar. Feature cards that duplicate sidebar items are redundant.Should be a hub — provide entry points to all features. No sidebar means the dashboard IS the navigation.
Settings / configSettings items accessible via sidebar don't need dashboard cards. Theme toggle in header is optional if Settings is 1 click away.Settings behind a tab or gear icon — header controls add convenience.
Form / inputWider layout allows side-by-side fields. Keyboard toolbar not needed (Tab key navigation).Keyboard Done toolbar is essential. Single-column layout.
List / tableCan show more columns, denser rows. Hover effects for interactivity.Swipe actions, pull-to-refresh. Touch-optimized row height (44pt min).

How to apply: After classifying the view type, check which platform the screenshot/code targets. If macOS with sidebar, apply the "summary" heuristic for dashboards. If iPhone, apply the "hub" heuristic. Flag findings that are platform-specific (e.g., "This redundancy only applies on macOS where the sidebar provides the same navigation").


Phase 3: Screenshot Analysis

When a screenshot is provided, analyze it visually:

3a. First Impression (3-Second Test)

  • What does the user see first? Is it what matters most?
  • Can the user tell what this screen does within 3 seconds?
  • Is the primary action obvious?

3b. Visual Scan Pattern

  • Does the layout follow natural eye flow (F-pattern or Z-pattern)?
  • Are related elements grouped visually?
  • Is there a clear visual hierarchy (primary > secondary > tertiary)?

3c. Content-to-Chrome Ratio

  • Measure: What percentage of the viewport is actual content vs. navigation chrome?
  • Target: >60% content on iPhone, >70% on iPad
  • Flag any view where content ratio falls below 50%

Phase 4: Code Analysis

When a SwiftUI file is provided, read it and analyze:

4a. View Structure

  • Total nesting depth (VStack chains)
  • Number of distinct visual sections
  • Spacing and padding accumulation
  • Bottom padding for floating elements

4b. Component Audit

  • Redundant UI elements (same info shown twice)
  • Elements that could be merged (separate rows that should be one)
  • Hidden/conditional elements wasting space when collapsed
  • Action button sizing and placement

4c. Text and Typography

  • Font hierarchy clarity (is there a clear primary > secondary > tertiary?)
  • Line limit truncation (important text cut off while less important text has room)
  • Dynamic Type support (.system(size:) vs semantic fonts)

4d. Color Usage

  • Meaningful vs. decorative color
  • Competing colors
  • Contrast sufficiency
  • Information redundancy (color + icon + text)

Phase 5: Domain Analysis

Run the applicable domains based on the interview:

Verification Template (MANDATORY per view audit)

Before grading a view, produce this checklist showing what was actually inspected:

| Domain | Checked? | Receipt | Findings |
|--------|----------|---------|----------|
| 1. Space Efficiency | ? | (file:line) | |
| 2. Visual Hierarchy | ? | (file:line) | |
| 3. Information Density | ? | (file:line) | |
| 4. Interaction Patterns | ? | (file:line) | |
| 5. Accessibility | ? | (file:line) | |
| 6. HIG Compliance | ? | (file:line) | |
| 7. Dark Mode | ? | (file:line) | |
| 8. Performance Impact | ? | (file:line) | |
| 9. Design System Compliance | ? | (file:line) | |
| 10. Competitive Comparison | ? | (file:line) | (only if competitor screenshot provided; otherwise skip with reason) |
| 11. Color Audit | ? | (file:line) | |
| 12. iPad Sheet Sizing | ? | (file:line) | |
| 13. Button Hit Region | ? | (file:line) | |
| 14. Silent Picker | ? | (file:line) | (candidates — verify on device) |

Rules:

  • Every domain must be marked checked (yes) or skipped (no — with reason)
  • Skipped domains cannot contribute to the grade (positive or negative)
  • A grade cannot be produced while any domain has ?
  • Domain 10 is conditionally applicable — only runs when the user supplies a competitor screenshot during Phase 1. If no screenshot, mark it skipped (no competitor screenshot provided) and do not include in scoring.

Domain 1: Space Efficiency enumerate-required

Goal: Maximize content-to-chrome ratio; minimize wasted vertical space.

CheckWhat to Look ForCommon Fix
Header overheadCustom headers stacking on nav barCollapse or merge headers
Dual button rowsAction buttons on separate line from navigationMerge into one row
Section headersOversized section titles with iconsReduce font size, remove decorative icons
Bottom paddingExcessive padding for floating elementsReduce to actual element height + margin
Hints/tipsPermanent hints that should dismiss after first useUse coach marks or remove after learning
Photo sectionsSeparate photo rows when thumbnail could be inlineMerge photo into title/header row
Dividers/spacersExcessive VStack spacing or explicit Spacer()Reduce spacing values
Mergeable sectionsSmall sections (1-2 items) with their own header overheadMerge into adjacent related section
Relocatable controlsButtons/toggles in separate rows that could fit in an existing header or toolbarMove into header, nav bar, or existing row
Redundant entry pointsSame action accessible from both a toolbar button AND a content card/rowRemove the duplicate; keep the more discoverable one

Layout reorganization analysis (run for every view):

Before recommending individual element changes, check whether reorganizing the layout would save more space than tweaking individual elements:

  1. Count items per section — sections with 1-2 items are candidates for merging with adjacent sections
  2. Check for orphaned controls — buttons, toggles, or status indicators in their own row that could be absorbed into an existing element (e.g., theme toggle → header)
  3. Identify duplicate entry points — the same action accessible from both a toolbar/action bar AND a content card below; remove the less discoverable one
  4. Measure section header overhead — each section header costs ~40pt; merging 2 sections saves ~40pt without touching content

Metrics:

  • Content starts at: [Y position in points from top]
  • First interactive element at: [Y position]
  • Content-to-chrome ratio: [percentage]
  • Target: Content should start within 120pt of safe area top on iPhone

Domain 2: Visual Hierarchy enumerate-required

Goal: The most important information should be the most prominent.

CheckWhat to Look ForCommon Fix
Title prominenceIs the item/screen title the dominant element?Ensure title is largest text
Competing elementsMultiple elements fighting for attentionDifferentiate with size/weight/color
Status overloadStatus indicators louder than contentReduce to subtle badges
Date/number sizingDates or numbers displayed too largeUse caption/footnote for secondary data
Action vs. contentAction buttons more prominent than contentTone down button styling
TruncationImportant text truncated while less important text has roomAllow wrapping or reprioritize
Color dominanceBright colors on secondary elementsReserve bright colors for primary actions

Analysis technique:

  1. Squint at the screenshot — what stands out?
  2. That should be the primary content, not navigation chrome
  3. If navigation or status draws the eye first, hierarchy is wrong

Domain 3: Information Density enumerate-required

Goal: Show the right amount of information — not too sparse, not too cluttered.

CheckWhat to Look ForCommon Fix
Sparse rowsList rows with too much whitespaceReduce padding, show more per row
Dense rowsToo much crammed into one rowProgressive disclosure
Redundant infoSame data shown in multiple placesRemove duplicates
Hidden useful infoImportant data behind taps/scrollingSurface in primary view
Badge overloadToo many status badges on one elementPrioritize, hide secondary
Empty statesLarge empty areas when data is missingCollapse section
Nonsense valuesNegative numbers, meaningless datesUse human labels or hide

Density targets:

  • List row: 2-3 lines max (title + subtitle + trailing status)
  • Card: 4-6 data points visible without scrolling
  • Form section: 3-5 fields visible without scrolling

Domain 4: Interaction Patterns mixed

Goal: Every interactive element should be discoverable, predictable, and satisfying.

CheckWhat to Look ForCommon Fix
Touch targetsElements smaller than 44x44ptIncrease frame/padding
Ambiguous buttonsButtons that look like labelsAdd clear button styling
Combined buttonsTwo features sharing one buttonSeparate into distinct buttons
Gesture-only actionsActions only via swipe/long-pressAdd visible button alternative
Dead endsScreens with no clear next actionAdd CTA or navigation hint
Feedback gapsActions with no visual/haptic responseAdd animation or haptic
Scroll discoveryContent below fold with no indicatorAdd hint or gradient
Menu depthImportant actions buried in menusSurface frequently-used actions

Domain 5: Accessibility mixed

Goal: Every user, regardless of ability, can use the view effectively.

CheckWhat to Look ForCommon Fix
Color-only infoInformation conveyed only by colorAdd icon + text (triple redundancy)
Fixed fonts.system(size:) instead of semanticUse Dynamic Type
Missing labelsImages/icons without accessibility labelsAdd .accessibilityLabel()
Small textText below 11pt that doesn't scaleUse .caption minimum
Contrast ratioLow contrast text on backgroundsEnsure 4.5:1 (WCAG AA)
VoiceOver orderReading order doesn't match visualReorder or group
MotionAnimations without Reduce Motion checkCheck accessibility setting

Domain 6: HIG Compliance mixed

Goal: Follow Apple Human Interface Guidelines for platform consistency.

CheckWhat to Look ForCommon Fix
NavigationCustom back buttons, non-standard patternsUse system NavigationStack
Tab barIncorrect icons or labelsFollow SF Symbol + short label
Sheet presentationMissing drag indicator or dismissAdd .presentationDragIndicator(.visible)
System colorsHard-coded colorsUse .primary, .secondary
Platform differencesiOS-only patterns on macOSUse #if os(iOS)
Safe areasContent under notch or home indicatorRespect safe area insets
Standard controlsCustom controls duplicating systemUse SwiftUI standard controls

Domain 7: Dark Mode grep-sufficient

Goal: The view should look correct and intentional in both light and dark mode.

CheckWhat to Look ForCommon Fix
Hardcoded colorsColor.white, Color.black, hex valuesUse semantic colors (.primary, .background)
Background assumptions.background(Color.white)Use .background(Color(.systemBackground))
Shadow visibilityShadows invisible in dark modeUse .shadow with adaptive opacity
Image contrastImages with white/transparent backgroundsAdd dark mode variants or tinted backgrounds
Separator visibilityLight separators disappearingUse .separator system color
Accent consistencyAccent colors that clash in dark modeTest all accent colors in both modes
Material usageSolid backgrounds where materials work betterUse .ultraThinMaterial for overlays

Analysis: If screenshot provided, check if the view uses light or dark mode. If code available, run the automated checks below.

CRITICAL: Do NOT delegate Domain 7 checks to Explore subagents. Run each check directly using Grep/Read tools against the target view file(s). Subagent sampling causes false negatives.

Automated Detection

Check 7a: Hardcoded colors

# Find hardcoded Color.white, Color.black, Color(red:green:blue:), hex colors
grep -n "Color\.white\|Color\.black\|Color(red:\|Color(hex:\|UIColor(red:\|NSColor(red:" <view_file>.swift

# Find .foregroundColor(.white) / .foregroundStyle(.white)
grep -n "foregroundColor(\.white)\|foregroundStyle(\.white)\|foregroundColor(\.black)\|foregroundStyle(\.black)" <view_file>.swift

Safe patterns (do NOT flag):

// ✅ Semantic colors
.foregroundStyle(.primary)
.background(Color(.systemBackground))

// ✅ White text on intentionally colored backgrounds (e.g., Insights card pattern)
// Check: if the background is a solid accent color, .white text is correct

// ✅ Color.white/black in Color(light:dark:) adaptive initializers

Check 7b: Background assumptions

# Find non-adaptive backgrounds
grep -n "\.background(Color\.white)\|\.background(\.white)\|\.background(Color\.black)" <view_file>.swift

# Find hardcoded RGB backgrounds
grep -n "\.background(Color(red:" <view_file>.swift

Check 7c: Shadow visibility in dark mode

# Find shadows with low opacity black (invisible in dark mode)
grep -n "\.shadow(color:.*\.black.*opacity.*0\.[0-1]" <view_file>.swift

# Find shadows without color parameter (default is black, may be invisible)
grep -n "\.shadow(radius:" <view_file>.swift | grep -v "color:"

Check 7d: Material opportunities

# Find solid background overlays that could use materials
grep -n "\.background(Color.*opacity\|\.background(\.ultraThin\|\.background(\.thin\|\.background(\.regular" <view_file>.swift

# Find ZStack overlays with solid semi-transparent backgrounds
grep -B3 -A3 "ZStack" <view_file>.swift | grep "opacity"

Domain 8: Performance Impact grep-sufficient

Goal: UI patterns should not cause frame drops, excessive redraws, or memory issues.

CheckWhat to Look ForCommon Fix
Heavy bodyComplex expressions in view bodyExtract to computed properties
Inline imagesImage decoding in bodyUse AsyncCachedImage or background decoding
Missing lazyLarge lists without LazyVStackSwitch to lazy containers
Excessive stateToo many @State vars causing redrawsConsolidate or use @Observable
Geometry readersGeometryReader in scroll viewsUse .onGeometryChange or remove
Conditional complexityDeep if/else chains in bodyExtract to @ViewBuilder functions
Animation costHeavy animations on low-end devicesReduce or check Reduce Motion

Analysis: Read the SwiftUI file and run the automated checks below. Flag files over 500 lines that could benefit from extraction.

CRITICAL: Do NOT delegate Domain 8 checks to Explore subagents. Run each check directly.

Automated Detection

Check 8a: Missing lazy containers

# Find non-lazy VStack/HStack inside ScrollView with ForEach
grep -n "ScrollView" <view_file>.swift
# Then check: is the ForEach inside a VStack (not LazyVStack)?
grep -B5 "ForEach" <view_file>.swift | grep "VStack\b" | grep -v "LazyVStack"

Safe patterns (do NOT flag):

// ✅ Small static lists (< 20 items) — lazy containers add overhead for small lists
// ✅ VStack with fixed content (no ForEach)
// ✅ LazyVStack already used

Check 8b: GeometryReader in scroll contexts

# Find GeometryReader usage
grep -n "GeometryReader" <view_file>.swift

# Check if it's inside a ScrollView or List (causes layout thrashing)
grep -B10 "GeometryReader" <view_file>.swift | grep "ScrollView\|List {"

Safe patterns (do NOT flag):

// ✅ GeometryReader at the top level (not inside scroll)
// ✅ .onGeometryChange modifier (proper replacement)
// ✅ GeometryReader used only for initial measurement (cached in @State)

Check 8c: Excessive @State count

# Count @State variables in the view
grep -c "@State " <view_file>.swift

# Flag if > 8 @State vars in a single view (suggests consolidation needed)

Check 8d: Heavy view body

# Count lines in the body property (approximate)
# Find "var body:" and count lines until next "var " or "func " at same indent
grep -n "var body:" <view_file>.swift

# Flag if body exceeds ~100 lines — extract to computed properties or subviews
wc -l <view_file>.swift
# Flag if total file > 500 lines

Check 8e: Image decoding in body

# Find UIImage/NSImage initialization in view body (should be async)
grep -n "UIImage(data:\|UIImage(contentsOf\|NSImage(data:\|NSImage(contentsOf" <view_file>.swift

# Find synchronous image loading
grep -n "Data(contentsOf:" <view_file>.swift

Check 8f: Animation without Reduce Motion check

# Find .animation or withAnimation
grep -n "\.animation(\|withAnimation" <view_file>.swift

# Check if @Environment(\.accessibilityReduceMotion) is declared
grep -n "accessibilityReduceMotion" <view_file>.swift

# Flag: animations present but no reduce motion check

Safe patterns (do NOT flag):

// ✅ .animation(.default, value:) — implicit animations with value binding (low cost)
// ✅ System animations (sheet presentation, navigation transitions)
// ✅ Reduce Motion already checked

Domain 9: Design System Compliance enumerate-required

Goal: The view should follow the project's established design system. Compare it against the project's documented rules and flag both off-pattern usage (custom UI where a standard component/modifier exists, off-palette colors, non-@ScaledMetric icons, sheets not using SheetContainer) AND missing-for-consistency gaps (a feature most sibling views enable but this one doesn't).

Why this is project-specific: Domain 9 has no fixed ruleset — its rules ARE the project's design system. With no design-system source (conventions.yaml / CLAUDE.md rules / DESIGN_SYSTEM.md / style guide), it skips entirely and recommends establishing one. It never invents rules.

Reference: references/domain-9-design-system-compliance.md — full checks 9a–9e, unused-capability check, cross-view consistency pass, finding format, exclusions, acceptance criteria, synthetic fixture.

Detection steps (summary — full bodies in the reference):

  1. Read the design-system source (config → docs). If none exists, skip with a single "no design system found" note.
  2. 9a color palette — flag colors outside the approved palette.
  3. 9b component usage — custom header/container UI where a shared component exists.
  4. 9c modifier usage — card-like UI without .stuffolioCard() / .actionCard().
  5. 9d sheet pattern — sheets not using SheetContainer + SheetHeader.
  6. 9e icon sizing — fixed icon frames without @ScaledMetric.
  7. Unused-capability check — view builds custom UI for a feature a shared component already exposes via a parameter → enable the parameter, delete the custom UI.
  8. Cross-view consistency — using the Adaptive View Profile, flag features most sibling views enable that this view lacks; always framed as a recommendation with a skip option.

Project-convention awareness: sources rules from .radar-suite/conventions.yaml (approved_palette, spacing_namespace, card_modifiers, sheet_container, shared_components) if present, else greps CLAUDE.md / DESIGN_SYSTEM.md / Sources/Views/Components/. No source → skip the domain.

Severity default: 🟢 MEDIUM (drift, not breakage). Elevates to 🟡 HIGH when the violation breaks a load-bearing accessibility rule the design system encodes (colorblind-palette rule, Dynamic Type via @ScaledMetric), when 5+ views share the off-pattern shape (systemic), or when a missing empty/loading/error state leaves a real flow with no feedback. Never CRITICAL on its own.

Exclusions (skip without flagging): no design-system source exists; dev-only harness / #Preview / fixture; a documented, sanctioned exception; platform-guarded chrome correct for its platform. Full list in the reference.

CRITICAL: Do NOT delegate Domain 9 to Explore subagents. The conformance comparison needs the project docs and the view file held together; a subagent that reads only the view loses the baseline and produces false positives.


Domain 10: Competitive Comparison (On Request) enumerate-required

Only runs when user provides a competitor screenshot during interview.

AnalysisWhat to Compare
Information densityHow much data is visible without scrolling?
Visual hierarchyWhat does each app emphasize first?
Interaction patternsHow many taps to accomplish the same task?
Space efficiencyContent-to-chrome ratio comparison
Unique strengthsWhat does each app do better?

Output as a side-by-side comparison table.


Domain 11: Color Audit mixed

Goal: Ensure intentional, consistent, and effective use of color throughout the view. Detect monochromatic flatness, semantic drift, opacity inconsistencies, and missing visual differentiation.

Adaptive Color Profile: On first run, this domain reads CLAUDE.md and design system files to learn the project's palette rules. Findings are saved to .agents/ui-enhancer-radar/color-profile.md so subsequent audits can compare views against established patterns.

CRITICAL: Do NOT delegate Domain 11 checks to Explore subagents. Run each check directly using Grep/Read tools against the target view file(s).

11a. Color Inventory Table

Build a table of every colored element in the view:

ElementColorOpacityRoleCategory
Header bg.blue100%BrandingChrome
Section icon.secondary100%DecorationChrome
Toggle (on).blue100%InteractiveSystem
Row text.primary100%ContentText

Categories: Chrome (navigation, headers, borders), Content (user data, labels), Interactive (buttons, toggles, pickers), Status (badges, indicators), Decoration (icons, backgrounds, separators)

Automated Detection for Color Inventory

# Step 1: Extract all color references from the view file
grep -n "\.foregroundStyle(\|\.foregroundColor(\|\.fill(\|\.background(\|\.tint(\|Color\.\|Color(\|\.opacity(\|\.shadow(" <view_file>.swift

# Step 2: Extract specific named colors
grep -on "\.\(blue\|red\|green\|yellow\|orange\|purple\|pink\|cyan\|teal\|indigo\|gray\|mint\|brown\|primary\|secondary\|tertiary\|white\|black\|clear\|accentColor\)" <view_file>.swift

# Step 3: Extract custom color references
grep -n "AccessibleColor\|sf3aYellow\|Color(red:\|Color(hex:\|Color(\"" <view_file>.swift

# Step 4: Extract opacity modifiers
grep -n "\.opacity(" <view_file>.swift

Build the inventory table from these results. Each grep match becomes a row.

11b. Color Distribution

Count unique colors and how many elements use each:

Color Distribution:
  .secondary / .gray:  14 elements  ████████████████  (58%)  ⚠️ DOMINANT
  .blue:                4 elements  ████              (17%)
  .primary:             3 elements  ███               (12%)
  .red:                 2 elements  ██                 (8%)
  .tertiary:            1 element   █                  (4%)

Flag: Any color family used by >50% of elements → "Monochromatic risk" Flag: Any color used only once → "Orphan color — is it intentional?"

11c. Monochromatic Detection (Form Flatness)

This is the most critical check for form/settings views. When a view is visually flat — same background, same text color, same icon color everywhere — users cannot scan it effectively.

Automated Detection for Monochromatic Risk

# Count distinct color families in the view file (excluding opacity variants)
# Extract unique color names from all color references
grep -oh "\.\(blue\|red\|yellow\|orange\|purple\|pink\|cyan\|teal\|indigo\|gray\|mint\|brown\|primary\|secondary\|tertiary\|accentColor\)" <view_file>.swift \
  | sort -u | wc -l

# Count how many elements use each color family
grep -oh "\.\(blue\|red\|yellow\|orange\|purple\|pink\|cyan\|teal\|indigo\|gray\|mint\|brown\|primary\|secondary\|tertiary\)" <view_file>.swift \
  | sort | uniq -c | sort -rn

# Flag: if .secondary or .gray accounts for >50% of color references → monochromatic risk
# Flag: if total distinct color families ≤ 2 → critical monochromatic

Color Variance Score: Count distinct color families (not counting opacity variants) visible in the view, excluding system chrome (status bar, nav bar).

ScoreDistinct ColorsAssessment
1-2MonochromaticCritical — view appears as a flat, undifferentiated wall
3-4Low varietyHigh — sections blend together, hard to scan
5-6AdequateMedium — functional but could benefit from more differentiation
7+Good varietyPass — clear visual zones

When monochromatic is detected, recommend (in order):

  1. Colored section header icons — Each section gets a semantically colored icon circle (e.g., Network = blue cloud, Privacy = red shield, Data = purple database). This alone breaks the monochrome wall into scannable zones.
  2. Section background tints — Subtle colored backgrounds (5-8% opacity) behind each section group, using the section's accent color.
  3. Icon colorization — Replace .secondary gray icons with semantically meaningful colors from the project palette (shield = red, cloud = blue, sparkles = yellow).
  4. Interactive row highlighting — Rows with pickers, navigation chevrons, or buttons get a subtle accent indicator to distinguish from static display rows.

11d. Section Distinguishability

Can you tell where one section ends and another begins without reading the text?

CheckWhat to Look ForFix
Section headers same style as row labelsHeaders use same font/color as contentMake headers bolder, colored, or add accent bar
No visual boundary between sectionsSections separated only by thin dividersAdd section background tints or spacing
All icons same colorEvery icon is .secondary grayAssign semantic colors per section
Sections run together visuallyNo color or weight change at section boundariesAdd colored section headers or dividers

11e. Interactive vs. Static Contrast

Can users instantly identify which elements are tappable?

CheckWhat to Look ForFix
Buttons look like labelsNavigation rows with no chevron/color distinctionAdd .blue text or chevron indicator
Pickers look like static textPicker values in same color as labelsUse accent color for picker values
Destructive actions blend in"Clear History" looks like "Activity History"Use .red for destructive, accent for navigation
Toggle rows vs info rowsBoth look identical except for the toggleAdd subtle leading tint or icon color

11f. Opacity Consistency

Group elements by role and check if similar elements use matching opacities:

RoleElementsOpacities FoundConsistent?
SubtitlesRow descriptions, section footers70%, 85%, .secondaryNo — standardize to .secondary
BackgroundsSection tints, hover states6%, 8%, 45%Check if intentional variation
ShadowsCard shadows, text shadows10%, 20%, 30%, 35%Acceptable range
BordersCard borders, row separators15%, 20%, 30%Narrow to 2 values

Flag: Same-role elements with >20% opacity variance → "Inconsistent opacity"

11g. Semantic Drift

Does the same color mean different things in different parts of the view?

ColorLocation AMeaning ALocation BMeaning BDrift?
.blueSidebar"Own" phaseDashboardPrimary actionMinor
.orangeSidebar"Dispose Of" phaseDashboardImport/ExportYes — different semantic

Flag: Same color with clearly different meanings in adjacent or related areas.

11h. Light/Dark Mode Delta

For each element, note whether color/opacity changes between modes:

CheckWhat to Look ForFix
Hardcoded .white or .blackWon't adapt to mode switchUse .primary, .background, semantic colors
Hex colors without dark variantColor(hex: "#FFFFFF") in both modesUse Color(.systemBackground) or asset catalog
Shadows invisible in dark modeColor.black.opacity(0.1) disappearsUse adaptive opacity or colored shadows
Tints that wash outLight tints (5% opacity) invisible on dark backgroundsIncrease dark mode opacity (e.g., 3% light → 8% dark)

Automated Detection for Light/Dark Mode Delta

# Reuses Domain 7 checks — cross-reference here
# Find hardcoded white/black
grep -n "Color\.white\|Color\.black\|\.white)\|\.black)" <view_file>.swift | grep -v "//.*white\|//.*black"

# Find hex colors without dark variant
grep -n 'Color(hex:\|Color("#\|Color(red:' <view_file>.swift

# Find shadows using black with low opacity
grep -n "shadow.*\.black.*opacity.*0\.[0-1]" <view_file>.swift

# Find low-opacity tints that may wash out in dark mode
grep -n "\.opacity(0\.0[1-8])" <view_file>.swift

11i. Contrast Pairs (WCAG AA)

Check text-on-background combinations:

PairRatio NeededCommon Failures
Body text on background4.5:1.secondary on .systemGroupedBackground in light mode
White text on colored cards4.5:1White on .yellow or .cyan (low contrast)
Caption text on tinted backgrounds4.5:1.tertiary on subtle tints
Interactive text on background3:1 (large text).blue on dark backgrounds can be low

11j. Design System Compliance

Compare actual color usage against project rules:

  1. Read CLAUDE.md for palette restrictions (e.g., "never use green", "use AccessibleColor.sf3aYellow instead of .yellow")
  2. Read design system files for approved colors
  3. Flag any color not in the approved palette
  4. Flag any use of restricted colors

Automated Detection for Design System Color Compliance

# Step 1: Find restricted colors (project-specific — read CLAUDE.md first)
# Example: if green is forbidden:
grep -n "\.green\|Color\.green\|foregroundStyle(\.green)\|foregroundColor(\.green)" <view_file>.swift

# Example: if system .yellow should be sf3aYellow:
grep -n "\.yellow\b" <view_file>.swift | grep -v "sf3aYellow\|AccessibleColor\|semantic.*warning"

# Step 2: Find colors not in approved palette
# Extract all color names, compare against approved list from CLAUDE.md
grep -oh "\.\(blue\|red\|green\|yellow\|orange\|purple\|pink\|cyan\|teal\|indigo\|gray\|mint\|brown\)" <view_file>.swift \
  | sort -u

# Step 3: Find custom hex/RGB colors (may not be in palette)
grep -n "Color(red:\|Color(hex:\|Color(\"" <view_file>.swift

Safe patterns (do NOT flag):

// ✅ Colors used in conditional semantic contexts (status indicators per CLAUDE.md)
// ✅ .yellow used for semantic warning status (explicitly allowed per CLAUDE.md)
// ✅ sf3aYellow / AccessibleColor.sf3aYellow (approved replacement)

Adaptive View Profile

The View Profile is a persistent file that grows with each audit, enabling cross-view consistency checks for both color (Domain 11) and component usage (Domain 9). Stored at .agents/ui-enhancer-radar/view-profile.md.

On first audit of a project:

  1. Check for .agents/ui-enhancer-radar/view-profile.md — if it doesn't exist, create it
  2. Record the Color Inventory Table, opacity conventions, and semantic color map from this audit
  3. Record which shared components the view uses and which optional parameters it enables
  4. Note the project's palette rules from CLAUDE.md

On subsequent audits:

  1. Load the View Profile
  2. Color comparison: Flag views that deviate from established color/opacity conventions
  3. Component comparison: Flag views that don't enable features most sibling views use
  4. Update the profile with any new patterns discovered
  5. If a previously recorded convention has changed in the majority of views, update the convention (not the outlier)

View Profile format (.agents/ui-enhancer-radar/view-profile.md):

# UI Enhancer View Profile
*Last updated: [date] | Views audited: [count]*

## Project Palette
[Colors from CLAUDE.md or design system]

## Color Conventions
| Role | Standard Color | Standard Opacity | Views Using |
|---|---|---|---|
| Section header icon | Semantic per section | 100% | DashboardView, ToolsView |
| Row subtitle | .secondary | 100% | SettingsView, ItemDetailView |
| Card shadow | sectionColor | 20% resting | DashboardView |

## Semantic Color Map
| Color | Meaning | Consistent Across Views? |
|---|---|---|
| .blue | Primary actions, Own phase | Yes |
| .orange | Dispose Of, data flow | Yes |

## Component Usage
| Component | Parameter | Enabled In | Not Enabled In | Adoption |
|---|---|---|---|---|
| ContentIllustratedHeader | showThemeToggle | Dashboard, Tools, Reports, MyProducts, StuffScout, LegacyWishes | Settings, Archive | 75% |
| ContentIllustratedHeader | showHelp | Dashboard, Tools, Reports | Settings, Archive, MyProducts | 50% |
| ContentIllustratedHeader | solidBackground | Dashboard | All others | 12% (intentional — dashboard only) |
| SheetContainer | showHelp | AddItem, StuffScout, Backup | Restore, Export | 60% |

## Detected Patterns
| Pattern | Views Using | Views Missing | Notes |
|---|---|---|---|
| Keyboard Done toolbar | All form views | — | Universal |
| Pull-to-refresh | Dashboard, MyProducts | Reports, Archive | Data views only |
| Empty state handling | MyProducts, Dashboard | Loans, Locations | Gap — should add |

## Refinement History
| Date | View | Change | Kept? | Notes |
|---|---|---|---|---|
| 2026-03-22 | DashboardView | VStack spacing 24→16→12pt | Kept 12pt | User wanted tighter |
| 2026-03-22 | DashboardView | Solid header background | Kept | More punch in light mode |
| 2026-03-22 | DashboardView | Quick Stats collapsed padding -8pt | Kept | Closer to MY STUFF |

Refinement History records what was tried during the refinement loop (Phase 7f) — both kept and reverted changes. This serves two purposes:

  1. If the user returns and says "I liked the spacing we tried last time," the history shows what values were used
  2. It reveals patterns — if the user consistently asks for tighter spacing, future audits should start with tighter recommendations

Domain 12: iPad Sheet Sizing enumerate-required

Goal: For every .sheet(isPresented:) and .sheet(item:) call in the codebase, verify that tall presented content (Form / List / ScrollView) has been given an iPad sizing mechanism. Catches the caller-side blind spot that Domain 9's "Sheet pattern" check leaves — a sheet can use no house-style container AND no Apple sizing API, and still compile, pass all other domains, and quietly truncate to a floating ~540×620pt form sheet on iPad.

Why this is cross-file: the defect is a relationship between the caller (a .sheet { ... } closure somewhere) and the callee (the presented view's body). Neither file is wrong in isolation. Single-file grep cannot detect it; the domain needs to enumerate call sites, classify presented-view bodies, and check for any of four sizing mechanisms.

Reference: references/domain-12-ipad-sheet-sizing.md — full heuristic, exclusions list, finding format, acceptance criteria.

Recognized sizing mechanisms (any one of these satisfies the check):

  1. House-style container (SheetContainer or project equivalent) that applies sizing internally
  2. .presentationSizing(.page) on the sheet closure (iOS 18+)
  3. .presentationDetents([.large]) on the sheet closure (iOS 17 fallback)
  4. Project convenience modifier (e.g., .iPadPageSheet()) that wraps one of the above

Detection steps:

  1. Enumerate every .sheet(isPresented:|item:) call: grep -rn --include="*.swift" -E "\.sheet\(isPresented:|\.sheet\(item:" Sources/
  2. For each, identify the presented view's type (top-level expression in the closure)
  3. Read that view's body — skip if it starts with a recognized sizing container; skip if exclusions match (pickers, scanners, share sheets — see reference)
  4. Check if the sheet closure has any sizing modifier (.presentationSizing, .presentationDetents, project modifier)
  5. If the body is tall (Form / List / ScrollView at the top, or NavigationStack wrapping one of those) AND no sizing mechanism is present → flag

Project-convention awareness: learns house-style container + modifier names from .radar-suite/conventions.yaml if present, or from grepping CLAUDE.md / Sources/Views/Components/ for a View extension that applies presentationSizing(.page) or presentationDetents([.large]). Falls back to Apple-API-only checks if no convention is found.

Severity default: 🟢 MEDIUM (user-visible iPad UX regression, not a crash). Elevates to 🟡 HIGH when any of: app is actively iPad-facing, 5+ sites share the issue (systemic), or the presented view is a critical flow (add-item, settings root, legal/compliance sheet).

Borderline cases (NavigationStack { VStack { Header; mainContent } } where mainContent is a conditional tree) are flagged as medium-confidence with a one-line synopsis of the tallest branch — not batched into the high-confidence fix set by default.

Output row format (per finding):

File:lineState bindingPresented view (body shape)Why flaggedConfidenceSuggested fix
Sources/Views/Lists/RMAListView.swift:210$showingAddRMARMAFormView (NavigationStack { Form })Tall content, no sizing container, no sizing modifierHighAppend .iPadPageSheet() (project convention) or .presentationSizing(.page) (Apple default) inside the sheet closure

Exclusions (skip without flagging): Photos/document pickers, scanners, share sheets, confirmation dialogs, splash screens, chooser views, single-action confirmations. Full list in the reference file.

Do NOT delegate Domain 12 to Explore subagents. Run the grep + Read passes directly. Enumeration is the point; a subagent can silently drop findings.


Domain 13: Button Hit Region enumerate-required

Goal: For every Button { } label: { } call site, detect the three-factor interaction bug where .buttonStyle(.plain) + a manually drawn trailing chevron.right + Form/List context collapses the iPad hit region to the chevron alone. The rest of the row becomes visually tappable but functionally dead — users tap and nothing happens.

Why this is three-factor: no single factor is wrong. .buttonStyle(.plain) is legal. Manual chevrons are legal. Buttons inside Forms are legal. The interaction of the three — on iPad specifically — is what breaks. Single-file pattern matchers can't detect interactions.

Reference: references/domain-13-button-hit-region.md — full heuristic, exclusions, both fix options, acceptance criteria.

Detection steps:

  1. Enumerate every Button { } label: { } in Sources/ (skip Button("Text", action:) form — no custom label, no bug)
  2. Check label closure for manually drawn trailing chevron: Image(systemName: "chevron.right") or "chevron.forward" (ignore DisclosureGroup custom implementations)
  3. Check Button's modifier chain for .buttonStyle(.plain) or .buttonStyle(.borderless) — other styles don't have this bug
  4. Check for Form/List context: structural walk for Form { ... } / List { ... } ancestor, OR modifier indicators on the Button or its parent Section (.listRowBackground, .listRowInsets, .listRowSeparator)
  5. Check label closure for .contentShape(Rectangle()) or .contentShape(.rect) — if present, author explicitly fixed the hit region; skip
  6. When all four trigger and no content-shape escape, flag

Recommended fixes (offer both):

  • Fix A (preferred per HIG): remove the decorative chevron. Card modifiers (.actionCard(), .destructiveCard(), colored row backgrounds) already read as tappable. HIG says chevrons are for NavigationLinks, not Buttons.
  • Fix B: keep the chevron, add .contentShape(Rectangle()) on the label's outer HStack. Preserves visual treatment but guarantees hit region.

Severity: 🟡 HIGH default (user-visible interaction failure — button looks broken). Elevates to 🔴 CRITICAL for buttons on critical paths (add-item, save, export, payment), systemic cases (5+ sites in same view hierarchy), or accessibility failures confirmed via VoiceOver.

Exclusions:

  • NavigationLink { } label: — chevrons here are system-managed
  • DisclosureGroup — chevron is a control, intentional
  • .buttonStyle(.bordered), .borderedProminent, or any system style other than .plain / .borderless
  • Toolbar item Buttons
  • macOS-only views (#if os(macOS) wrapping the whole body)
  • Animated expand/collapse chevrons (.rotationEffect(.degrees(isExpanded ? 90 : 0)))

Output row format:

File:lineButton actionLabel synopsisButton styleContextFix A (preferred)Fix B
Sources/Features/Settings/Views/BackupDataSheet.swift:193createBackup()icon + 2-line text + chevron.plainSection in Form via SheetContainerRemove chevronAdd .contentShape(Rectangle())

Pattern sweep follow-up: this bug is rarely isolated. After a first fix, grep the full codebase for the same shape (same card modifier + same chevron + same button style). Offer batch application to consistent patterns.

Do NOT delegate Domain 13 to Explore subagents. Three-factor reasoning requires holding the full label closure, the modifier chain, and the Form/List context simultaneously. A subagent can lose one of the three factors and produce either false positives or false negatives.


Domain 14: Silent Picker enumerate-required

Goal: For every Picker(...) call site, detect the two-factor interaction bug where a menu-style Picker with no explicit .pickerStyle(...) sits in a custom (non-Form/List) container — a DisclosureGroup body, a ScrollView+VStack screen, or a card with cleared row chrome. The picker renders as an enabled pop-up button but its menu never presents on tap. The user taps and nothing happens; the accessibility tree reports a healthy AXPopUpButton. No crash, no warning, no anti-pattern token.

Why this is two-factor: a style-less Picker is idiomatic. Custom containers are legal. The interaction — automatic menu presentation inside cleared-row-chrome / non-Form containers — is what fails, at runtime only.

Cousin of Domain 13, not a duplicate: both yield a control that's visually present and accessibility-enabled but dead on tap. But Domain 13 is Button + chevron + .buttonStyle(.plain) collapsing the hit region (tap misses); Domain 14 is Picker + no .pickerStyle + custom container failing to present the menu (tap lands, nothing opens). Domain 13's detector greps Button/chevron/buttonStyle and walks straight past Picker — which is exactly why this shipped with Domain 13 already in place.

Reference: references/domain-14-silent-picker.md — full heuristic, container classification, false-positive list, on-device verify protocol, acceptance criteria.

Detection steps:

  1. Enumerate every Picker(...) in Sources/ (both Picker("Title", selection:) and Picker(selection:) { } label: { } forms)
  2. Skip any Picker with an explicit .pickerStyle(...) of any kind — the style is the fix
  3. Reject DatePicker / ColorPicker / PhotosPicker false positives (the bare Picker( grep matches them)
  4. Classify the container: genuine Form/List row with intact chrome → skip; DisclosureGroup body, ScrollView+VStack screen, or cleared-row-chrome / custom-card context → candidate
  5. Emit as a ⚠️ candidate, NOT a confirmed bug — static analysis proves the risk, not the deadness

Candidates require on-device verification. Build, run, tap each flagged picker. Menu opens → false positive (some custom containers do present correctly), note and leave. Menu does not open → confirmed, apply Fix A.

Recommended fixes (offer both):

  • Fix A (preferred): add .pickerStyle(.menu) — one line, lowest risk, verified to restore presentation.
  • Fix B (only for nested tiers): rebuild on Menu { Button… } label: { … } — needed only when a picker reveals a sub-picker (two-tier), which Picker can't express cleanly.

Severity: 🟡 HIGH default (control ignores taps — reads as broken). Elevates to 🔴 CRITICAL on critical paths (add-item, edit-item, save, payment) — a dead control in the primary add/edit flow is a textbook App Store Guideline 2.1 rejection ("a control that does nothing") — or when 3+ pickers in one view hierarchy share the pattern.

Exclusions:

  • Any explicit .pickerStyle(...) (.menu, .segmented, .wheel, .inline, .radioGroup, custom)
  • Picker in a genuine Form/List row with intact chrome
  • DatePicker, ColorPicker, PhotosPicker
  • Already rebuilt on Menu { }

Output row format:

File:linePickerStyleContainerStatusFix AFix B
Sources/Views/Forms/WarrantyFormView+BasicSections.swift:193"Acquired From"none (menu)DisclosureGroup/VStack, cleared chrome⚠️ verify on device+ .pickerStyle(.menu)Menu { } rebuild

Pattern sweep follow-up: this bug travels in packs (origin case: 7 sites across 4 files). After the first confirmed fix, sweep for the same shape: Picker + no .pickerStyle + (cleared-chrome modifier OR DisclosureGroup body OR ScrollView/VStack screen).

Do NOT delegate Domain 14's candidate confirmation to static reasoning. The container classification can be delegated, but "is the menu actually dead?" requires a real tap on a real device/sim. Emit candidates; verify on-device before asserting any picker is broken.


Phase 6: Generate Report

Report Structure

## UI Enhancer Report: [ViewName]
*UI Enhancer v[version] | [date]*

### Focus: [from interview — e.g., "General polish" or "Specific: too much wasted space"]

### Screenshot Analysis
- Content-to-chrome ratio: X%
- First content element at: Ypt from top
- Primary action visibility: [Obvious / Hidden / Ambiguous]
- 3-second test: [Pass / Fail — what draws the eye]

### Findings

| # | Domain | Finding | Severity | Effort | Recommendation |
|---|--------|---------|----------|--------|---------------|
| 1 | Space | 45% of screen is header chrome | Critical | Medium | Merge photo into title row |
| 2 | Hierarchy | Date text dominates over item title | High | Small | Replace with status circle |
| 3 | Dark Mode | 3 hardcoded Color.white references | Medium | Trivial | Replace with semantic colors |
| ... | ... | ... | ... | ... | ... |

### UX Score

| Domain | Score | Notes |
|--------|-------|-------|
| 1. Space Efficiency | 4/10 | Content starts at 410pt |
| 2. Visual Hierarchy | 6/10 | Date text too dominant |
| 3. Information Density | 7/10 | Good density, some redundancy |
| 4. Interaction Patterns | 5/10 | Combined buttons, gesture-only deletes |
| 5. Accessibility | 8/10 | Good labels, some fixed fonts |
| 6. HIG Compliance | 7/10 | Minor deviations |
| 7. Dark Mode | 6/10 | Some hardcoded colors |
| 8. Performance Impact | 8/10 | Clean structure |
| 9. Design System Compliance | 7/10 | Mostly compliant |
| 10. Competitive Comparison | N/A | (skipped — no competitor screenshot provided) |
| 11. Color Audit | 5/10 | Monochromatic sections, inconsistent opacities |
| 12. iPad Sheet Sizing | 9/10 | Uses .iPadPageSheet() convention |
| 13. Button Hit Region | 6/10 | One .plain + chevron in Form section needs .contentShape(Rectangle()) |
| 14. Silent Picker | 7/10 | One menu Picker in a DisclosureGroup card lacks .pickerStyle(.menu) — verify on device |
| **Overall** | **6.4/10** | (Domain 10 excluded from average when N/A) |

### Before/After ASCII Mockup

BEFORE:
┌─────────────────────────┐
│ [Nav Bar]               │
│ [Photo Row - 56pt]      │
│ [Title Row - 64pt]      │
│ [Tab Chips - 40pt]      │
│ [Action Buttons - 32pt] │
│ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ [Content starts here]   │
│ ...                     │
└─────────────────────────┘
Content ratio: 39%

AFTER:
┌─────────────────────────┐
│ [Nav Bar]               │
│ [Photo+Title - 64pt]    │
│ [Chips + Buttons - 40pt]│
│ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ [Content starts here]   │
│ ...                     │
│ ...                     │
│ ...                     │
└─────────────────────────┘
Content ratio: 63%

### Space Budget (Before/After)

| Metric | Before | After | Delta |
|--------|--------|-------|-------|
| Header chrome | [N]pt | [N]pt | -[N]pt |
| Search/filters | [N]pt | [N]pt | -[N]pt |
| Section headers (×[count]) | [N]pt | [N]pt | -[N]pt |
| Content starts at | [Y]pt | [Y]pt | -[N]pt |
| Content-to-chrome ratio | [X]% | [X]% | +[N]% |
| Sections | [N] | [N] | -[N] |
| Feature cards/buttons | [N] | [N] | -[N] |

**Net space recovered:** ~[N]pt

### Implementation Priority

**Quick Wins (do first):**
1. [Finding] — [exact code change]

**Medium Effort:**
2. [Finding] — [approach]

**Nice-to-Have:**
3. [Finding] — [approach]

Phase 6b: Content & Identity Preservation Check (MANDATORY before removing UI)

When any finding recommends removing or replacing a UI element, check whether it contained informational text OR visual identity elements that serve a purpose beyond decoration.

What to check

For each element being removed, ask:

  • Does it contain explanatory text (descriptions, subtitles, instructions)?
  • Does it contain status information (counts, states, labels)?
  • Would a first-time user lose context about what this screen does?
  • Does it contain branding elements (app icon, section icon, colored backgrounds) that establish visual identity?
  • Does it contribute to visual consistency across the app (same component used in multiple views)?
  • Is it tagged [PRESERVE] from the Design Intent interview?

If text content would be lost, the text must be relocated, not deleted. Propose one of these options to the user:

If visual identity would be lost (icons, colors, branded backgrounds), route to Phase 6c (Element Compaction) instead of removing — compaction preserves identity at reduced size.

Relocation options

OptionWhen to useExample
Help button (?)Descriptive text that experienced users don't needToolbar ? button → popover with description
Info icon (i)Context that's useful but not essential to scanSmall ℹ️ next to a title, expands on tap
First-visit onlyOnboarding text that should disappear after learningShow once via @AppStorage, hide after first visit
Nav bar subtitleShort taglines (< 40 chars).navigationSubtitle("Identify & Appraise")
Tooltip / help textSecondary info on macOS.help("Identify and value antiques...")
Keep as-isThe text is truly decorative and losing it is fineMarketing copy repeated elsewhere

How to present

After listing findings but before implementation, flag any content at risk:

questions:
[
  {
    "question": "Removing [element] would also remove the text '[text]'. Where should this information go?",
    "header": "Content",
    "options": [
      {"label": "Drop it (Recommended)", "description": "The text isn't needed — users understand from context"},
      {"label": "Help button (?)", "description": "Add a toolbar help button that shows this on tap"},
      {"label": "First-visit only", "description": "Show on first use, hide after"},
      {"label": "Keep the element", "description": "Don't remove this element after all"}
    ],
    "multiSelect": false
  }
]

If "Keep the element" — remove that finding from the playbook and adjust space savings estimates.

Skip this check when

  • The removed element contained only a title that's already in the nav bar
  • The removed element contained only color/icon decoration with no text
  • The text is displayed elsewhere on the same screen (e.g., in a banner below)

Phase 6c: Element Compaction (MANDATORY when recommending removal of visual elements)

When a finding recommends removing a decorative or branding element for space efficiency, the user may want to preserve the element's visual identity at a smaller footprint. Always offer compaction as an alternative to removal.

Cross-View Consistency Check (run before compaction decisions)

Before recommending removal or compaction of any visual element, check whether it's part of a cross-view pattern:

  1. Grep the codebase for the component name (e.g., ContentIllustratedHeader, SheetHeader, custom component names)
  2. Count how many views use the same component
  3. If used in 3+ views, flag it as a consistency pattern:
⚠️ Cross-view pattern detected: [ComponentName] is used in [N] views:
  - ViewA.swift (line X)
  - ViewB.swift (line Y)
  - ViewC.swift (line Z)

Removing it from this view would break visual consistency.
Recommendation: Compact (not remove), or apply the change across all [N] views.

If a consistency pattern is detected:

  • Default to Compact instead of Remove
  • If the user chooses Remove, warn: "This will make [ViewName] visually inconsistent with [N] other views that use [ComponentName]. Apply the same change to all, or just this one?"
  • Offer: "Apply to all [N] views" / "Just this view" / "Cancel"

When to trigger

This check runs when ANY finding recommends removing:

  • Illustrated headers (ContentIllustratedHeader, custom banners)
  • Branded sections with icons, backgrounds, or imagery
  • Photo rows, hero images, or visual feature cards
  • Any element the user may consider part of the view's visual identity
  • Any element tagged [PRESERVE] during the Design Intent interview

What to ask

For each element recommended for removal, present compaction as the default:

questions:
[
  {
    "question": "The [element] uses ~[N]pt. How would you like to handle it?",
    "header": "Element",
    "options": [
      {"label": "Compact (Recommended)", "description": "Preserve visual identity at reduced size (~[M]pt savings)"},
      {"label": "Remove entirely", "description": "Maximum space savings (~[N]pt recovered)"},
      {"label": "Keep as-is", "description": "No change to this element"}
    ],
    "multiSelect": false
  }
]

Compaction techniques by element type

Element TypeFull SizeCompaction TechniquesTarget Size
Illustrated header (icon + title + subtitle + background)~100-140ptInline icon (28pt) + title only, reduce background height, drop subtitle~44-56pt
Section header (decorative circle + title)~40-48ptSmaller circle (18pt), reduce font, tighten padding~28-32pt
Photo/hero row~80-120ptThumbnail (40pt) inline with title instead of full-width~44pt
Status banner/card~60-80ptCompact badge or chip instead of card~28-36pt
Tip/hint section~60-100ptCollapsible disclosure, or single-line with (i)~20-44pt
Feature card~80-120ptReduce padding, smaller icon, tighter text~48-64pt

How to generate compact code

When "Compact" is selected, apply these reductions in order until target height is reached:

  1. Reduce icon size — e.g., 48pt → 28pt, 32pt → 20pt
  2. Inline layout — switch from VStack to HStack where possible
  3. Drop secondary text — remove subtitles, taglines, descriptions (relocate per Phase 6b if needed)
  4. Tighten spacing — reduce padding and VStack/HStack spacing by 30-50%
  5. Reduce background — shrink or remove decorative backgrounds, keep accent color as border or tint
  6. Simplify — remove shadows, reduce corner radius, flatten visual layers

Playbook format for compaction

When compaction is chosen, the playbook entry should show the before/after with measurements:

### Fix #N: Compact [element name]

**File:** `Sources/Views/[file].swift`
**Lines:** [range]

**Before:** (~[N]pt height)
[exact code block]

**After:** (~[M]pt height, [savings]pt saved)
[exact replacement code — compacted version]

**Why:** Preserves visual identity while reclaiming [savings]pt of vertical space

**Test:** Verify element is visually recognizable at smaller size on iPhone SE and Pro Max

When NOT to compact

  • The element is purely redundant (same title shown in nav bar AND header AND banner) — removal is better
  • The element cannot be meaningfully reduced (already near minimum viable size)
  • The user explicitly chose "Remove entirely"

Phase 6d: Visual Compensation Check (MANDATORY when removing visual elements)

When findings remove headers, icons, colored elements, or decorative components, the result may look visually flat. Before implementing, check whether the remaining UI needs visual enrichment to compensate.

When to trigger

This check runs when ANY finding:

  • Removes a header component (SheetHeader, ContentIllustratedHeader, custom headers)
  • Removes colored backgrounds, accent bars, or decorative elements
  • Consolidates multiple visual sections into fewer elements
  • Strips icons or imagery from the view

What to ask

questions:
[
  {
    "question": "Removing [element] will reduce visual richness. How would you like to compensate?",
    "header": "Visual",
    "options": [
      {"label": "Colored section headers (Recommended)", "description": "Add colored icon circles to section headers for visual anchoring"},
      {"label": "Per-section accent colors", "description": "Use project palette to differentiate sections (icons, borders, or backgrounds)"},
      {"label": "Both \u2014 headers + accents", "description": "Full treatment: colored header icons + per-section accent colors from project palette"},
      {"label": "No compensation needed", "description": "The view looks fine without it"}
    ],
    "multiSelect": false
  }
]

Compensation techniques (by view type)

View TypeBest CompensationWhy
Help / referenceColored icon circles in section headersProvides visual anchoring without distracting from linear reading
Dashboard / overviewColored card backgrounds + accent barsScanning views benefit from strong visual differentiation
Form / inputSubtle section tints or header iconsKeep focus on inputs, use color sparingly
Detail / inspectorAccent bars on cards + status colorsHelp users scan for specific information
List / tableAlternating row tints or leading color indicatorsHelp distinguish items at a glance

Color palette for compensation

Always check the project's design system first. Before applying any colors:

  1. Read CLAUDE.md for documented color rules or palette restrictions
  2. Search for design system files (DESIGN_SYSTEM.md, StyleGuide.swift, Colors.swift, Theme.swift)
  3. Check for existing color constants or enums in the codebase (grep for static let, Color(, UIColor()

If a project palette exists: Use only colors from that palette. Follow any restrictions (e.g., "never use green", "use semantic colors only"). Reference the project's color constants in code, not raw SwiftUI colors.

If no project palette exists: Use this default set, which provides good contrast and variety across common forms of color vision:

ColorUse for
BluePrimary, required, actions
PurpleSecondary, optional, analysis
Teal/CyanTools, utilities, coverage
OrangeMedia, images, discovery
PinkSupport, resources, special
YellowTips, highlights, warnings
GrayNotes, neutral, settings

When to skip

  • The view is already visually rich after removal (e.g., content itself has color/imagery)
  • Only minor chrome was removed (a single label or small spacer)
  • The user explicitly chose "No compensation needed"

Phase 7: Implementation Playbook

MANDATORY: Before ANY code edits, complete Phase 7a (User Commit Offer), Phase 7b (Visual Inspection Gate), and Phase 7c (Guided Visual Review). These are NOT optional. Skipping them means making blind changes to visual UI.

For each finding, generate the exact code change — not just a description.

Playbook Entry Format

### Fix #1: [Finding title]

**File:** `Sources/Views/Detail/EnhancedItemDetailView.swift`
**Lines:** 180-265

**Before:**
[exact code block to replace]

**After:**
[exact replacement code]

**Why:** [one sentence explaining the UX improvement]

**Test:** [what to verify after applying]

Offer to apply changes:

questions:
[
  {
    "question": "How would you like to apply these changes?",
    "header": "Implement",
    "options": [
      {"label": "Apply all (Recommended)", "description": "Implement all fixes with keep/revert after each"},
      {"label": "Quick wins only", "description": "Only Trivial/Small effort changes"},
      {"label": "One at a time", "description": "Apply and verify each fix individually"},
      {"label": "Save playbook", "description": "Save to file, implement later"}
    ],
    "multiSelect": false
  }
]

Phase 7a: User Commit Offer (MANDATORY — execute before ANY edits)

This phase is NOT optional. Do NOT skip it. Do NOT silently create checkpoints. ASK the user.

First, check if git is available by running git rev-parse --is-inside-work-tree. If git is available, offer the git-based options. If not, offer file-based alternatives.

If git IS available:

questions:
[
  {
    "question": "Before making changes, would you like to commit your current work? This creates a clean revert point.",
    "header": "Safety",
    "options": [
      {"label": "Commit first (Recommended)", "description": "Commit uncommitted changes so you can revert cleanly"},
      {"label": "Skip — working tree is clean", "description": "No uncommitted work to protect"},
      {"label": "Skip — I'll manage git myself", "description": "Proceed without committing"}
    ],
    "multiSelect": false
  }
]

If "Commit first":

  1. Run git status to show what will be committed
  2. Stage and commit with message: chore: checkpoint before ui-enhancer-radar changes
  3. Confirm the commit hash to the user

If "Skip — working tree is clean": Verify with git status --porcelain. If there ARE uncommitted changes, warn and re-ask.

If "Skip — I'll manage git myself": Proceed without committing.

If git is NOT available:

questions:
[
  {
    "question": "No git repository detected. Before making changes, would you like to back up the files that will be modified?",
    "header": "Safety",
    "options": [
      {"label": "Back up files (Recommended)", "description": "Copy each file to a .backup before editing"},
      {"label": "Skip — I have my own backups", "description": "Proceed without backup"},
      {"label": "Save playbook only", "description": "Show the changes but don't apply — I'll do it manually"}
    ],
    "multiSelect": false
  }
]

If "Back up files": For each file that will be modified, create a copy: cp file.swift file.swift.backup List the backup files created so the user can find them.

If "Save playbook only": Generate the full playbook with before/after code blocks, but do not apply any edits. The user applies changes manually in Xcode.

Revert behavior without git:

  • "Revert this fix" → restore from .backup file
  • "Revert all" → restore all .backup files
  • After the audit completes successfully, offer to clean up .backup files

Phase 7b: Visual Inspection Gate (MANDATORY — blocks ALL code changes)

⚠️ CRITICAL WARNING: NEVER modify UI code based solely on code analysis.

Code analysis can count colors, measure spacing values, and detect structural patterns — but it CANNOT tell you whether a view actually looks wrong. A view with 3/5 blue icons might look perfectly fine because the form layout separates them visually. A view with "excessive" padding might feel exactly right on a real device.

Every UI change must be validated by a human looking at the actual view. This is non-negotiable. The user must visually inspect the view before ANY finding is approved or rejected.

Why This Gate Exists

Without visual inspection, the skill will:

  • Fix "problems" that aren't actually visible to users
  • Make changes that look worse than the original
  • Waste time on micro-optimizations that don't matter on a real screen
  • Create a "revert everything" scenario because changes were made blind

How It Works

Before applying ANY code changes from the playbook, block on user visual inspection.

Present this prompt (do NOT skip, do NOT auto-proceed):

questions:
[
  {
    "question": "Before making changes, you need to see the actual view. How are you viewing it?",
    "header": "Inspect",
    "options": [
      {"label": "Xcode Canvas (Recommended)", "description": "Open the file in Xcode — Canvas shows the view live. Fastest feedback loop."},
      {"label": "Running in Simulator", "description": "App is running in Simulator — I can navigate to the view"},
      {"label": "Running on device", "description": "App is running on a physical device"},
      {"label": "I'll view it later", "description": "Save the findings as a playbook — I'll apply changes when I can see the view"},
      {"label": "Explain pros/cons", "description": "Why does visual inspection matter for this?"}
    ],
    "multiSelect": false
  }
]

If "Xcode Canvas": Confirm: "Open [FileName].swift in Xcode. The Canvas panel (right side) should show the view. If Canvas isn't visible, press Opt+Cmd+Return. Reply when you can see it."

If "Running in Simulator" or "Running on device": Confirm: "Navigate to [ViewName] in the app. Reply when you can see it."

If "I'll view it later": Save the full playbook to .agents/ui-enhancer-radar/[date]-[view]-playbook.md. Do NOT apply any code changes. Print:

📋 Playbook saved. Run `/ui-enhancer-radar` on this view again when you can see it.
   No code changes were made.

Then skip Phases 7c-7e entirely and go to Phase 9 (Summary).

If "Explain pros/cons": Explain briefly, then re-prompt with the same options (minus Explain).

Gate Rule

Do NOT proceed to Phase 7c until the user confirms they can see the view. There is no bypass. There is no "trust the code analysis" option. If the user cannot view the screen right now, the correct action is to save the playbook for later.


Phase 7c: Guided Visual Review (MANDATORY — with user looking at the view)

This is the core of the visual audit. The user is looking at the actual view. Walk through each finding collaboratively, then collect any additional issues the user spots.

Part 1: Walk Through Recommended Changes

For each finding in the playbook, present it as a question while the user is looking at the view:

Finding #[N]: [description]

Look at [specific element] on your screen.
[Describe what to look for — e.g., "Notice the three blue icons in a row: Appearance, Privacy, and iCloud. Do they blend together?"]

What do you think?
1. **Fix this** — [brief description of the change]
2. **Compact instead** — [if applicable: preserve visual identity at smaller size]
3. **Skip** — it looks fine on screen, leave it
4. **Explain pros/cons** — walk through the tradeoff before deciding

Key behavioral rules:

  • Describe what to LOOK FOR, not what's "wrong." Let the user's eyes decide if it's actually a problem.
  • Accept "Skip" gracefully. Code analysis flagged it, but the user's eyes override. Mark as Accepted with reason "Looks fine on screen per user inspection."
  • Never argue if the user says it looks fine. They are literally looking at it. You are not.
  • Be specific about which element to examine — don't say "check the colors"; say "look at the three icons next to Appearance, Privacy, and iCloud."

Part 2: User-Spotted Issues (MANDATORY — always ask)

After walking through all recommended findings, always ask:

questions:
[
  {
    "question": "Now that you're looking at [ViewName], do you see anything else you'd like to change? Things code analysis might miss — alignment, spacing feel, visual weight, element sizing, color choices.",
    "header": "Your eyes",
    "options": [
      {"label": "Yes, I see some things", "description": "I'll describe what I'd like to change"},
      {"label": "No, looks good", "description": "The recommended changes cover everything"},
      {"label": "Actually, let me look at dark mode too", "description": "Toggle to dark mode before deciding"}
    ],
    "multiSelect": false
  }
]

If "Yes, I see some things": Let the user describe in free text. For each item they mention:

  1. Evaluate against design rules (push-back if needed — see Push-back guidance below)
  2. Add to the approved changes list
  3. Generate the playbook entry

If "Actually, let me look at dark mode too": Wait for user to toggle. Then re-ask about both light and dark mode issues.

What This Phase Produces

A final approved list of changes — combining:

  • Recommended findings the user confirmed (from Part 1)
  • User-spotted issues (from Part 2)
  • With all "Skip" items removed

This list is the input to Phase 7d.


Phase 7d: Apply Approved Changes (execute after visual review)

Apply ONLY the changes approved in Phase 7c. One at a time, with keep/revert after each.

Per-Change Flow

For each approved change:

  1. Apply the fix (Edit tool)
  2. If testable (accessibility fix, dead code removal, component wiring change) — write a test. If purely visual (spacing, color, layout reorder) — skip the test.
  3. Show what changed (brief description + files modified)
  4. Direct user to check the view:

"Check [ViewName] in [Canvas/Simulator/device]. The [element] should now [description of visible change]."

  1. Ask:
questions:
[
  {
    "question": "Fix #N applied: [description]. How does it look?",
    "header": "Review",
    "options": [
      {"label": "Keep", "description": "Looks good, move to next fix"},
      {"label": "Compact instead", "description": "Revert removal, apply compacted version preserving visual identity"},
      {"label": "Revert this fix", "description": "Doesn't look right — undo this change, continue with others"},
      {"label": "Revert all", "description": "Undo everything back to checkpoint"},
      {"label": "Stop here", "description": "Keep changes so far, skip remaining fixes"}
    ],
    "multiSelect": false
  }
]

If "Keep": After each kept fix that removes a reference to a component/property/array, check for dead code:

  1. Grep the codebase for the removed reference
  2. If the definition is now unreferenced, flag it: "The definition of [name] at [file:line] is now unused. Clean up?"
  3. If the user confirms, remove the dead definition

If "Compact instead":

  1. Revert: git checkout -- [files modified by this fix]
  2. Apply compaction (Phase 6c techniques)
  3. Direct user to check the view again
  4. Re-ask with Keep / Revert

If "Revert this fix":

git checkout -- [files modified by this fix]

Continue to next fix.

If "Revert all":

git checkout -- [all files modified by ui-enhancer-radar in this session]

Report: "All UI Enhancer changes reverted."

If "Stop here": Skip remaining fixes. Offer to save remaining playbook entries.

Batch Revert Command

Available anytime: /ui-enhancer-radar revert

  1. Shows files changed during the session
  2. Asks for confirmation (Revert all / Show diff first / Cancel)

Safety Rules

  1. Never force-push — revert only affects local changes
  2. Never revert past user commits — only revert ui-enhancer-radar edits
  3. No revert if already pushed — warn and suggest git revert instead

Phase 7e: Pattern Sweep — Similar View Queue (after applying changes to one view)

After all approved changes are applied to a view, check if similar patterns exist in other views. Pre-generate tailored recommendations for each similar view, but require visual inspection before presenting or applying them.

Why pre-generate but gate on visual inspection

Code analysis CAN reliably detect structural similarity (same component, same color pattern, same layout issue). What it CANNOT do is tell you whether the fix from View A makes sense in View B — the context may be different. So:

  • Pre-generate: Read each similar view's code, adapt the original fix to its specific structure, and prepare tailored recommendations. This is "thinking" work — safe to do without seeing the view.
  • Gate on viewing: Present the tailored recommendations ONLY after the user can see the view. The user validates whether the recommendations make visual sense in this context.

This means the skill does the work upfront, and the user just validates — efficient without being blind.

When to trigger

After Phase 7d completes (all approved changes applied or stopped), and at least one change was kept.

Step 1: Find similar views and pre-generate recommendations

For each type of change that was applied:

  1. Build a grep query from the change (e.g., if you changed .blue to .purple on a Privacy icon, search for other views with the same component/pattern)

  2. Search all view files in Sources/

  3. For each matching view, read the code and generate specific recommendations adapted to that view's structure. Don't just copy the original fix — account for differences:

    • Different number of sections/icons
    • Different semantic meanings (Privacy in one view vs. Network in another)
    • Different component parameters enabled
    • Different layout structure that may not need the same change
  4. Present the queue with the full rating table:

Pattern: [description — e.g., "monochromatic blue icons in settings-style views"]

I found [N] views with the same pattern. I've read each one and prepared
specific recommendations based on what we changed in [original view]:

| # | View | Pattern Match | Tailored Recommendation | Severity |
|---|------|--------------|------------------------|----------|
| 1 | PrivacyNetworkView | 5/7 icons .blue | Change Network→cyan, VPN→purple, Cache→orange, keeping Privacy→blue | HIGH |
| 2 | CloudSyncView | 3/4 icons .blue | Change Zones→purple, Status→cyan, keeping Sync→blue | MEDIUM |
| 3 | NotificationSettingsView | 2/4 icons .blue | Minor — only 2 adjacent blues. Change Schedule→orange | LOW |

Then ask:

questions:
[
  {
    "question": "[N] similar views found. Walk through them one at a time? You'll view each before any changes.",
    "header": "Queue",
    "options": [
      {"label": "Start the queue (Recommended)", "description": "Open each view, review tailored recommendations, apply what looks right"},
      {"label": "Defer all", "description": "Add to DEFERRED.md for a future visual inspection session"},
      {"label": "Accept as-is", "description": "These views are fine — the pattern doesn't bother me elsewhere"},
      {"label": "Explain pros/cons", "description": "Walk through why consistency matters across views"}
    ],
    "multiSelect": false
  }
]

There is no "Fix all now" option. Every view requires visual inspection. Batch-applying visual changes across multiple views without looking at them is exactly what this skill is designed to prevent.

Step 2: Walk through the queue (one view at a time)

For each view in the queue:

2a. Direct user to open the view:

"Open [ViewName] in [Canvas / Simulator / device]. [Brief description of what the view shows — e.g., 'This is the privacy settings form with network, VPN, and cache sections.']"

Wait for user to confirm they can see it.

2b. Present tailored recommendations:

Once the user confirms, present the pre-generated recommendations for THIS specific view:

Based on what we changed in [original view], here's what I'd recommend for [this view]:

1. [Specific change — e.g., "Change Network section icon from .blue to .cyan"]
   Look at [specific element]. Does the blue blend with adjacent sections?

2. [Specific change — e.g., "Change VPN section icon from .blue to .purple"]
   Look at [specific element]. Would purple better distinguish this section?

Do you see the same issues here?

Then ask:

questions:
[
  {
    "question": "[ViewName]: [N] recommendations. How does it look?",
    "header": "Review",
    "options": [
      {"label": "Apply all", "description": "All recommendations look right for this view"},
      {"label": "Apply some", "description": "I'll tell you which ones to apply and which to skip"},
      {"label": "Skip this view", "description": "It looks fine as-is — move to next view"},
      {"label": "I see other things too", "description": "Apply recommendations + I'll add my own changes"},
      {"label": "Stop the queue", "description": "Done with similar views — keep remaining as-is or defer"}
    ],
    "multiSelect": false
  }
]

If "Apply all": Apply changes, direct user to verify visually, then Keep/Revert per Phase 7d flow.

If "Apply some": User specifies which. Apply only those.

If "I see other things too": Apply recommendations, then collect user-spotted issues (same as Phase 7c Part 2). This is valuable — the user is already looking at the view, so capture everything.

If "Skip this view": Mark as Accepted ("Looks fine on screen per user inspection"). Move to next view in queue.

If "Stop the queue": Ask whether remaining views should be Deferred (tracked) or Accepted (closed).

Step 3: Queue progress

After each view in the queue, print a mini progress banner:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 Similar views: [completed]/[total]
   [ViewA] ✅ Fixed | [ViewB] ✅ Skipped | [ViewC] ⏳ Next
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

What NOT to sweep

  • Changes that were specific to one view's unique layout (not a pattern)
  • Refactoring changes (sheet router enum) — these are per-view architectural decisions
  • Changes the user "Skip"ped during visual review — if they said it looks fine in the original view, don't flag the same thing elsewhere
  • Views the user already audited in this session — don't re-queue them

Phase 7f: Refinement Loop (after all changes applied)

After all findings are applied (or stopped early), offer the user a chance to refine the result. This is where users request tweaks like "add a background tint," "make the icon bigger," or "change the color."

When to trigger

After the last change in Phase 7d is resolved (Keep / Stop here), ask:

questions:
[
  {
    "question": "All changes applied. Would you like to refine anything?",
    "header": "Refine",
    "options": [
      {"label": "Done (Recommended)", "description": "Changes look good, move to tests"},
      {"label": "Refine an element", "description": "Tweak a specific element (color, size, spacing, background)"},
      {"label": "Compare before/after", "description": "See what changed before deciding"},
      {"label": "Question or new direction", "description": "Ask about the changes, suggest a different approach, or redirect"}
    ],
    "multiSelect": false
  }
]

Refinement flow

If "Refine an element":

  1. Ask the user what they want to change (free-form or screenshot)
  2. Evaluate the request against the project's design system and CLAUDE.md before implementing
  3. Apply the refinement
  4. Show the result and re-ask with Keep / Revert / Refine again

Push-back guidance (MANDATORY)

Before implementing any refinement, check it against design rules. Push back when a refinement would:

ViolationExamplePush-back response
Break colorblind safety"Make it green""Green isn't in the colorblind-safe palette — it's confused with red by ~8% of users. How about cyan or blue instead?"
Break color palette"Use hot pink""That color isn't in the project's design system. The closest approved colors are pink (.pink) or purple (.purple). Which works?"
Violate Dynamic Type"Use .system(size: 11)""Fixed font sizes don't scale with Dynamic Type. Use .caption or .footnote instead for the same visual size with accessibility support."
Break visual consistency"Remove the icon circle""[ComponentName] uses icon circles in [N] other views. Removing it here would break consistency. Resize instead?"
Undo the audit's improvement"Put the header back to full size""That would restore ~[N]pt of the space we just recovered. If you want the branding back, compact is the middle ground — it preserves identity at half the height."
Break HIG compliance"Make the touch target 30pt""Apple HIG requires 44pt minimum touch targets. I can make it visually 30pt with a 44pt tap area using .contentShape()."
Exceed space budget"Add a subtitle and description""Adding that text would push content below the fold. Consider putting it behind a disclosure or (?) button instead."
App Store rejection riskSee table belowFlag with "App Store risk" and cite the specific guideline

App Store Review guardrails

UI changes that are known to trigger App Store rejections. Flag these BEFORE implementing:

RiskGuidelineWhat triggers itPush-back response
Missing accessibility2.1 (Performance)Removing .accessibilityLabel(), VoiceOver support, or Dynamic Type"Removing this accessibility label could trigger rejection under guideline 2.1. VoiceOver users won't be able to interact with this element."
Non-functional UI2.1 (Performance)Adding buttons/links that do nothing, placeholder UI, "coming soon" labels"Empty buttons or 'coming soon' UI can trigger rejection. Either wire it up or remove it."
iPad layout broken2.4.1 (Multitasking)Hardcoding widths that break iPad split view, removing landscape support"This layout would break on iPad multitasking. Use adaptive layout (ViewThatFits, size classes) instead of fixed widths."
Missing back/dismiss4.0 (Design)Removing dismiss buttons from sheets, creating navigation dead ends"Removing this dismiss button would create a dead end — users can't escape. App Review flags this under guideline 4.0."
Misleading UI2.3.1 (Accurate)Icons/labels that suggest functionality the app doesn't have"This icon implies [feature] but the app doesn't support it. App Review flags misleading UI under guideline 2.3.1."
Minimum font size2.1 (Performance)Text below 11pt that doesn't scale"Text this small may be flagged as unreadable. Use .caption2 (11pt) as the minimum, and ensure it scales with Dynamic Type."
Privacy UI5.1.1 (Data Collection)Removing permission explanations, hiding privacy controls"Removing this explanation could violate guideline 5.1.1. Users must understand why data is collected before granting permission."

How to push back

  1. Explain why — cite the specific rule (CLAUDE.md, HIG, colorblind palette, cross-view pattern)
  2. Offer an alternative — never just say "no," always suggest what WOULD work
  3. Defer to the user — if they insist after hearing the tradeoff, implement it with a note: "Applied as requested. Note: this deviates from [rule]."

Diminishing returns guardrail

Track refinement count per element. After 3 refinements on the same element:

"This element has been refined 3 times. Further tweaks may not be visible to users. Suggest moving on — you can always revisit later. Continue refining or move on?"

This prevents infinite micro-adjustment loops while respecting the user's autonomy.

When to skip refinement

  • User chose "Done" — proceed to Phase 8 (Tests)
  • Single-domain audit (e.g., /ui-enhancer-radar space) — refinement is less relevant for focused checks
  • No visual changes were made (e.g., only performance or accessibility fixes)

Phase 8: Tests

Before asking, analyze the changes and make a recommendation. State whether tests are needed and why, then ask.

How to decide:

  • No tests needed: Pure styling (color, font, padding), layout reordering, section merging, spacing changes, renaming labels, enabling existing component parameters — recommend skipping
  • Tests recommended: New reusable component extracted, new interaction behavior added, conditional logic changed, accessibility labels modified — recommend adding
questions:
[
  {
    "question": "[Recommendation: Skip/Add] — [brief reason]. Would you like to add tests?",
    "header": "Tests",
    "options": [
      {"label": "Skip tests", "description": "Verify visually by running the app"},
      {"label": "Add tests for new components", "description": "Generate tests for any extracted or new components"},
      {"label": "Add preview coverage", "description": "Ensure #Preview blocks cover all states"},
      {"label": "Full test suite", "description": "Unit tests + previews + accessibility checks"}
    ],
    "multiSelect": false
  }
]

If "Skip tests" — proceed to Phase 9. Most UI enhancer changes (spacing, color, layout reordering, compaction) are verified visually, not via tests.

How to verify changes visually

After applying changes, suggest a verification method:

MethodBest forHow
Xcode Canvas preview (Recommended)Quick visual check after each changeOpen the modified file in Xcode, Canvas auto-refreshes as code changes. No build required.
Run on simulatorFull interaction testing, navigation flowsCmd+R in Xcode. Verify the view looks right in context with real data.
Run on deviceFinal validation, touch targets, real-world sizingBuild to physical device for accurate colors, text sizes, and haptics.
Screenshot comparisonBefore/after side-by-sideCapture simulator screenshots before and after; compare in Preview.app or Finder.
Dark mode toggleDark mode verificationToggle Appearance in Xcode Canvas or Settings > Developer > Dark Appearance on device.
Dynamic TypeAccessibility text scalingChange text size in Canvas controls or Settings > Accessibility > Display & Text Size.

Always mention Canvas as the recommended method — it gives immediate feedback without building, and the user can see changes as they're applied.

What Gets Tests

Change TypeTest TypeExample
New component extractedUnit test (Swift Testing)WarrantyStatusCircle properties and states
Layout changePreview verification#Preview with all states
Accessibility changeAccessibility audit testVoiceOver label coverage
Interaction changeUI test (if warranted)Button tap triggers expected action
Performance changeNo test neededVerified by profiling

Test Template

import Testing
@testable import [AppModule]

@Suite("[ComponentName] Tests")
struct [ComponentName]Tests {
    @Test("Renders correct state for [condition]")
    func [testName]() {
        // Arrange
        // Act
        // Assert with #expect
    }
}

When to Skip Tests

  • Pure styling changes (color, font, padding) — verify visually
  • Layout reordering — verify via preview/screenshot
  • Removing unused code — no behavior to test

Phase 9: Summary & Progress Tracking

Files Changed Summary (MANDATORY)

Before asking about progress, always list every file modified during this audit:

## Files Modified

| File | Changes |
|------|---------|
| `Sources/Views/Tools/ToolsView.swift` | Removed action bar, enabled showThemeToggle, full-width single-tool grid |
| `Sources/Views/Tools/ToolCategory.swift` | Merged input+output → importExport, renamed CSV Import → Spreadsheet Import |
| `Documentation/Development/FUTURE_FEATURES.md` | Added XLSX import item |

Total files: 3 | Lines added: X | Lines removed: Y

This helps users who return to the project later know exactly what was touched.

Progress Question

questions:
[
  {
    "question": "Changes applied. Want to measure the improvement?",
    "header": "Progress",
    "options": [
      {"label": "Done for now (Recommended)", "description": "Save the report and move on"},
      {"label": "Re-audit now", "description": "Run the audit again and compare scores"},
      {"label": "Capture new screenshot", "description": "Take a fresh screenshot and compare side-by-side"}
    ],
    "multiSelect": false
  }
]

Progress Report Format

## Progress: [ViewName]

UI Enhancer v[version]

| Domain | Before | After | Change |
|--------|--------|-------|--------|
| Space Efficiency | 4/10 | 7/10 | +3 |
| Visual Hierarchy | 6/10 | 8/10 | +2 |
| Overall | 6.4/10 | 8.1/10 | +1.7 |

Content-to-chrome ratio: 39% → 63% (+24%)
Findings resolved: 8/12
Tests added: 5

Phase Progress Banner (CRITICAL — BLOCKING requirement in Normal and Pre-Approved modes)

After EVERY phase and EVERY commit, your NEXT output MUST be the progress banner followed by the next-phase AskUserQuestion. Do not output anything else first. Do not leave a blank prompt.

Hands-Free mode exception: In Hands-Free mode, the progress banner still prints, but the next-phase AskUserQuestion is suppressed (per the Hands-Free Mode precedence rules in § Skill Introduction). Replace the AskUserQuestion with the Hands-Free completion message instead.

The skill has 9 numbered top-level phases (the user-facing structure). Several phases have lettered sub-phases (2b, 6b/6c/6d, 7a/7b/7c/7d/7e/7f) — these are implementation steps within a parent phase and print as substeps, not as standalone phases.

Banner template (top-level phase completion):

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ Phase [N] of 9 complete: [phase name]

⏱  Next: Phase [N+1] — [phase name] (~[time estimate])
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Banner template (sub-phase completion within a top-level phase):

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ Phase [N] (substep [letter]) of 9 complete: [substep name]

⏱  Next: Phase [N] (substep [next-letter]) — [substep name] (~[time estimate])
   or → Phase [N+1] if this was the last substep
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

This keeps the denominator stable at 9 while still announcing fine-grained progress through Phase 7's six substeps (7a-7f).

Phase time estimates:

PhaseNameEst. Time
1Interview~2 min
2 (with substep 2b)Gather Input + View Type Classification~3 min
3Screenshot Analysis~2 min
4Code Analysis~3-5 min
5Domain Analysis~5-10 min
6 (with substeps 6b/6c/6d)Report + Content Preservation + Compaction + Compensation~3-5 min
7 (with substeps 7a/7b/7c/7d/7e/7f)Implementation Playbook through Refinement Loop~25-50 min total
8Tests~5-10 min
9Summary~2 min

Then immediately prompt for the next phase (or substep, where applicable). After a commit, reprint the banner and auto-prompt. Never leave a blank prompt.


Pipeline Mode Behavior (Tier 2/3)

When running inside a Tier 2 or Tier 3 pipeline (detected via tier field in .radar-suite/session-prefs.yaml):

  1. On skill start: Emit the pipeline-level progress banner (see radar-suite-core.md Pipeline UX Enhancements #1). If this is the first skill in the pipeline OR experience_level is Beginner/Intermediate, also emit the audit-only statement.
  2. On skill completion: Emit a per-skill mini rating table marked "PRELIMINARY" (see Pipeline UX Enhancements #2). Then emit the pipeline-level progress banner showing this skill as complete.
  3. Within-skill phase banners (above) are still emitted normally in addition to the pipeline-level banners.

short_title Requirement (v2.1)

Every finding MUST include a short_title field (max 8 words). This is the human-scannable label used in pipeline banners, pre-capstone summaries, and ledger output.

Example: short_title: "Low contrast on dark mode card"

All finding ID references in output (tables, banners, summaries) use the format: RS-NNN (short_title).


Optional Features

These features are available but not run by default. User must explicitly request them.

Device Size Simulation (Optional)

Analyze how the view behaves across device sizes by examining spacing, padding, and layout constraints in code.

/ui-enhancer-radar --devices
DeviceWidthCheck
iPhone SE375ptDoes content fit? Truncation?
iPhone 16393ptStandard layout
iPhone 16 Pro Max430ptExtra space utilized?
iPad 11"820ptSplit view behavior?

Simulator Screenshot Capture (Optional)

Automatically capture a screenshot from a running simulator.

/ui-enhancer-radar --capture
# List available simulators
xcrun simctl list devices booted

# Capture screenshot
xcrun simctl io booted screenshot /tmp/ui-enhancer-radar-capture.png

Before/After Screenshot Comparison

When /ui-enhancer-radar compare is used, or when the user wants to see visual progress:

Automated workflow (if simulator is running):

# Capture "after" screenshot
xcrun simctl io booted screenshot /tmp/ui-enhancer-radar-after.png

Then read both the original screenshot (provided at start) and the new capture, comparing side by side.

Manual workflow (guide the user):

  1. "Take a screenshot now (Cmd+Shift+4 on macOS, or Cmd+S in Simulator)"
  2. "Apply the changes"
  3. "Take another screenshot"
  4. "Open both in Preview.app — Window > Tile All to Left/Right for side-by-side"

What to compare:

  • Content-to-chrome ratio: did it improve?
  • Visual hierarchy: is the primary content more prominent?
  • Color variety: did monochromatic sections gain differentiation?
  • Space recovery: is content visible that was previously below the fold?
  • Consistency: do the changes match the app's existing patterns?

Batch Mode (/ui-enhancer-radar batch [path])

Audit all views in a directory, build the View Profile in one pass, then rank views by severity.

How it works:

  1. Glob for *.swift files in the specified path
  2. For each file, classify the view type (Phase 2b) and run a lightweight analysis (skip interview — use default "General polish, All domains, Moderate")
  3. Build the View Profile from all views simultaneously — this gives the strongest baseline for cross-view consistency
  4. Rank views by overall severity score
  5. Present the ranked list with top findings per view

Output format:

## Batch Audit: [path]
*[N] views analyzed | [date]*

| # | View | Type | Score | Top Finding |
|---|------|------|-------|-------------|
| 1 | PrivacyNetworkView | Settings/form | 3/10 | Monochromatic — 14/18 elements gray |
| 2 | ArchiveView | List | 5/10 | Section headers undifferentiated |
| 3 | DashboardView | Dashboard | 6/10 | Sidebar-content redundancy (macOS) |
| ... | ... | ... | ... | ... |

Run `/ui-enhancer-radar` on any view above for a full audit.

After presenting the ranked list, offer a seamless transition:

questions:
[
  {
    "question": "[ViewName] scored lowest ([score]/10). Start a deep audit on it now?",
    "header": "Next",
    "options": [
      {"label": "Deep audit worst view (Recommended)", "description": "Open [ViewName] in Canvas/simulator, then walk through findings visually"},
      {"label": "Pick a different view", "description": "Choose which view to audit from the ranked list"},
      {"label": "Done for now", "description": "Save batch results to handoff, audit views later"},
      {"label": "Explain more", "description": "What does a deep audit do that batch scan doesn't?"}
    ],
    "multiSelect": false
  }
]

If user selects a view: Transition directly into the full audit flow (Phase 1 interview → Phase 7 implementation). Do NOT require the user to re-invoke the skill — continue in the same session. The batch scan findings for that view become the starting point for Phase 5 (Domain Analysis), pre-populated with what the batch already found.

Batch mode limitations:

  • No interview — uses defaults. Run individual audits for design-intent-aware analysis.
  • No screenshot analysis — code-only. Provide screenshots for visual domains.
  • No implementation — findings only. Run individual audits to apply fixes.
  • Transition to individual audit is seamless — no need to re-invoke the skill.

Analysis Heuristics Reference

Content-to-Chrome Ratio

Chrome = status bar + nav bar + tab bar + custom headers + toolbars + search + pickers
Content = everything else

Targets:   iPhone >60% | iPad >70% | macOS >75%
Flags:     <50% Critical | 50-60% High | 60-70% Medium | >70% Good

Vertical Space Budget (iPhone)

Viewport: ~667pt (SE) to ~852pt (Pro Max)
Status bar: 54pt (Dynamic Island) / 44pt (notch) / 20pt (none)
Nav bar: 44pt | Tab bar: 49pt + 34pt safe area

Budget: Header max 80pt | Actions max 44pt (one row) | Content >60% of remaining

Visual Hierarchy Scoring

For each element, assign visual weight:
  Size: Large=3, Medium=2, Small=1
  Weight: Bold=2, Regular=1, Light=0
  Color: Bright=2, Standard=1, Muted=0
  Position: Top-left=3, Center=2, Bottom-right=1

Highest weight should be primary content.
If chrome/status/badge scores highest → hierarchy issue.

Common Anti-Patterns

Anti-PatternExampleFix
Date Dominance"Sep 1982" in title3.boldCaption or status circle
Header StackLogo+title+subtitle+search+picker+buttonsCollapse to essentials
Badge Overload5 status badges on one rowPrioritize, hide secondary
Combined ControlsTwo functions in one buttonSeparate into distinct controls
Permanent Hints"Tap any field to edit" on every visitShow once, then remove
Negative Numbers"-15,892 days" for expired items"Expired" label or icon
Orphaned ChromeParent header visible on child viewEnsure child replaces parent
Giant Padding100pt padding for 44pt floating buttonMatch to actual element size
Hardcoded ColorsColor.white in dark modeUse semantic .background
Heavy View Body200-line body with inline logicExtract computed properties

Edge Case Guardrails

Known situations where the skill can cause problems if not handled carefully:

Edge CaseRiskGuardrail
No #Preview blockCanvas verification recommended but impossibleCheck for #Preview before recommending Canvas. If missing, offer to add one or suggest simulator instead.
Shared component modificationChanging a shared component (e.g., ContentIllustratedHeader) breaks all views that use itNEVER modify shared components directly. Always apply changes at the call site. If a component needs changing, flag it as a cross-cutting change and confirm scope.
Multiplatform viewsPlatform-specific heuristics applied to the wrong platform; changes on one side break the otherSee multiplatform guardrail below.

Multiplatform guardrail (MANDATORY for any view with #if os() blocks)

Before making any changes to a multiplatform view:

  1. Detect platform blocks — grep the file for #if os(iOS), #if os(macOS), #else. If found, the view is multiplatform.
  2. Analyze each platform separately — don't apply iPhone heuristics (44pt touch targets, 852pt viewport) to macOS code, or macOS heuristics (menu bar, sidebar, window chrome) to iOS code.
  3. After every change, check the other platform:
    • If editing iOS-specific code: does macOS still have equivalent functionality? (e.g., dismiss buttons, theme toggle, toolbar actions)
    • If editing macOS-specific code: does iOS still have equivalent functionality?
    • If editing shared code (outside #if blocks): does it work on BOTH platforms?
  4. Watch for duplicate controls — adding something to shared code that already exists in a platform-specific block creates duplicates on that platform (e.g., theme toggle in both header AND macOS toolbar).
  5. Platform-specific metrics:
MetriciOSmacOS
Min touch/click target44pt24pt (but 44pt preferred)
Viewport height667-932ptVariable (window)
NavigationTabView, NavigationStackNavigationSplitView, sidebar
Dismiss mechanismSwipe down, X buttonClose button, Esc key
Typography baselineSan Francisco, Dynamic TypeSan Francisco, fixed sizes acceptable
Large files (1000+ lines)Partial file reads may miss context, leading to incomplete findingsRead the full file in sections before generating findings. If the file exceeds 1000 lines, note it and focus on the visible sections.
Rename cascadesRenaming an enum case (e.g., ToolCategory.input → .importExport) requires updating every switch statementAfter any rename, grep the codebase for the old name to verify no references remain. Build before moving to the next fix.

Decision Prompt Rules (MANDATORY — all user-facing decisions)

Every AskUserQuestion that presents a design decision, implementation choice, or finding resolution MUST include an "Explain pros/cons" option:

  • [Recommended option] (Recommended) — [one-line description]
  • [Alternative(s)] — [one-line description each]
  • Accept as-is — [why this is safe to leave] (where applicable)
  • Explain pros/cons — Walk through the tradeoffs before deciding

If the user selects "Explain pros/cons": present a brief analysis (3-5 bullets), then re-prompt with the same options (minus "Explain pros/cons").

Never silently note findings "for future." Every finding discovered during the audit must be presented with the full Issue Rating Table and a decision prompt (Fix / Defer / Accept / Explain pros/cons).

Finding Dependencies and Fingerprints

When creating findings, populate these optional fields where relationships are obvious:

  • depends_on/enables: Visual fixes sometimes depend on each other -- e.g., a color system change enables contrast fixes across multiple views. If one fix must come before another, populate with finding IDs.
  • pattern_fingerprint/grep_pattern/exclusion_pattern: Assign fingerprints for generalizable UI anti-patterns (e.g., hardcoded_color, missing_dynamic_type, insufficient_contrast, missing_dark_mode_adaptive).

Finding Resolution (MANDATORY — end of every run)

Principle: Every finding must reach a terminal state. "Deferred" is not terminal — it's temporary.

Terminal States

StateMeaningHow
FixedCode changed, test written, committedPhase 7 apply workflow
PlannedAdded to DEFERRED.md with severity, effort, reason, release gateUser chose "save for later"
AcceptedUser explicitly said "this is fine"User chose "accept as-is"

Self-Resolution (end of every run)

After Phase 9 completes (or if the user chose "plan only"), check for unresolved findings in this session. If any exist, present:

You have [N] unresolved findings from this audit:

| # | Finding | Severity | Effort |
|---|---------|----------|--------|
| 1 | ... | ... | ... |

Every finding needs a decision:
1. **Fix now** — enter apply workflow for these items
2. **Plan it** — add to DEFERRED.md (tracked, reviewed before release)
3. **Accept as-is** — explicitly sign off (removed from tracking)
4. **Explain more** — walk through what each finding means

For each finding, the user chooses Fix / Plan / Accept. Update the handoff YAML accordingly:

  • Fix → move to findings_fixed after apply completes
  • Plan → move to findings_planned, write to DEFERRED.md
  • Accept → move to findings_accepted

fix-deferred Subcommand

When invoked via /ui-enhancer-radar fix-deferred:

  1. Read own handoff YAML (.agents/ui-audit/ui-enhancer-radar-handoff.yaml)
  2. Extract findings that were not applied in previous runs
  3. If empty → "No deferred findings from previous ui-enhancer-radar runs."
  4. If non-empty → present findings table (already rated from original run)
  5. For each finding, ask: Fix now / Plan it / Accept as-is
  6. Fix items enter Phase 7 apply workflow. Plan items go to DEFERRED.md. Accept items go to findings_accepted.
  7. Update handoff YAML with resolved statuses

verify Subcommand (lightweight re-check)

When invoked via /ui-enhancer-radar verify: Read own handoff YAML, grep for each finding's pattern in the codebase, classify as Still present / Resolved / Changed, update handoff accordingly. Print summary. Much faster than a full re-audit. See data-model-radar SKILL.md for full verify logic.

Startup Check

On every invocation, check for DEFERRED.md at the project root. If it exists and contains ui-enhancer-radar items:

📋 You have [N] planned items from previous ui-enhancer-radar audits in DEFERRED.md.
   [M] are pre-release priority. Run `/ui-enhancer-radar fix-deferred` to resolve them.

DEFERRED.md Format

If DEFERRED.md doesn't exist, create it when the first item is planned. Format:

# Deferred Findings

Items intentionally deferred from radar audits. Review before each release.

| # | Finding | Source | Severity | Release Gate | Effort | Reason | Date | Review By |
|---|---------|--------|----------|-------------|--------|--------|------|-----------|

Release Gate values: Pre-release / Post-release / Next major

Review By: Default 90 days from deferral date. Capstone flags overdue items.


Cross-Skill Handoff

UI Enhancer Radar complements data-model-radar (model layer), ui-path-radar (navigation paths), roundtrip-radar (data safety), and capstone-radar (ship readiness). Findings from one skill inform the others.

Cross-Skill Resolution (after fixing any finding)

When a fix resolves a finding that originated from ANOTHER skill's handoff, update that skill's handoff YAML. Read the other skill's handoff, find the matching finding in findings_deferred[] or for_capstone_radar.blockers[], move it to findings_fixed[] (or resolved[]) with the fix commit hash and resolved_by: "ui-enhancer-radar". This prevents stale handoffs from blocking capstone's ship recommendation.

On Completion — Write Handoff

After completing a view audit, write/update .agents/ui-audit/ui-enhancer-radar-handoff.yaml:

source: ui-enhancer-radar
date: <ISO 8601>
project: <project name>
views_audited: <count>

# File timestamps — enables staleness detection by consuming skills
file_timestamps:
  <file path>: "<ISO 8601 mod date>"
  # one entry per unique file referenced in issues

findings_fixed:
  - finding: "<description>"
    severity: "<CRITICAL|HIGH|MEDIUM|LOW>"
    fix_commit: "<git hash>"

findings_deferred:
  - finding: "<description>"
    severity: "<CRITICAL|HIGH|MEDIUM|LOW>"
    reason: "<why deferred>"
    group_hint: "<optional batching suggestion>"

findings_planned:
  - finding: "<description>"
    severity: "<CRITICAL|HIGH|MEDIUM|LOW>"
    release_gate: "<Pre-release|Post-release|Next major>"
    reason: "<why deferred>"
    deferred_md_row: true
    group_hint: "<optional batching suggestion>"

findings_accepted:
  - finding: "<description>"
    severity: "<CRITICAL|HIGH|MEDIUM|LOW>"
    reason: "<why accepted>"
    accepted_date: "<ISO 8601>"

for_ui_path_radar:
  # Visual issues that suggest structural navigation problems
  suspects:
    - view: "<view file>"
      finding: "<e.g., button with no visible action>"
      question: "<is this button wired to a destination?>"
      group_hint: "<optional batching suggestion>"

for_roundtrip_radar:
  # Views with data binding concerns found during visual audit
  suspects:
    - workflow: "<affected workflow>"
      finding: "<e.g., form field not reflected in saved data>"
      file: "<file:line>"
      question: "<does this field round-trip correctly?>"
      group_hint: "<optional batching suggestion>"

for_capstone_radar:
  # Visual/UX issues that affect ship readiness
  blockers:
    - finding: "<description>"
      urgency: "<CRITICAL|HIGH>"
      group_hint: "<optional batching suggestion>"

File Timestamps

For each unique file path referenced across all issues, record its modification date:

stat -f "%Sm" -t "%Y-%m-%dT%H:%M:%SZ" "<file path>"

Enables consuming skills to detect staleness — if a view file changed after the audit, visual findings may be stale.

Group Hints

Optional field for batching related issues. Common hints:

  • spacing_issues — padding, margins, layout
  • color_accessibility — contrast, colorblind safety
  • hierarchy_issues — visual prominence problems
  • hig_violations — Apple HIG non-compliance
  • dark_mode — light/dark mode issues

Automatic: This file is always written so other audit skills can pick up where this one left off. No user action needed.

End-of-Run Directory Cleanup (MANDATORY)

Per the Artifact Lifecycle rules in radar-suite-core.md, before returning from this skill:

  1. List files in .radar-suite/.
  2. Move any stale single-use handoffs (RESUME_PHASE_*.md, RESUME_*.md except NEXT_STEPS.md, *-v[0-9]*.md) to .radar-suite/archive/superseded/.
  3. Confirm Class 1 persistent-state files (ledger.yaml, session-prefs.yaml, DEFERRED.md) are in-place rewrites — not dated or versioned.
  4. Confirm Class 2 handoff files are overwrites, not appends.

This prevents .radar-suite/ from accumulating stale prose artifacts across runs.

Write to Unified Ledger (MANDATORY)

After writing the handoff YAML, also write findings to .radar-suite/ledger.yaml following the Ledger Write Rules in radar-suite-core.md:

  1. Read existing ledger (or initialize if missing)
  2. Record this session (timestamp, skill name, build)
  3. For each finding: check for duplicates, assign RS-NNN ID if new, set impact_category, compute file_hash
  4. Write updated ledger

Impact category mapping for ui-enhancer-radar findings:

  • Color contrast failure (WCAG) → ux-broken (accessibility violation)
  • Missing dark mode support → ux-degraded
  • Spacing/alignment issues → polish
  • HIG violations → polish
  • Visual hierarchy problems → ux-degraded
  • Colorblind safety violations → ux-broken

On Startup — Read Ledger & Handoffs (MANDATORY)

Before Phase 1 (Interview), read the unified ledger and ALL companion handoff YAMLs:

Read .radar-suite/ledger.yaml (if exists) — check for existing findings to avoid duplicates
Read .agents/ui-audit/data-model-radar-handoff.yaml (if exists)
Read .agents/ui-audit/ui-path-radar-handoff.yaml (if exists)
Read .agents/ui-audit/roundtrip-radar-handoff.yaml (if exists)
Read .agents/ui-audit/capstone-radar-handoff.yaml (if exists)

Ledger check: If the ledger contains findings for views you're about to audit, note their RS-NNN IDs. When you find the same issue, update the existing finding instead of creating a new one.

Regression check: For any fixed findings in the ledger whose file_hash no longer matches the current file, flag for re-verification per the Regression Detection protocol in radar-suite-core.md.

Parse for_ui_enhancer_radar sections. Each companion can direct findings to this skill. Look for:

  • for_ui_enhancer_radar.suspects[] — views another skill flagged as having visual issues
  • for_ui_enhancer_radar.priority_views[] — views another skill wants audited first

If found, incorporate as context during the interview phase (e.g., "capstone-radar wants ToolsHelpView audited first — monochromatic icons flagged"). These are not pre-confirmed findings — verify each one independently.

What each companion provides:

  • data-model-radar — model fields that should be displayed but aren't (missing UI for data)
  • ui-path-radar — dead buttons to remove before visual audit
  • roundtrip-radar — data issues that affect view correctness, computed data never displayed
  • capstone-radar — priority views from ship readiness grading

If not found, proceed normally.


Compliance Self-Check (MANDATORY — run before final summary)

Before writing the final summary, handoff YAML, or session wrap-up, execute this mechanical checklist. Do NOT skip it. Do NOT summarize without running it first.

Review your own output from this session and fill in each row:

| # | Gate | Check | Pass? | Gaps |
|---|------|-------|-------|------|
| 1 | Table Format | Every findings table has 9 columns (Finding, Confidence, Urgency, Risk:Fix, Risk:NoFix, ROI, Blast Radius, Fix Effort, Status) | ? | |
| 2 | Test Gate | Every committed fix has a test — or a documented exemption (visual, dead code, singleton) | ? | |
| 3 | Visual Inspection | User confirmed they could see every view BEFORE any code changes were applied | ? | |
| 4 | Pattern Sweep | Every similar-view finding was presented with full table + decision prompt (no silent "noted for future") | ? | |
| 5 | Decision Prompts | Every design decision included "Explain pros/cons" option | ? | |
| 6 | Finding Resolution | Every finding reached terminal state (Fixed, Planned, Accepted) — no orphaned "deferred" items | ? | |

If ANY gate fails, print the gap, fix it, then proceed:

  • Visual Inspection fail: If code was changed without user viewing the screen, flag it: "Changes to [view] were applied without visual confirmation. Verify in Canvas/simulator now, or revert."
  • Other gates: See data-model-radar SKILL.md for full gate-checking instructions.

End Reminder

After every phase/commit: print progress banner → AskUserQuestion → never blank prompt.

Gates (all mandatory):

GateRule
Visual InspectionNEVER modify UI without user viewing it first. Phase 7b is non-negotiable. If user can't see view, save playbook and stop.
Context ExhaustionAfter 50 tool calls, downgrade findings to probable (long context). Add context_exhaustion_after: [N] to handoff.
Table FormatALL findings tables need 9 columns. Count before output. No exceptions.
Test GateTest with each fix. Pure visual/dead code exempt (document why).

Phase 7 order: 7a (Commit) → 7b (Visual Gate) → 7c (Guided Review) → 7d (Apply) → 7e (Pattern Sweep) → 7f (Refinement). Never skip 7b or 7c.

</ui-enhancer-radar>

Keep looking

Skills are one crate of 628,971. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.