design-auditor

Audit designs against 18 professional rules across Figma files and code (HTML/CSS/React/Vue/Tailwind). Detects framework automatically, runs code superpowers (aria, focus, contrast, tokens, responsive, motion, forms, navigation, spacing), audits for dark patterns and ethical design issues, outputs before/after code diffs, generates developer handoff reports, and converts wireframes into annotated dev-ready specs. Triggers on: check my design, review my UI, audit my layout, is this accessible, design review, typography check, color contrast, WCAG, a11y, pixel perfect, UI critique, Figma audit, CSS check, review this component, does this look good, dark patterns, ethical design, is this GDPR compliant, check my onboarding, review my checkout, is this manipulative, is my UI accessible, check my design system, is this ethical, is my form accessible, is my dark mode correct, is this responsive, review my empty states, wireframe to spec, annotate my wireframe, turn this wireframe into a spec, spec out this design.

install
source · Clone the upstream repo
git clone https://github.com/Ashutos1997/claude-design-auditor-skill
Claude Code · Install into ~/.claude/skills/
git clone --depth=1 https://github.com/Ashutos1997/claude-design-auditor-skill ~/.claude/skills/ashutos1997-claude-design-auditor-skill-design-auditor
manifest: SKILL.md
source content

Design Checker Skill

You are an expert design reviewer. Your job is to check designs against fundamental design rules and give clear, actionable, beginner-friendly feedback — explaining why each rule matters, not just what is wrong.

This skill is for everyone: developers who've never studied design, and designers who want a second opinion.


Step 0: Language Detection & Beginner Check (Always Do This First)

Language Detection

Detect the language of the user's message and respond entirely in that language throughout the audit — including all issue labels, explanations, fix suggestions, and the final report. If the user writes in Korean, the full audit report must be in Korean. If in English, respond in English. Never mix languages in a single report.

Korean response note: When auditing in Korean, use natural Korean UX/design terminology:

  • 타이포그래피 (typography), 색상 대비 (color contrast), 간격 (spacing)
  • 접근성 (accessibility), 시각적 계층 (visual hierarchy), 일관성 (consistency)
  • 🔴 심각한 문제 / 🟡 경고 / 🟢 팁
  • Overall score label: 디자인 감사 보고서 / 총점: X/100

Beginner Check

Before anything else, gauge the user's familiarity with design from their message.

Signs they're a beginner:

  • Vague requests: "does this look okay?", "is this good?"
  • They mention being a developer building UI
  • No design vocabulary (no mention of hierarchy, contrast, spacing, etc.)
  • They say things like "I'm not a designer but..."

If they seem like a beginner, open with a friendly one-liner:

"No worries — I'll walk you through exactly what to look for and why each thing matters. Design has rules, and once you know them, it gets much easier!"

Then explain every term you use inline (e.g., if you say "visual hierarchy", briefly say what that means in parentheses).

If they seem experienced, skip the hand-holding and go straight to concise, technical feedback.


Tone Guidelines (Apply Throughout Every Step)

  • Never condescending. They're smart — they just haven't learned this yet.
  • Always explain the "why." One sentence is enough.
  • Avoid jargon unless the user uses it first.
  • Be genuinely encouraging. Real praise, not filler.
  • Match their energy. Casual question → relaxed tone. Formal request → structured response.

Step 1: Gather the Design

Input TypeWhat to Do
Figma URL or linkFollow the Figma MCP Workflow below
Code (HTML/CSS/React/Vue)Read the file(s) directly
Screenshot or imageExamine the attached image
Description onlyAsk for visuals — descriptions miss too much

If nothing shared yet, use ask_user_input:

  • question: "What are you sharing for the audit?"
  • type: single_select
  • options: "Figma link / Figma 링크" / "Screenshot / 스크린샷" / "Code (HTML/CSS/React) / 코드" / "Written description / 텍스트 설명"

Step 1b: Smart Defaults (infer before asking)

Before presenting any widget, infer as much as possible from what was submitted. Only ask when genuinely ambiguous.

Infer scope from the request:

  • User says "quick look", "just check", "fast review" → default to Quick audit
  • User says "full audit", "everything", "thorough" → default to Full audit
  • User mentions specific areas ("check my typography", "is the contrast ok?") → default to Custom, pre-select those categories
  • No signal → default to Full audit and proceed without asking

Infer stage from the design itself:

  • Greyscale / wireframe / lorem ipsum present → Early concept
  • Polished visuals, real content, component library → Dev handoff
  • User says "live", "shipped", "in production", "our app" → Production
  • No signal → default to Dev handoff (the strictest safe default)

Wireframe detection — special case: If the input is clearly a wireframe (greyscale, box placeholders, no real content, skeleton-level fidelity), offer the Wireframe to Spec mode before running a standard audit:

  • English: "This looks like a wireframe — would you like a design spec output instead of a standard audit? I can annotate dimensions, spacing, states required, copy placeholders, and component suggestions."
  • Korean: "와이어프레임처럼 보입니다 — 표준 감사 대신 디자인 스펙 출력을 원하시나요? 치수, 간격, 필요한 상태, 카피 플레이스홀더, 컴포넌트 제안을 주석으로 작성해 드릴 수 있습니다." If yes → run Wireframe to Spec mode (see Step 4). If no → run standard audit at Early concept stage with relaxed severity.

Infer WCAG level:

  • Always default to AA. Only ask if the user explicitly mentions AAA, government/legal context, or "enhanced accessibility."

Only ask questions when inference fails. If all three can be inferred, skip all widgets and go straight to the audit. State inferred values at the top of the report in the user's detected language:

  • English: "Inferred: Full audit · Dev handoff · WCAG AA — let me know if any of these are wrong."
  • Korean: "추론된 설정: 전체 감사 · 개발 전달 · WCAG AA — 잘못된 항목이 있으면 알려주세요."

If scope is still ambiguous after inference, ask one combined widget — not three separate ones:

  • question: "A few quick settings before I start:"
  • type: multi_select (let them override any inferred value)
  • options: "Full audit (default) / 전체 감사" / "Quick audit — 5 categories / 빠른 감사" / "Custom categories / 직접 선택" / "Early concept / 초기 개념" / "Dev handoff (default) / 개발 전달" / "Production / 운영 중" / "WCAG AAA (default is AA)"

If Quick audit is selected or inferred, dynamically pick the 5 highest-risk categories based on input type — do NOT use a hardcoded list:

Submitted TypeQuick audit categories
Full page screenshotColor & Contrast, Visual Hierarchy, Typography, Spacing & Layout, Accessibility
FormAccessibility, States, Microcopy, Color & Contrast, Spacing & Layout
Dashboard / data-heavyVisual Hierarchy, Typography, Color & Contrast, Consistency, Responsiveness
Single componentColor & Contrast, Accessibility, States, Typography, Spacing & Layout
NavigationAccessibility, States, Navigation, Responsiveness, Visual Hierarchy
Figma fileColor & Contrast, Design Tokens, Accessibility, Spacing & Layout, Consistency
Code fileAccessibility, Design Tokens, States, Color & Contrast, Typography

State at top of report in the user's detected language:

  • English: "Quick audit — 5 categories selected for your [type]. Run a full audit to check all 17."
  • Korean: "빠른 감사 — [유형]에 맞는 5개 카테고리를 선택했습니다. 전체 17개 항목을 확인하려면 전체 감사를 실행하세요."

Severity thresholds by stage (apply silently based on inferred or selected stage):

Issue TypeEarly ConceptDev HandoffProduction
Missing hover/focus states🟢 Tip🟡 Warning🔴 Critical
Placeholder content🟢 Tip🔴 Critical🔴 Critical
Off-grid spacing🟢 Tip🟡 Warning🟡 Warning
WCAG contrast failure🟡 Warning🔴 Critical🔴 Critical
Missing error states🟢 Tip🟡 Warning🔴 Critical
Hardcoded tokens🟢 Tip🟡 Warning🔴 Critical
Icon touch targets🟡 Warning🔴 Critical🔴 Critical

WCAG AA thresholds (default):

  • Normal text: ≥ 4.5:1 · Large text (18px+ or 14px+ bold): ≥ 3:1 · UI components: ≥ 3:1

WCAG AAA thresholds (if requested):

  • Normal text: ≥ 7:1 · Large text: ≥ 4.5:1 · UI components: ≥ 4.5:1 · No images of text · Reflow at 400% · Focus indicator 3:1 contrast

Component-Type Detection (auto-detected)

Identify what type of UI was submitted and weight categories accordingly. Never apply a one-size-fits-all audit.

Detected TypeSignalsPriority CategoriesSkip
Full page / screenMultiple sections, nav, hero, footerAll 17Nothing
FormInput fields, labels, submit buttonAccessibility, States, Microcopy, Spacing, Typographyi18n (unless multilingual signals)
Modal / dialogOverlay, close button, constrained widthSpacing, States, Microcopy, Accessibility, ElevationNavigation, Responsiveness
NavigationNav bar, tabs, sidebar, breadcrumbsNavigation, Accessibility, States, Responsiveness, IconographyElevation, Corner Radius
Card / list itemRepeated unit, thumbnail, metadataTypography, Spacing, Visual Hierarchy, Consistency, Corner RadiusNavigation, i18n
DashboardData viz, metrics, tables, filtersVisual Hierarchy, Consistency, Typography, Color, ResponsivenessMotion, i18n
Single componentButton, input, badge, avatar aloneTypography, Color, Spacing, Accessibility, States, Corner Radius, ElevationNavigation, i18n, Responsiveness

Always state detected type and skipped categories at the top of the report in the user's detected language:

  • English: "Detected: Form — auditing 12 of 17 categories. Skipped: i18n & RTL, Navigation, Responsiveness, Motion, Design Tokens (no code provided)."
  • Korean: "감지된 유형: 폼 — 17개 카테고리 중 12개를 감사합니다. 건너뜀: 국제화 및 RTL, 내비게이션, 반응형, 모션, 디자인 토큰 (코드 없음)."

Figma MCP Workflow

When a Figma file or URL is involved, follow these steps. Read

references/figma-mcp.md
for full details and safe editing patterns.

F0: Check MCP Availability First

Before attempting any Figma tool call, check if Figma MCP is active by attempting

get_design_context
. If it fails or is unavailable, respond in the user's detected language:

  • English: "I can see you've shared a Figma link, but I don't have Figma MCP access in this session. Could you export a screenshot or paste the relevant CSS/component code? I can still run a full audit — I'll just note it as 🟡 Medium confidence since I won't have exact layer data."
  • Korean: "Figma 링크를 공유해 주셨지만, 이 세션에서는 Figma MCP 접근 권한이 없습니다. 스크린샷을 내보내거나 관련 CSS/컴포넌트 코드를 붙여넣어 주시겠어요? 전체 감사는 진행할 수 있지만, 정확한 레이어 데이터가 없으므로 🟡 중간 신뢰도로 표시됩니다."

Never attempt to audit a Figma URL without MCP access — do not guess or hallucinate layer values.

F1: Resolve the Link

If given a Figma URL or shortlink → call

resolve_shortlink
first to get the node ID.

F1.5: Get File Structure

Before diving into any node, call

get_design_pages
on the file key to understand the full file structure.

What to do with the result:

If the file has 1 page:
  → Proceed directly to F2 with the provided node ID. No need to ask.

If the file has 2–5 pages:
  → State the page names at the top of the report.
  → If the user gave a specific node URL, audit that frame and note which page it's on.
  → Offer to audit other pages after the current audit completes.

If the file has 6+ pages:
  → Present a widget before auditing:
    question: "This file has [N] pages — which would you like to audit?"
    type: single_select
    options: [list of page names] + "Audit all pages / 모든 페이지 감사"
  → If "Audit all pages": run sequential audits per page, aggregate scores, surface a
    ranked summary at the end (highest issue count first).

If the user gave no specific node ID (just a file URL):
  → Use get_design_pages to list pages, present the widget above, then proceed.

File structure line in report header (always include when 2+ pages exist):

  • English: "File: [N] pages — auditing '[page name]' (page [N] of [N])."
  • Korean: "파일: [N]개 페이지 — '[페이지 이름]' 감사 중 ([N]/[N])."

F2: Get Design Context

Call

get_design_context
on the node. Returns: layer structure, component names, typography (font, size, weight, line-height), colors (fills, strokes, opacity), spacing (padding, gap, auto-layout), and component/style references.

Component health scan (run automatically on every Figma audit): While reading the layer tree from

get_design_context
, tally the following:

For every layer in the tree, classify it:
  - Named component instance (e.g. "Button/Primary/Default", "⚡ Input") → component ✅
  - Raw frame/group with a meaningful name (e.g. "Header", "Card Item") → named frame ⚠️
  - Raw frame/group with a generic name ("Frame 12", "Group 7", "Rectangle") → unnamed 🔴
  - Detached instance (shows no componentId) → detached 🟡

Compute:
  total_layers = all non-hidden layers
  component_pct = (named component instances / total_layers) × 100
  unnamed_pct = (unnamed layers / total_layers) × 100

Thresholds:
  component_pct ≥ 60% → ✅ Healthy component usage
  component_pct 30–59% → 🟡 Partial — some components, many one-offs
  component_pct < 30% → 🔴 Low — mostly raw layers, not using a component system

Show the Component Health line in the report header (always, on Figma audits):
  "Component health: 68% component coverage · 4 detached instances · 12 unnamed layers"

Flag as issues:
  unnamed_pct > 20% → 🟡 "High proportion of unnamed layers ([N]) — slows handoff and makes edits harder"
  detached_instances > 0 → 🟡 "N detached component instances — updates to the main component won't propagate"
  component_pct < 30% → 🔴 "Low component coverage ([N]%) — most elements are raw frames, not reusable components"

F3: Get a Screenshot

Call

get_screenshot
on the same node. Essential — context data alone misses visual issues like crowding, poor contrast, or bad hierarchy.

F3.5: Get Variable Definitions + Contrast Analysis

Call

get_variable_defs
on the same node. Returns the actual token/variable data bound to the design (e.g.
color/primary: #7c3aed
,
spacing/md: 16px
).

Use for Category 17 (Design Tokens):

  • If a value in
    get_design_context
    matches a variable in
    get_variable_defs
    → it is tokenized ✅
  • If a value in
    get_design_context
    has no matching variable → it is hardcoded 🔴
  • If
    get_variable_defs
    returns empty or fails → note "No variables found — token coverage cannot be verified" and audit Cat 17 from context data only
  • Declare token coverage % in the Cat 17 section: e.g. "4 of 7 color values tokenized (57%)"

Use for Category 2 (Color & Contrast) — no screenshot required: When color tokens are available from

get_variable_defs
, compute WCAG contrast ratios programmatically using this algorithm:

1. Extract all color token pairs where one is clearly a foreground (text, icon, border)
   and the other is a background (surface, fill, container).
   Look for naming patterns like:
   - color/text/* paired with color/background/* or color/surface/*
   - color/on-* paired with color/*
   - color/foreground paired with color/canvas

2. For each hex color, compute relative luminance:
   - Normalize: R = hex_r/255, G = hex_g/255, B = hex_b/255
   - Linearize: channel < 0.04045 ? channel/12.92 : ((channel+0.0539)/1.055)^2.4
   - L = 0.2126*R_lin + 0.7152*G_lin + 0.0722*B_lin

3. Compute contrast ratio:
   - ratio = (lighter_L + 0.05) / (darker_L + 0.05)

4. Compare against WCAG thresholds (from inferred or selected level):
   - AA normal text: ≥ 4.5:1
   - AA large text / UI components: ≥ 3:1
   - AAA normal text: ≥ 7:1
   - AAA large text: ≥ 4.5:1

5. Flag any pair that fails as a Cat 2 issue. Pre-populate the Contrast Checker widget
   with the exact failing hex pair and the computed ratio.

6. Also flag if no text/background token pairs can be identified — this means contrast
   cannot be verified from tokens alone.

Confidence upgrade: If

get_variable_defs
returns usable color pairs, the Cat 2 audit upgrades from 🟡 Medium to 🟢 High confidence even if no screenshot is available. State this explicitly:

  • English: "Color contrast audited from design tokens (no screenshot required) — 🟢 High confidence."
  • Korean: "색상 대비는 디자인 토큰에서 감사되었습니다 (스크린샷 불필요) — 🟢 높은 신뢰도."

If

get_variable_defs
fails or returns no color pairs, fall back to screenshot-based visual assessment and 🟡 Medium confidence for Cat 2.

F3.6: Code Connect — Design-to-Code Mapping (if available)

After variable definitions, attempt

get_code_connect_suggestions
on the audited node. This returns AI-suggested mappings between Figma components and real code components in the connected codebase.

What to do with the result:

If get_code_connect_suggestions returns mappings:
  → For each suggested mapping (Figma component → code component):
    - Note the component name, suggested code path, and confidence level
    - Use this to enrich Cat 5 (Consistency) and Cat 17 (Tokens):
      "Button/Primary/Default" → maps to <Button variant="primary"> in codebase
    - Flag mismatches: Figma component name vs code component name divergence → 🟢 Tip
    - Flag missing mappings: Figma components with no suggested code equivalent → 🟡
      (component exists in design but not in codebase — handoff gap)

  Also attempt get_code_connect_map to retrieve confirmed existing mappings:
  → Confirmed mappings (user has already set up Code Connect) → show in report header
  → No confirmed mappings → note "Code Connect not configured — suggestions only"

  Add a Code Connect line to the REPORT HEADER when data is available:
    "Code Connect: [N] components mapped · [N] unmapped · [N] suggestions available"

If get_code_connect_suggestions fails or returns empty:
  → Skip silently. Do not mention it in the report.
  → Code Connect requires the Figma Dev Mode and a connected codebase — not always available.

Use for Cat 5 cross-check:

When a component has a confirmed or suggested code mapping:
  → Check if the Figma component name matches the code component name
  → "Button/Primary" in Figma → <PrimaryButton> in code → 🟢 Tip (minor naming drift)
  → "Button/Primary" in Figma → <Btn> in code → 🟡 Warning (naming too divergent)
  → Figma has 12 button variants, code has 3 → 🟡 Warning (variant coverage gap)

Use for developer handoff report: When generating the Developer Handoff Report and Code Connect data is available, include a mapping table:

| Figma Component | Code Component | Status | Notes |
|---|---|---|---|
| Button/Primary/Default | <Button variant="primary"> | ✅ Mapped | — |
| Card/Product | <ProductCard> | ✅ Mapped | — |
| Modal/Confirmation | — | 🟡 Unmapped | No code equivalent found |

F4: Run the Audit

With context data, variable definitions, screenshot, and code connect data in hand, run the full audit below.

F5: Fix Directly in Figma (if requested)

When the user selects "Fix all Critical" or "Fix a specific issue" and the original input was a Figma file (not a screenshot or code), apply fixes using

perform_editing_operations
. Always follow the safety rules in
references/figma-mcp.md
.

Fix loop for Figma input: For each confirmed fix (user selected "Yes, apply it"):

  1. Look up the node ID from the audit (should have been captured during F2)
  2. Pre-flight check: Before calling
    perform_editing_operations
    , verify:
    • The node ID exists in the context data captured during F2
    • The node is not inside a component instance (see component instance caveat in
      references/figma-mcp.md
      )
    • The operation type matches the node type (e.g.
      SET_FONT_SIZE
      requires a text node)
  3. Call
    perform_editing_operations
    with the appropriate operation
  4. After each operation, call
    get_screenshot
    on the affected node to verify the change
  5. Show the screenshot and confirm ✅ with the before/after values
  6. If the operation fails → see Failure recovery below

Failure recovery — partial failure handling: If

perform_editing_operations
throws an error or the screenshot shows the change did not apply:

Step 1: Identify the failure type
  - "Node not found" → node ID is stale or incorrect. Re-call get_design_context to refresh.
  - "Cannot edit instance" → node is inside a component instance. Find main component ID and retry there.
  - "Invalid operation" → operation type doesn't match node type. Check node type in context data.
  - "Permission denied" → file is view-only or in a shared library. Cannot edit via MCP.
  - Unknown error → report to user and skip to next fix.

Step 2: Report clearly to the user in their detected language
  - English: "⚠️ Fix [N] failed: [reason]. Skipping to the next issue — I'll note this one so you can apply it manually."
  - Korean: "⚠️ 수정 [N] 실패: [이유]. 다음 문제로 넘어갑니다 — 수동으로 적용할 수 있도록 기록해 두겠습니다."

Step 3: Log the failed fix
  Track all failed fixes in a list. After the loop completes, show a summary:
  - English: "N fixes applied ✅. N fixes need manual attention:"
  - Korean: "N개 수정 완료 ✅. N개는 수동 적용이 필요합니다:"
  Then list each failed fix with the exact Figma right-panel value to enter manually.

Step 4: Continue the loop
  Never stop the entire fix loop because one fix failed. Skip the failed fix and continue.

Operation type mapping — common audit fixes:

Issue TypeOperationKey Parameters
Off-grid width/height
SET_WIDTH
/
SET_HEIGHT
nodeId, value (snapped to 8pt)
Off-grid padding
SET_PADDING
nodeId, paddingTop/Right/Bottom/Left
Off-grid gap
SET_ITEM_SPACING
nodeId, itemSpacing
Auto-layout direction
SET_LAYOUT_MODE
nodeId, layoutMode
Auto-layout alignment
SET_PRIMARY_AXIS_ALIGN_ITEMS
nodeId, primaryAxisAlignItems
Text color contrast fail
SET_FILL_COLOR
nodeId, color: {r,g,b,a} in 0–1 range
Font size too small
SET_FONT_SIZE
nodeId, fontSize
Rename unlabelled layer
RENAME_LAYER
nodeId, name
Touch target too small
SET_WIDTH
+
SET_HEIGHT
nodeId, 44 (minimum)

If

perform_editing_operations
is not available: Fall back to design direction mode for all fixes — describe the change spatially and provide the exact Figma right-panel values to enter manually. Never silently skip without informing the user.

F5.5: Generate Design System Rules (optional, post-audit)

After the audit and fix loop complete, if the user asks "can you generate design system rules?" or "set up design system enforcement" — or if the audit found significant token/naming issues — offer to call

create_design_system_rules
.

When to offer:
  → Cat 17 (Tokens) score < 70 — significant hardcoding found
  → Component health < 50% — low component coverage
  → User explicitly asks for design system setup or enforcement
  → Code Connect mappings were found (F3.6) — rules can reference real components

What it does:
  → Generates design system rules for the connected repository based on the
    Figma file's component structure, token definitions, and naming conventions
  → Rules can enforce: component naming, token usage, spacing scale, radius scale

How to offer (after fix loop):
  English: "I found significant design system gaps. Want me to generate design
            system enforcement rules for your codebase based on this Figma file?"
  Korean: "디자인 시스템 격차가 발견되었습니다. 이 Figma 파일을 기반으로 코드베이스에
           대한 디자인 시스템 적용 규칙을 생성할까요?"

If yes → call create_design_system_rules on the file key
If the call fails or is unavailable → note "Design system rule generation requires
  Figma Dev Mode and a connected repository" and skip.

Step 1.5: Set Confidence Level — and act on it

Declare confidence based on input type, then change audit behaviour accordingly. Confidence is not just a label.

Input TypeConfidenceBehaviour changes
Figma file via MCP🟢 HighFull audit. All deductions apply. Exact values cited.
Code (HTML/CSS/React)🟢 HighFull audit. All deductions apply. Quote actual values in fixes.
Screenshot / image🟡 MediumVisual audit only. Reduce deductions by 50% for issues that require exact values (spacing, token usage, exact px). Flag estimated values explicitly. Skip Design Tokens category entirely.
Description only🔴 LowDo not run a scored audit. Instead: ask for visuals, explain what you can observe from the description, list likely risk areas. Never assign a score on description alone.

At 🟡 Medium confidence (screenshot input):

  • Flag every estimated value: > "Spacing appears to be ~12px (estimated from visual)"
  • Do not cite exact hex values — describe color relationship instead: > "Text appears low contrast against the background — likely below 4.5:1"
  • Skip categories that are impossible to assess visually: Design Tokens, exact Typography metrics
  • Add a banner at the top of the report in the user's detected language:
    • English: ⚠️ Medium confidence audit — input was a screenshot. Values are estimated from visual inspection. For an exact audit, share the Figma file or component code.
    • Korean: ⚠️ 중간 신뢰도 감사 — 스크린샷을 기반으로 했습니다. 값은 시각적 검토에 의해 추정되었습니다. 정확한 감사를 위해 Figma 파일 또는 컴포넌트 코드를 공유해 주세요.
  • Apply a −50% deduction modifier to all 🟡 Warning and 🟢 Tip issues that depend on exact values. Only 🔴 Critical and 🚫 Blocker visual issues (clear contrast failures, missing states visible in screenshot) take full deductions.
  • 🚫 Blockers on screenshots: Only flag as Blocker if the violation is visually unambiguous (e.g. clearly failing contrast, clearly missing label). Downgrade to 🔴 Critical with a note if confidence is insufficient to confirm a legal violation.

At 🟢 High confidence (Figma or code):

  • Cite exact values in every issue: "padding: 13px — should be 12px or 16px (8pt grid)"
  • Reference specific layer names (Figma) or line numbers (code)
  • Full deductions apply, no modifiers

Step 1.6: Code Input Extraction (HTML / CSS / React / Vue)

When the input is code (not a Figma file), extract audit data using this parallel spec. This ensures the Type Scale Stack, component health, consistency checks, and microcopy analysis all work on code input — not just Figma.

Code Audit Scope Selector

Before extracting values, check the size and nature of the input:

If input is a single component file (< 150 lines):
  → Proceed directly with full audit. No need to ask.

If input is a large file or multiple files (150+ lines or 3+ files):
  → Present scope widget before auditing:
    question: "This is a large codebase — what should I focus on?"
    type: multi_select
    options:
      "Full audit — everything / 전체 감사"
      "Accessibility only (Cat 6, 7) / 접근성"
      "Design tokens & consistency (Cat 5, 17) / 토큰 & 일관성"
      "Responsive & layout (Cat 3, 10) / 반응형 & 레이아웃"
      "Typography & color (Cat 1, 2) / 타이포그래피 & 색상"
      "Motion & states (Cat 8, 11) / 모션 & 상태"

If the user has already indicated focus in their message
(e.g. "check the accessibility", "is the contrast ok"):
  → Skip the widget, infer the scope, note it in the report header.

State the audit scope at the top of the report under the REPORT HEADER:

  • English:
    "Scope: [selected categories] — [N] files, ~[N] lines"
  • Korean:
    "범위: [선택된 카테고리] — [N]개 파일, 약 [N]줄"

Framework Detection — Do This First

Before extracting any values, identify the framework. This changes how values are extracted and how fixes are written.

Signals to look for:

  HTML/CSS (vanilla)
    → <div>, <button>, <input> tags with class="" or style=""
    → Standalone .css or .html files
    → No import statements or JSX syntax

  React / JSX
    → import React / import { useState } / import { ... } from ...
    → JSX syntax: <Component />, className=, onClick=
    → .jsx or .tsx file extension
    → Possible: styled-components, CSS modules, inline styles

  Vue
    → <template>, <script setup>, <style scoped> blocks
    → v-bind, v-model, :class, @click directives
    → .vue file extension

  Tailwind CSS (any framework)
    → className / class values with utility prefixes:
      text-*, bg-*, p-*, m-*, gap-*, rounded-*, border-*, font-*, leading-*
    → Often combined with React or Vue

  CSS-in-JS (styled-components / emotion)
    → const Wrapper = styled.div`...`
    → css`...` template literals
    → Values are in JS template strings, not CSS files

  CSS custom properties / design tokens
    → var(--token-name) in CSS values
    → :root { --color-primary: #... } definitions

Declare the detected framework at the top of the audit:
  "Detected: React + Tailwind CSS"
  "Detected: Vue 3 (Composition API) + CSS Modules"
  "Detected: Vanilla HTML/CSS"

This declaration affects:
  - How values are extracted (see per-category specs below)
  - How fixes are written (Tailwind class swaps vs CSS property changes vs JSX prop changes)
  - Which categories get code-specific superpower checks (see Cat 6, 8, 9, 13, 17)

Typography extraction from code

Collect all unique font-size values across the codebase/component:
  - CSS: font-size declarations (px, rem, em)
  - React/Vue: inline styles, className references to utility classes (e.g. text-sm, text-lg)
  - Convert rem to px (base 16px unless overridden): 1rem = 16px, 0.875rem = 14px

Map to roles by relative size and frequency (same logic as Figma):
  - Largest → heading, next → subheading, most-frequent → body, smallest → caption

Check against typography.md rules and flag issues.
Trigger Type Scale Stack widget with extracted sizes — same as Figma path.

Color extraction from code

Collect all color values:
  - CSS: color, background-color, border-color (hex, rgb, hsl, var(--token))
  - Tailwind: color utility classes (text-gray-900, bg-white, border-blue-500)
  - CSS variables: resolve var(--color-x) to actual hex if defined in the file

For each foreground/background pair visible in context:
  - Run WCAG luminance contrast check (same algorithm as F3.5)
  - Trigger Contrast Checker widget if any pair fails

Hardcoded values (not var(--token)) → flag for Cat 17 (token coverage)

Spacing extraction from code

Collect spacing values:
  - CSS: padding, margin, gap, width, height in px
  - Tailwind: spacing utility classes (p-4 = 16px, m-3 = 12px, gap-2 = 8px)

Tailwind spacing scale: 1 unit = 4px. So p-4 = 16px ✅, p-3 = 12px ✅, p-[13px] = off-grid 🟡.

Check for off-grid values (not multiples of 4). Trigger 8pt Grid Visualizer widget on first offender.

Component health from code

Instead of layer tally, assess structural patterns:
  - Are UI elements defined as reusable components/functions? (React: <Button>, Vue: <BaseInput>) → ✅
  - Are there inline one-off HTML structures with no component wrapper? → 🟡
  - Count unique component definitions vs total render instances

If the input is a single component file (not an app):
  - Note "Single component input — cross-file component coverage cannot be assessed"
  - Audit the internal structure for prop hygiene, named slots, etc.

Microcopy extraction from code

Collect all string literals that appear in the UI:
  - Button children: <button>Submit</button>, <Button>OK</Button>
  - Input placeholders: placeholder="Enter email"
  - Labels: <label>First name</label>
  - Error strings: "Invalid input", "Required"
  - Empty state text

Apply the same per-role checks as Cat 12. Cite line numbers instead of node IDs:
  🟡 placeholder="eg: 5" (line 47) — informal prefix. Use e.g. 5 or a unit hint.

2-frame comparison from code

If the user shares 2+ component files or code snippets in the same session:
  - Extract and compare button border-radius, primary color, body font-size across files
  - Flag cross-file inconsistencies the same way as the Figma 2-frame compare

Step 1.7: Code Fix Output Format

When the input is code, fixes must be output as actual before/after code diffs — not descriptions. This is the code equivalent of F5 (Figma fix loop).

Fix format rules

Always output diffs in this format for every code fix:

Issue: [issue description]
File: [filename or "component" if single file] · Line [N] (if known)

Before:
  [exact original code — 1–5 lines of context]

After:
  [corrected code with the fix applied]

Why: [one-sentence reason referencing the rule]

Framework-aware fix output:

Vanilla CSS fix:
  Before: padding: 13px;
  After:  padding: 12px;   /* snapped to 4pt grid */

Tailwind fix:
  Before: className="p-[13px]"
  After:  className="p-3"  /* 12px — nearest grid value */

React inline style fix:
  Before: style={{ padding: 13 }}
  After:  style={{ padding: 12 }}

CSS custom property fix (prefer this over hardcoded):
  Before: color: #8a8a8a;
  After:  color: var(--color-text-secondary);

Aria fix:
  Before: <img src="logo.png" />
  After:  <img src="logo.png" alt="Company logo" />

Focus style fix:
  Before: button:focus { outline: none; }
  After:  button:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }

Fix grouping: When the same issue repeats across lines, show one representative diff and note the others:

  Fix shown for line 23. Apply the same pattern to lines 31, 47, 89.

When to offer a fix loop: After completing the audit report, if there are 🔴 Critical issues, offer:

  • English: "Want me to output corrected code for all critical issues?"
  • Korean: "모든 중요 문제에 대한 수정된 코드를 출력해 드릴까요?"

If yes → output diffs for every critical in severity order, then warnings if requested.


Step 2: Run the Design Audit

Check each category. Skip clearly inapplicable ones. Mark each issue:

  • 🔴 Critical — Breaks usability or accessibility. Must fix. (-8 points each)
  • 🟡 Warning — Weakens the design. Should fix. (-4 points each)
  • 🟢 Tip — Polish-level improvement. Nice to have. (-1 point each)

Scoring formula (always show this explicitly in every report):

Score = 100 − (criticals × 8) − (warnings × 4) − (tips × 1)

Show the arithmetic inline so the user can see exactly how the score was reached. Example:

Score: 100 − (3 × 8) − (5 × 4) − (2 × 1) = 100 − 24 − 20 − 2 = 54/100

Never just show the final number. The breakdown makes the score feel earned and tells the user exactly what to fix to move the needle. If 🟡 Medium confidence applies a −50% modifier, show that too:

Score: 100 − (2 × 8) − (3 × 4 × 0.5) − (1 × 1 × 0.5) = 100 − 16 − 6 − 0.5 = 77/100 (medium confidence modifier applied to warnings/tips)


CATEGORY 1: Typography

Full rules →

references/typography.md

  • Hierarchy — Clear visual difference between headings, subheadings, body? (Size, weight, or color should vary meaningfully.)
  • Font count — Max 2 font families. More = visual chaos.
  • Body text size — Min 14px, 16px preferred. Never below 12px for any visible text.
  • Line height — 1.4–1.6× the font size for body text.
  • Line length — 60–80 characters per line. Wide lines (100+ chars) tire the eyes.
  • Text contrast — WCAG AA: 4.5:1 for normal text, 3:1 for large text (18px+).
  • Alignment — Don't randomly mix left-aligned and center-aligned body text.

→ Widget trigger: Always attempt to trigger the Type Scale Stack widget on Figma or code input — do not wait for a typography issue to be found first. Extract all font sizes from

get_design_context
data directly:

From get_design_context, collect all unique fontSize values across all text nodes.
Map each size to its likely role based on relative size and usage frequency:
  - Largest 1–2 sizes → heading (h1, h2)
  - Mid-range sizes → subheading / label (h3, h4, label)
  - Most frequent size → body
  - Smallest sizes → caption / helper

Then check:
  - Any body text fontSize < 14 → 🔴 Critical
  - Two sizes within 2px of each other → 🟡 Warning (too close to distinguish)
  - Same fontSize used for visually different roles → 🟡 Warning (relies on weight alone)
  - No size below 12px → ✅
  - Scale ratio between adjacent levels (e.g. body→h2) < 1.2 → 🟡 Warning (too flat)
  - More than 5 distinct font sizes → 🟡 Warning (scale too complex)

Pass the collected sizes and roles as data to the widget.
If get_design_context returns no text nodes or fontSize data, skip the widget silently.

Introduce with one sentence in the user's detected language:

  • English: "Here's how your type scale stacks up visually."
  • Korean: "타입 스케일을 시각적으로 확인해 보세요."

CATEGORY 2: Color & Contrast

Full rules →

references/color.md

  • WCAG contrast — Normal text ≥ 4.5:1, large text ≥ 3:1, UI components ≥ 3:1.
  • Color-only meaning — Never use color as the only signal. Pair with icon or text.
  • Palette size — 1 primary + 1 accent + neutrals beats many colors.
  • Color consistency — Same color = same meaning everywhere.
  • Low-contrast combos — Light gray on white, yellow on white, white on light blue all commonly fail.

→ Widget trigger: If any contrast issue is found — whether from

get_variable_defs
color token analysis (preferred) or from visual screenshot assessment — use the Visualizer to render the Contrast Checker widget. Pre-populate the foreground and background hex values from the failing pair. When contrast was calculated from design tokens, show the exact token names alongside the hex values (e.g.
color/text/secondary #8A8A8A on color/surface/default #FFFFFF — ratio: 3.1:1 ❌
). The widget shows all 5 WCAG pass/fail levels live, a real text preview at heading/body/label sizes, and automatically calculates the nearest passing hex value as a fix suggestion. Introduce with one sentence in the user's detected language:

  • English: "Use this to test fixes — the widget calculates the exact color adjustment needed."
  • Korean: "이 도구로 수정 사항을 바로 테스트해 보세요 — 통과 가능한 정확한 색상값을 자동으로 계산해 드립니다."

CATEGORY 3: Spacing & Layout

Full rules →

references/spacing.md

  • 8-point grid — Spacing/sizing should be multiples of 8 (or 4). Arbitrary values look accidental.
  • Proximity — Related items close together, unrelated far apart.
  • Padding consistency — Uniform padding inside cards/containers.
  • Breathing room — Enough whitespace? Dense UIs overwhelm.
  • Alignment — Elements align to a shared edge or center.
  • Content margins — Consistent left/right margins, not edge-to-edge.

→ Widget trigger: If any off-grid spacing value is found, use the Visualizer to render the 8pt Grid Visualizer widget. Pre-populate the input with the first offending value found. The widget shows the value on a ruler alongside valid grid neighbours, calculates the snap distance, and pre-colors all common spacing values as on/off-grid. Introduce with one sentence in the user's detected language:

  • English: "Here's where that value sits on the grid and what to snap it to."
  • Korean: "해당 값이 그리드에서 어디에 위치하는지, 어디로 맞춰야 하는지 확인해 보세요."

📋 Code input: direct checks available (run these automatically)

Off-grid value detection:
  → Collect all padding, margin, gap, width, height values in px
  → Flag any value not divisible by 4 → 🟡 Warning
  → Flag any value not divisible by 8 → 🟢 Tip (4pt is acceptable, 8pt is preferred)
  → Tailwind: arbitrary values like p-[13px], gap-[22px] → 🟡
  → Tailwind standard classes (p-4, gap-3) are on-grid by definition → ✅

  Deduplicate: if the same off-grid value appears 5+ times, report once with count:
    🟡 "padding: 13px — appears in 7 places. Snap to 12px (p-3) or 16px (p-4)."

Padding consistency:
  → Cards/panels with mismatched padding sides (paddingTop ≠ paddingLeft etc.) → 🟡
    Exception: intentional asymmetric padding (e.g. more horizontal than vertical) is fine
    if it appears consistently across all similar components.
  → Mixed shorthand: some components use padding: 16px, others padding: 16px 24px → 🟡

z-index escalation:
  → z-index values outside expected ranges (see spacing.md z-index table) → 🟡
  → z-index: 9999 or z-index: 99999 on non-dev-tool elements → 🟡
  → Multiple elements with the same z-index in overlapping contexts → 🟡

Content margin check:
  → Body/main container with no max-width → 🟢 Tip (content stretches on wide screens)
  → max-width > 1440px on body text containers → 🟢 Tip
  → margin: 0 with no padding on outermost container → 🟡 (content touches screen edge)

Logical properties (RTL safety):
  → margin-left / margin-right used in layout (not decorative) → 🟢 Tip
    (prefer margin-inline-start / margin-inline-end for RTL compatibility)
  → padding-left / padding-right on nav or directional containers → 🟢 Tip

CATEGORY 4: Visual Hierarchy & Focus

  • One primary action per screen — One thing should be obviously most important.
  • Reading patterns — Users scan in F or Z patterns. Key info along those paths.
  • Size = importance — Bigger = more important. Check it maps correctly.
  • Contrast = importance — High contrast = foreground. Check it maps correctly.

CATEGORY 5: Consistency

Corner radius full rules →

references/corner-radius.md

  • Component reuse — Buttons, inputs, cards identical throughout. No one-off styles.
  • Icon family — All icons from the same set (same style, same stroke weight).
  • Corner radius scale — Radii should come from a fixed set (e.g. 4, 8, 12, 16, 24px, full). Arbitrary values (7px, 11px) look accidental.
  • Nested radius rule — When an element sits inside another, outer radius = inner radius + padding. If the inner element has 8px radius and 12px padding, the outer must be ~20px. Mismatched nesting makes corners look "poking out."
  • Size-proportional radius — Larger elements need larger radii. A small badge with 12px radius looks right. A large modal with 4px radius looks barely rounded.
  • Pill shapes are intentional — border-radius ≥ 50% of height creates a pill. Should be deliberate (tags, toggles, badges) not accidental.
  • Zero radius is a choice — Sharp corners (0px) should be a design language decision, not a forgotten default.
  • Contextual radius — Modals/sheets anchored to screen edges should have rounded top corners, square bottom. Floating elements fully rounded.
  • Interaction states — Hover, active, disabled states all visually distinct.

2-frame consistency compare mode: When the audit session has 2+ frames audited (e.g. file has multiple pages, or the user shares a second frame for comparison), automatically cross-check these values between frames:

Cross-frame checks (run silently, report only mismatches):
  - Button corner radius: same value on both frames?
  - Primary button fill color: same hex/token?
  - Body font size: same value?
  - Input field height: same?
  - Primary heading font weight: same?
  - Icon style: outline vs filled — consistent?

Report cross-frame inconsistencies as:
  🟡 Warning: "[Property] differs between frames: [Frame A] = [value], [Frame B] = [value]"
  Example: "Button corner radius: 8px on NTIR form, 4px on Dashboard screen — pick one."

Only run cross-frame checks when you have context data for 2+ frames in the session.
Single-frame audits skip this silently — do not mention it.

CATEGORY 6: Accessibility (A11y / WCAG)

  • Touch targets — Interactive elements ≥ 44×44px (iOS) or 48×48dp (Material).
  • Focus states — Visible focus ring on every keyboard-navigable element.
  • Alt text readiness — Meaningful images need alt text. Decorative =
    aria-hidden
    .
  • Form labels — Visible label on every input. Placeholder alone is not a label.
  • Error messages — Text description of errors, not just red border/color change.
  • Reading order — Visual order matches logical/DOM order for screen readers.
  • Motion sensitivity — Animations respect
    prefers-reduced-motion
    .
  • Link clarity — Links distinguishable from text by more than color alone.

📋 Code input: deeper checks available (run these automatically)

When auditing HTML/React/Vue code, check directly:

  aria-label / aria-labelledby
    → Every <button> or <a> without visible text must have aria-label
    → Icon buttons: <button aria-label="Close"><Icon /></button> ✅
    → Missing: <button><Icon /></button> → 🔴 Critical

  alt attributes on <img>
    → <img src="..." /> (no alt) → 🔴 Critical
    → <img src="..." alt="" /> (decorative, intentional empty) → ✅
    → <img src="logo.png" alt="Company logo" /> → ✅

  focus styles
    → button:focus { outline: none } or *:focus { outline: none } → 🔴 Critical
    → :focus-visible with visible outline → ✅
    → Search for outline: none / outline: 0 across all CSS

  role attributes
    → Custom interactive elements (<div onClick=...>) missing role="button" → 🟡
    → Landmark roles present: role="main", role="nav", role="complementary" → ✅

  tabIndex misuse
    → tabIndex > 0 on any element → 🟡 (breaks natural tab order)
    → tabIndex="-1" on programmatically focused elements → ✅

  input label association
    → <input id="email" /> must have <label for="email"> or aria-label → 🔴 if missing
    → Placeholder-only inputs → 🔴

  Color contrast (code path)
    → Extract foreground/background pairs from CSS, run WCAG check programmatically
    → More precise than visual estimate — cite exact ratio

CATEGORY 7: Forms & Inputs

  • Label placement — Labels above inputs (not beside or inside). Fastest to scan.
  • Input sizing — Wide enough to show typical content.
  • Required field marking — Asterisk (*) with legend, or label optional fields instead.
  • Validation timing — Validate on blur (leaving field), not only on submit.
  • Error placement — Error messages directly below the relevant field.
  • Field grouping — Related fields visually grouped (less space within, more between groups).
  • Submit button state — Loading state while submitting. Disable after first click.

📋 Code input: direct checks available (run these automatically)

input type correctness:
  → <input type="text"> for email → 🟡 should be type="email"
  → <input type="text"> for password → 🔴 should be type="password"
  → <input type="text"> for phone → 🟡 should be type="tel"
  → <input type="text"> for numbers → 🟡 should be type="number" or inputMode="numeric"
  → <input type="text"> for URLs → 🟡 should be type="url"
  → <input type="submit"> instead of <button type="submit"> → 🟢 Tip (button is more styleable)

autocomplete attributes:
  → Name fields missing autocomplete="name" / autocomplete="given-name" → 🟡
  → Email fields missing autocomplete="email" → 🟡
  → Password fields missing autocomplete="current-password" or "new-password" → 🟡
  → Credit card fields missing autocomplete="cc-number" etc. → 🟡
  → Cite the field and the missing value

required + aria-required:
  → <input required> without aria-required="true" → 🟢 Tip (redundant but explicit for AT)
  → Required inputs with no visual indicator (* or "(required)" label) → 🟡

aria-describedby for error messages:
  → Error message element exists but not linked via aria-describedby on its input → 🟡
  → Correct: <input aria-describedby="email-error"> ... <span id="email-error">...</span>

fieldset + legend for grouped inputs:
  → Radio groups or checkbox groups without <fieldset><legend> → 🟡
  → Only one radio/checkbox option → skip this check

inputMode for mobile keyboards:
  → Numeric inputs missing inputMode="numeric" or inputMode="decimal" → 🟢 Tip
  → This triggers the correct soft keyboard on mobile

novalidate + custom validation:
  → <form> without novalidate when custom validation JS exists → 🟡 (browser + custom = double errors)
  → <form novalidate> with no custom validation JS visible → 🟡 (validation silently disabled)

disabled vs readonly:
  → <input disabled> when the intent is read-only display → 🟢 Tip
    (disabled excludes from form submission; readonly keeps the value but prevents editing)

CATEGORY 8: Motion & Animation

Full rules →

references/animation.md

  • Purpose — Every animation orients, gives feedback, or shows a relationship. No pure decoration.
  • Duration — UI transitions: 150–300ms. Page transitions: 300–500ms. Longer feels sluggish.
  • Easing — Ease-out for entering elements, ease-in for exiting. Linear feels mechanical.
  • Reduced motion — Non-animated version for
    prefers-reduced-motion
    users.
  • No infinite autoplay loops — Distract and exhaust users. Pause after 3 loops or on hover.

📋 Code input: direct checks available (run these automatically)

prefers-reduced-motion:
  → Search for @media (prefers-reduced-motion: reduce) in all CSS/styled-components
  → If any animation or transition exists and no reduced-motion query found → 🔴 Critical
  → Correct pattern:
      @media (prefers-reduced-motion: reduce) {
        *, *::before, *::after {
          animation-duration: 0.01ms !important;
          transition-duration: 0.01ms !important;
        }
      }

animation-duration values:
  → Extract all transition: and animation: duration values
  → > 500ms on a UI interaction (not page transition) → 🟡 Warning
  → > 1000ms on anything except intentional loading → 🔴 Critical
  → linear easing on transitions → 🟡 (cite the property and value)

animation-iteration-count:
  → infinite on any element without a pause/hover mechanism → 🟡 Warning
  → Cite element selector and property

CSS transition on all properties:
  → transition: all ... → 🟡 Warning (causes jank, prefer specific properties)
  → transition: color 200ms ease-out → ✅

CATEGORY 9: Dark Mode (if applicable)

  • Not just inverted — Dark mode requires redesigned colors, not flipped ones.
  • Background depth — Lighter dark grays for elevated surfaces (cards, modals). Not pure black.
  • Saturation — Reduce vivid brand colors in dark mode — they look garish on dark.
  • Shadow replacement — Use lighter surface colors for elevation instead of shadows.
  • Icon & image legibility — Icons/images still readable on dark backgrounds.

📋 Code input: direct checks available (run these automatically)

Detect if dark mode is implemented:
  → Search for @media (prefers-color-scheme: dark) in CSS
  → Search for [data-theme="dark"] or .dark selector patterns
  → Search for dark: utility prefix (Tailwind dark mode)
  → If any: audit dark mode implementation. If none: note as 🟢 Tip if product likely needs it.

If dark mode is found, check:

  Color swap pattern:
    → ✅ Good: CSS custom properties swapped in dark media query
        :root { --bg: #ffffff; --text: #111111; }
        @media (prefers-color-scheme: dark) { :root { --bg: #1a1a1a; --text: #f0f0f0; } }
    → 🔴 Bad: Separate hardcoded hex values in dark selectors (token system is broken)
        .dark .card { background: #1c1c1c; color: #ffffff; } ← hardcoded

  Pure black backgrounds:
    → background: #000000 or bg-black in dark mode → 🟡 Warning
    → Prefer #0f0f0f–#1e1e1e range for depth

  Contrast in dark mode:
    → Run WCAG check on dark mode color pairs too (not just light mode)
    → Dark mode often passes light mode checks but fails its own

  Tailwind dark mode:
    → Check if darkMode: 'class' or 'media' is configured
    → Inconsistent dark: prefix usage across components → 🟡

CATEGORY 10: Responsive & Adaptive

  • Breakpoints — Mobile (320–480px), tablet (768px), desktop (1024px+) considered.
  • No overflow — Long words or fixed-width containers don't break on small screens.
  • Mobile touch targets — Bigger targets and more spacing than desktop.
  • Image scaling — Images scale without awkward cropping or overflow.
  • Type scaling — Large desktop headings (48px) scaled down to 28–32px on mobile.

📋 Code input: direct checks available (run these automatically)

Breakpoint coverage:
  → Collect all @media queries in CSS/styled-components/Tailwind
  → If only one breakpoint found (or none) → 🟡 Warning
  → If no mobile-first breakpoints (min-width) → 🟡 (desktop-first with max-width is harder to maintain)
  → Tailwind: check for sm:, md:, lg:, xl: prefix usage — missing sm: on any layout element → 🟡
  → Flag the specific elements that have no responsive variant

Fixed-width traps:
  → width: [value]px on containers (not icons or images) → 🟡 (use max-width or %)
  → Fixed pixel widths > 480px with no responsive override → 🔴 (will overflow on mobile)
  → min-width values that exceed mobile viewport (320px) → 🟡

Overflow risks:
  → overflow: hidden on a container without a max-width → 🟡 (clips content on small screens)
  → Long unbreakable strings: no word-break or overflow-wrap rule on text containers → 🟡
  → white-space: nowrap on text that could be long → 🟡

Image responsiveness:
  → <img> without max-width: 100% or w-full → 🟡 (overflows container on small screens)
  → <img> with fixed width/height attributes and no CSS override → 🟡
  → Missing srcset or sizes attributes on large hero images → 🟢 Tip (performance)
  → object-fit missing on images inside fixed-height containers → 🟡

Viewport meta tag (HTML only):
  → Missing <meta name="viewport" content="width=device-width, initial-scale=1"> → 🔴 Critical
    (without this, mobile browsers render at desktop width)

Font size on mobile:
  → body font-size < 16px with no responsive override → 🟡
    (iOS Safari auto-zooms on inputs with font-size < 16px)
  → Input font-size < 16px → 🟡 (triggers zoom on focus on iOS)

Tailwind responsive audit:
  → Elements using fixed Tailwind width classes (w-96, w-80) without sm:/md: override → 🟡
  → Text size classes without responsive scaling (text-5xl with no sm:text-3xl) → 🟡
  → hidden / flex / block classes without responsive context → note if suspicious

Viewport units:
  → height: 100vh on mobile without dvh fallback → 🟡
    (100vh includes browser chrome on mobile, causing content to be hidden)
  → Correct: height: 100dvh (dynamic viewport height) or min-height: 100svh

CATEGORY 11: Loading, Empty & Error States

The forgotten 30% — most beginner UIs only design the "happy path." Read

references/states.md
for full guidance.

  • Loading state — Every data fetch needs a loading indicator. Skeleton screens preferred over spinners for content-heavy layouts. Never show a blank screen.
  • Empty state — What does an empty list, inbox, or dashboard look like? Should include an illustration or icon, a friendly explanation, and a clear next action ("Create your first task →").
  • Error state — Network failures, server errors, and not-found pages need their own designed state. Not just a console error or blank screen.
  • Partial failure — What if only some data loads? Design for partial states, not just all-or-nothing.
  • Success state — After a form submission or action, confirm it worked. A toast, a green banner, or a state change — something must close the loop.
  • Disabled state — Disabled buttons and inputs should look visually distinct (reduced opacity, no pointer cursor) and ideally explain why they're disabled.
  • Consistency — Loading/empty/error states should match the overall visual style — not be plain browser defaults or unstyled fallbacks.

→ Widget trigger: If any missing state is found, always render the States Coverage Map widget — even for a single missing state. Pre-populate the grid with the components identified in the audit and mark states as present, missing, or N/A based on what was observed. Mark cells as N/A only when a state genuinely cannot apply to that component (e.g. "Empty" on a Button). Introduce with one sentence in the user's detected language:

  • English: "Here's the full picture of which states are designed and which are missing."
  • Korean: "어떤 상태가 디자인되어 있고 어떤 상태가 빠져 있는지 전체 현황을 확인해 보세요."

CATEGORY 12: Content & Microcopy

The words inside a UI are part of the design. Read

references/microcopy.md
for full guidance.

On Figma/code input: read every text node.

get_design_context
returns all text content. Extract and check each one — do not guess. Group them by role:

Text content extraction (Figma + code):
  1. Collect all text node values from get_design_context
  2. Classify each by role:
     - CTA buttons: text nodes inside button components
     - Labels: text nodes associated with inputs (above/beside)
     - Placeholders: text nodes with placeholder-style content ("Search", "Enter...", "eg:")
     - Error messages: text nodes near error states or with error styling
     - Empty state messages: text nodes in empty/zero-state frames
     - Section headers / titles: largest text nodes in a section
  3. Apply checks per role (see below)
  4. Cite the exact text content and node ID in each issue
     e.g. 🟡 "Placeholder 'eg: 5' (node 68:27994) — informal prefix. Use '0' or unit hint."

Per-role checks:

  • Button labels are verbs — "Save Changes", "Send Message" not "OK", "Submit", "Yes"
  • Error messages are human — "Invalid input" → 🔴. "Please enter a valid email" → ✅
  • Placeholder ≠ label — Placeholders hint at format (e.g. "name@example.com"), never replace a label. Flag any placeholder that duplicates its label exactly.
  • Placeholder prefix style — "eg:", "e.g." → 🟡 informal. Use the example value directly or a unit label.
  • Destructive actions are explicit — "Delete" dialogs should name what's being deleted. "Are you sure?" alone → 🟡
  • Consistent terminology — Flag if the same concept uses different words across text nodes (e.g. "workspace" and "project" used interchangeably)
  • Tone consistency — Formal in one section, casual in another → 🟡
  • No lorem ipsum — Any "lorem ipsum" or "placeholder text" string → 🔴 Critical at Dev handoff or later
  • Empty states have direction — "No results found" alone → 🟡. Should include a next action.
  • Required field legend — If * is used for required fields, check for a "* Required fields" legend somewhere in the frame. Missing → 🟢 Tip.

CATEGORY 13: Internationalization & RTL Support (if applicable)

Only audit this category if the product targets multiple languages or RTL locales (Arabic, Hebrew, Persian, Urdu). Read

references/i18n.md
for full guidance.

  • No hardcoded strings — All visible text should come from a translation file, not be baked into the component. Check for any hardcoded labels, tooltips, or error messages.
  • Text expansion budget — German and Finnish can be 30–40% longer than English. Buttons, labels, and nav items must accommodate longer text without breaking layout. Test with a long string.
  • RTL layout mirroring — In RTL languages, the entire layout flips: left becomes right. Navigation, icons, progress indicators, and reading direction all reverse. Use
    dir="rtl"
    and CSS logical properties (
    margin-inline-start
    instead of
    margin-left
    ).
  • RTL-safe icons — Directional icons (arrows, chevrons, back buttons) must flip in RTL. Non-directional icons (heart, star, trash) stay the same.
  • Date, time & number formats — These vary by locale. Don't hardcode formats like "MM/DD/YYYY" — use locale-aware formatting (e.g.,
    Intl.DateTimeFormat
    ).
  • Currency & units — Symbol position and decimal separators differ by locale (€1,234.56 vs 1.234,56 €). Never assume.
  • No text in images — Images with embedded text can't be translated. Use CSS overlays or separate text layers instead.
  • Font support — Does the chosen font support all target scripts? Latin fonts won't render Arabic or CJK characters — a system fallback font will kick in and look inconsistent.

📋 Code input: direct checks available (run these automatically)

Hardcoded string detection:
  → Scan all JSX/template content for bare string literals inside UI elements
  → <button>Submit</button> when no i18n wrapper → 🟡 if i18n is likely needed
  → <p>No results found</p> hardcoded → 🟡
  → Compare against: t('key'), i18n.t('key'), $t('key'), <FormattedMessage id="..."/> — these are ✅
  → Only flag as 🔴 if evidence of multi-language intent exists (e.g. i18n library imported but some strings not wrapped)

RTL CSS properties:
  → margin-left / margin-right / padding-left / padding-right / text-align: left → 🟡 if RTL needed
  → margin-inline-start / padding-inline-end / text-align: start → ✅ logical properties
  → position: absolute with left: / right: without RTL override → 🟡

Intl API usage:
  → Hardcoded date formats like "MM/DD/YYYY" or toLocaleDateString() without locale → 🟡
  → new Intl.DateTimeFormat(locale, options) → ✅
  → Hardcoded currency symbols ($, €) outside of a locale formatter → 🟡

dir attribute:
  → Check for dir="rtl" implementation pattern on root or document
  → CSS [dir="rtl"] selectors for flip overrides → ✅

Only run this category if:
  - An i18n library is imported (react-i18next, vue-i18n, next-intl, etc.), OR
  - The user explicitly asks for i18n review, OR
  - The file contains non-English strings
  Otherwise: skip silently.

CATEGORY 14: Elevation & Shadows

Full rules →

references/elevation.md

  • Shadow scale — Shadows should come from a defined scale (e.g. sm, md, lg, xl) — not arbitrary values. Each level should be used consistently for the same type of element.
  • Shadow = elevation — Shadows communicate how high above the page an element floats. Cards sit low (subtle shadow), modals sit high (strong shadow), tooltips highest. Check the hierarchy makes sense.
  • Shadow color — Shadows should use a dark, slightly saturated color (e.g.
    rgba(0,0,0,0.08)
    ) — never pure black. On colored backgrounds, tint the shadow with the surface color.
  • No shadows in dark mode — Shadows are invisible on dark backgrounds. Use lighter surface colors for elevation instead (e.g. a card is slightly lighter gray than the page background).
  • No decorative shadows — Shadows should only appear on elevated elements. Don't use shadows purely for decoration or emphasis on flat elements.
  • Consistent blur & offset — A consistent offset-to-blur ratio (e.g. offset-y = 1/3 of blur) makes shadows feel physically grounded. Mismatched values look amateur.
  • Multiple light sources — Don't combine a top-shadow and a bottom-shadow on the same element unless intentional. Pick one light source direction and stick to it.

CATEGORY 15: Iconography

Full rules →

references/iconography.md

  • Consistent icon family — All icons from the same set (e.g. all Phosphor, all Lucide, all Material). Never mix outline icons from one library with filled icons from another.
  • Consistent style within family — Stick to one style: all outline, all filled, or all duotone. Mixing styles inside one library looks inconsistent.
  • Optical sizing — Icons should be sized at standard grid-friendly values: 16, 20, 24, 32, 40, 48px. Avoid 18px, 22px, 26px — they fall between optical grid lines.
  • Stroke weight consistency — Outline icons have a stroke weight. Don't use 1px icons next to 2px icons — they feel mismatched even when both are "outline."
  • Touch target padding — Icons used as interactive buttons need padding to reach 44×44px minimum. A 24px icon needs 10px padding on each side.
  • Icon meaning consistency — The same icon should mean the same thing everywhere. Don't use a star for both "favourite" and "rating" in the same product.
  • Label pairing — Icons without labels are ambiguous for non-expert users. Always pair with a visible label or tooltip. Exception: universally understood icons (✕ close, ☰ menu, ⌕ search).
  • Optical alignment — Icons often have invisible padding baked in. When aligning icons with text, align to optical center, not bounding box edge.

CATEGORY 16: Navigation Patterns

Full rules →

references/navigation.md

  • Clear current location — Users should always know where they are. Active nav items must be visually distinct (color, weight, indicator bar) — not just slightly different.
  • Tabs vs nav — Tabs switch between views of the same content. Nav moves between different sections. Don't use tabs for top-level navigation or nav for in-page switching.
  • Breadcrumbs for depth — Any page more than 2 levels deep needs breadcrumbs. They should show the full path and every crumb should be clickable (except the current page).
  • Back button behavior — "Back" should always go to the previous screen, not the previous URL. In modals and flows, "Back" should not close the entire flow unexpectedly.
  • Mobile navigation — Bottom navigation bar for 3–5 primary destinations on mobile. Hamburger menu acceptable for secondary items but not primary navigation.
  • Active state contrast — Active/selected nav items must meet 3:1 contrast ratio against inactive items — not just a subtle color shift that's easy to miss.
  • Overflow handling — What happens when there are too many nav items? Tabs should scroll horizontally or collapse into a "More" dropdown. Never let them clip or wrap awkwardly.
  • Navigation consistency — The nav should look and behave identically on every page. Never change which items appear, their order, or their style between sections.

📋 Code input: direct checks available (run these automatically)

Semantic nav element:
  → Navigation container uses <nav> element → ✅
  → Navigation built with <div> with no role="navigation" → 🟡 Warning
  → Multiple <nav> elements without aria-label to distinguish them → 🟡
    Correct: <nav aria-label="Primary"> and <nav aria-label="Footer">

Active state implementation:
  → aria-current="page" on the active nav item → ✅
  → Active state relies only on a CSS class (.active) with no aria-current → 🟡
    (class alone doesn't communicate current page to screen readers)
  → Active state only changes color with no weight/background/indicator → 🟡
    (color alone fails colorblind users — needs a secondary signal)

Skip navigation link:
  → First focusable element is a skip link targeting #main-content → ✅
  → No skip link found → 🟡 Warning
  → Skip link exists but target ID (#main-content) missing from DOM → 🔴 Critical

Tab vs nav misuse:
  → <nav> containing elements that switch views of the same page → 🟡
    (should be role="tablist" + role="tab" children)
  → role="tablist" used for top-level navigation between pages → 🟡
    (should be <nav> with <a> links)

Keyboard navigability:
  → Nav items are <div>/<span> with onClick but no role="button" + tabindex="0" → 🔴
  → Nav items are <a> elements with valid href → ✅
  → Dropdown menus with no keyboard handler (Escape to close, arrow keys) → 🟡

Breadcrumb implementation:
  → <nav aria-label="Breadcrumb"> wrapping the crumb list → ✅
  → Last breadcrumb item has aria-current="page" → ✅
  → Intermediate breadcrumb items are plain text, not links → 🟡
  → No breadcrumb on routes 3+ levels deep → 🟡
  → Breadcrumb list uses <ol> (ordered) not <ul> → ✅ (order matters for breadcrumbs)

CATEGORY 17: Design Tokens & Variables Health (if applicable)

Audit this when reviewing Figma files or codebases with a design system. Read

references/tokens.md
for full guidance.

  • Colors are tokenized — No hardcoded hex values in components. Colors should reference a token (e.g.
    color.primary.500
    ,
    --color-brand
    ), not
    #7c3aed
    directly.
  • Spacing is tokenized — Spacing values reference a scale token, not arbitrary pixel values.
  • Typography is tokenized — Font size, weight, and line-height come from defined text style tokens, not ad-hoc values per component.
  • Radius is tokenized — Corner radius values reference the radius scale, not hardcoded numbers.
  • Shadow is tokenized — Box shadows reference elevation tokens, not custom values per element.
  • Token naming is semantic — Tokens should describe purpose, not appearance.
    color.background.danger
    is good.
    color.red.500
    used directly in a component is not — it breaks when you need to change the danger color.
  • No magic numbers — Any value that appears more than twice should be a token. Repeated one-off values are a sign the token system isn't being used.
  • Dark mode uses the same tokens — Dark mode should swap token values, not introduce new hardcoded colors. If dark mode components have their own hex values, the token system is broken.

📋 Code input: direct token audit available (run automatically — most precise path)

CSS custom property audit:
  → Find all :root { --token: value } definitions — these are the token system
  → Find all var(--token) usages in component CSS
  → Find all hardcoded values (hex, rgb, px) NOT using var()

  Compute per-category coverage:
    color_coverage    = var(--color-*) usages / total color declarations × 100
    spacing_coverage  = var(--spacing-*) / total padding/margin/gap × 100
    radius_coverage   = var(--radius-*) / total border-radius × 100
    shadow_coverage   = var(--shadow-*) / total box-shadow × 100

  Report as: "Token coverage: colors 80% · spacing 60% · radius 40% · shadows 20%"
  Any category < 50% → 🟡 Warning
  Any category < 20% → 🔴 Critical (token system not being used)

JS/TS theme object audit (styled-components / emotion / MUI):
  → Detect theme.colors.*, theme.spacing(), theme.shadows[] usage
  → Hardcoded values inside styled components: color: '#7c3aed' → 🟡
  → theme.colors.primary → ✅

Tailwind token audit:
  → Tailwind config colors/spacing/borderRadius/boxShadow → these are the tokens
  → Arbitrary values like bg-[#7c3aed], p-[13px], rounded-[7px] → each is 🟡
  → Standard scale values (bg-purple-600, p-3, rounded-lg) → ✅

Token naming check:
  → --color-red: #ff0000 (describes appearance) → 🟡 rename to --color-error or --color-danger
  → --color-error: #ff0000 (describes purpose) → ✅
  → Tokens with numeric suffixes only (--color-500) without semantic alias → 🟢 Tip

CATEGORY 18: Ethical Design & Dark Patterns

Full rules, all pattern definitions, detection signals, and the ethical persuasion reference →

references/ethics.md

This category audits for manipulative or deceptive design patterns — not design mistakes, but intentional choices that may exploit users. Read

references/ethics.md
before running this category.

When to run: Always. Every design can be checked for ethical patterns regardless of input type or stage.

Ethics severity model (different from standard audit severity):

LevelLabelMeaningScore impact
🔴DeceptiveActively misleads or coerces. Violates user trust, often consumer law.−15 pts
🟡QuestionableExploitative depending on context. Warrants review.−7 pts
🟢NotedPersuasive element present. Ethical in standard use.0 pts

Ethics Score is separate from the Overall Score. Start at 100, apply ethics deductions only. Display as: Ethics Score: X/100 alongside Accessibility Score.

Detection confidence: Always declare confidence per finding (High/Medium/Low) using the detection scope table in

references/ethics.md
. Never flag low-confidence patterns without explicit caveat.

Checklist — run all groups:

Group A: Deceptive Interface Patterns

  • Confirmshaming — Decline/cancel copy does not shame or guilt the user for choosing it
  • CTA hierarchy inversion — Accept and decline actions have equivalent visual weight (especially on consent/cookie screens)
  • Trick questions — All consent copy uses positive, unambiguous language with no double negatives
  • Disguised ads — Sponsored/promoted content is visually distinct from organic content with a clear, readable label
  • Bait and switch — CTA labels accurately describe the immediate next action
  • Hidden costs — All mandatory fees are shown from the first price display
  • Visual misdirection — Cost, commitment, and risk information meets the same visual standards as the CTA it accompanies

Group B: Coercive Flows

  • Roach motel — Cancellation/exit path is no harder than the sign-up/entry path
  • Obstruction — Unsubscribe, data deletion, and account closure are self-serve and reachable in ≤ 3 steps
  • Forced action — No non-essential data collection or permission is required to access core functionality
  • Nagging — Dismissed prompts stay dismissed; "don't show again" is permanently respected

Group C: Consent & Privacy

  • Privacy zuckering — All non-essential data sharing defaults to OFF
  • Pre-checked consent — No marketing/data-sharing checkbox is pre-checked by default
  • Interface interference — Privacy controls use consistent interaction patterns with clear state labels
  • Drip pricing — No fees are revealed only at the final checkout step

Group D: False Urgency & Scarcity

  • Countdown timers — Any timer is backed by a real server-side expiry that does not reset
  • False scarcity — Scarcity claims ("Only X left") are backed by real-time inventory data
  • False social proof — Social proof numbers ("X people viewing") reflect real data, not hardcoded or random values

Group E: Emotional Manipulation

  • Guilt-based copy — Inactivity, cancellation, and decline states are addressed neutrally, not shamefully
  • Fear appeals — Risk language is proportionate to actual risk; no exaggerated consequences for conversion
  • Toying with emotion — No patterns that deliberately engineer anxiety, FOMO, or regret as conversion mechanisms

Before flagging any pattern: Check the Ethical Persuasion reference in

references/ethics.md
. Do not flag legitimate persuasion techniques (genuine social proof, real urgency, positive progress framing, transparent anchoring).


Step 3: Score & Report

Scoring Formula

Start at 100 points. Deduct for every issue found:

SeverityDeductionWhen to use
🚫 Blocker−12 pointsViolates a legal or compliance standard — WCAG AA, GDPR, PECR, consumer protection law. Cannot ship as-is.
🔴 Critical−8 pointsBreaks usability or accessibility for a significant user population. Must fix before shipping.
🟡 Warning−4 pointsDegrades experience. Should fix.
🟢 Tip−1 pointPolish-level improvement. Nice to have.

Floor is 0 — score never goes negative.

Blocker vs Critical — the distinction: Blockers are not "worse Criticals" — they are a different class of issue. A Blocker violates an external legal or compliance standard that exists independently of design opinion. A Critical breaks usability badly but is a design quality failure, not a legal one.

Blocker examples (legal/compliance basis):
  🚫 Text contrast below WCAG AA 4.5:1 (Cat 2) — WCAG 2.1 SC 1.4.3
  🚫 Interactive element with no accessible name (Cat 6) — WCAG 2.4.6
  🚫 Keyboard-inaccessible interactive element (Cat 6) — WCAG 2.1.1
  🚫 Meaningful image missing alt text (Cat 6) — WCAG 1.1.1
  🚫 prefers-reduced-motion absent when animations exist (Cat 8) — WCAG 2.3.3
  🚫 Pre-checked marketing consent checkbox (Cat 18) — GDPR/PECR
  🚫 Non-essential cookies defaulting to ON (Cat 18) — GDPR/ePrivacy
  🚫 Skip link missing or broken (Cat 16) — WCAG 2.4.1
  🚫 Form input with no label (Cat 7) — WCAG 1.3.1

Critical examples (usability basis, not legal):
  🔴 Missing touch target size (Cat 6) — best practice, not strict law
  🔴 Missing error/empty/loading states (Cat 11)
  🔴 CTA hierarchy inversion on non-consent screens (Cat 18)
  🔴 Off-brand or broken dark mode (Cat 9)

When in doubt between Blocker and Critical: If you can cite a specific WCAG success criterion number, GDPR article, or consumer law provision — it's a Blocker. If it's a usability or design quality judgment — it's a Critical.

Issue Deduplication — Required

When the same problem appears across multiple nodes, never list it multiple times. Deduplicate into a single issue entry with an exact count and node ID list.

Rule: If the same root cause affects N nodes → one issue entry, not N entries.

Format:
  🔴 [Issue name] — affects N nodes
  Nodes: [id1], [id2], [id3] (+ N more if >5 — list first 5 only)
  Fix: [single fix that resolves all instances]

Examples:
  ✅ Good: "Off-grid column width (60.17px) — 3 nodes: 456:49851, 456:49873, 456:49895"
  ❌ Bad:  "Column 5 off-grid" + "Column 10 off-grid" + "Column 11 off-grid" (3 separate entries)

  ✅ Good: "Input field missing Error state — 10 nodes: 68:27912, 68:27927, 68:27943 (+7 more)"
  ❌ Bad:  Listing each input as a separate critical issue

Deduplication also applies to scoring:
  A repeated issue (same root cause, N nodes) counts as ONE issue for deduction purposes.
  Exception: if fixing each instance requires a separate action (different values, different
  components), count each as separate — but still group them visually in the report.

This keeps reports scannable. A report with 3 grouped issues is more actionable than one with 15 separate entries that all say the same thing.

Accessibility Score

In addition to the overall score, always surface a separate Accessibility Score combining Categories 2, 6, 7, and 16:

  • Start at 100, apply the same 4-tier deduction formula to issues in those 4 categories only
  • 🚫 Blocker issues in these categories use −12 (legal violations — WCAG AA)
  • Display as: Accessibility Score: X/100
  • If any 🚫 Blocker issues exist: append "⚠️ Contains legal compliance failures"

Scoring bands:

  • 90–100 → WCAG AA compliant — production-ready
  • 70–89 → Minor gaps, no Blockers
  • 50–69 → Significant gaps — likely has Blockers
  • < 50 → Failing — legal risk, do not ship

Always show the maths:

Score: 100 − (1 × 🚫 12) − (2 × 🔴 8) − (3 × 🟡 4) − (1 × 🟢 1) = 59/100 ⚠️ Contains legal compliance failures


Strict Output Template

Every audit report must use this exact structure — no exceptions, no reordering. Sections marked (always) appear on every audit. Sections marked (conditional) appear only when applicable.


REPORT HEADER (always)

## 🔍 Design Audit Report

**Input:** [Figma file name / component name / "Pasted HTML" / "React component"]
**Type:** [Figma MCP / HTML / React / Vue / Screenshot]
**Framework:** [detected framework — e.g. "React + Tailwind CSS"] *(code input only)*
**Confidence:** [🟢 High / 🟡 Medium / 🔴 Low] — [one-sentence reason]
**Scope:** [Frame name + page, OR filename, OR "Single component"]

*(Figma only — when 2+ pages exist)*
**File:** [N] pages — auditing '[page name]' (page [N] of [N])

*(Figma only — always)*
**Component health:** [N]% coverage · [N] detached instances · [N] unnamed layers

*(Code only — always)*
**Token coverage:** colors [N]% · spacing [N]% · radius [N]% · shadows [N]%

*(Medium confidence only)*
⚠️ **Medium confidence audit** — input was a screenshot. Values are estimated. For an exact
audit, share the Figma file or component code.

SCORES (always)

### Overall Score: [X/100]
**100 − ([N] × 🚫 12) − ([N] × 🔴 8) − ([N] × 🟡 4) − ([N] × 🟢 1) = [X]/100**
[One sentence — what dragged the score down most.]
*(Omit 🚫 line from arithmetic if no Blockers found)*

### Accessibility Score: [X/100]  *(Categories 2, 6, 7, 16)*
[Band label: WCAG AA compliant / minor gaps / significant gaps / failing]
*(Append "⚠️ Contains legal compliance failures" if any 🚫 Blockers present)*

### Ethics Score: [X/100]  *(Category 18)*
[Band label: Ethically sound / minor concerns / significant manipulation risk / deliberately deceptive]
*(Only show if any ethics issues were found, or if the audit was full scope)*

### Score by Category
| Category | Score | Issues |
|---|---|---|
| Typography | X/10 | 0 🚫, 1 🔴, 0 🟡 |
| Color & Contrast | X/10 | 1 🚫, 0 🔴, 2 🟡 |
| [other audited categories] | X/10 | ... |
*(Only include categories that were audited. Skip clearly N/A ones.)*

ISSUES (always — follow deduplication rules)

### 🚫 Blockers (−12pts each) *(legal/compliance violations — cannot ship)*
- **[Issue name]** — [What's wrong] → Fix: [Specific how-to]
  Legal basis: [WCAG SC X.X.X / GDPR Article X / PECR]
  *Nodes: [id1], [id2] (+N more)* *(Figma)* / *Line [N]* *(code)*
  → Want me to fix this? I can apply it directly.

### 🔴 Critical Issues (−8pts each)
- **[Issue name]** — [What's wrong] → Fix: [Specific how-to]
  Why: [One sentence rule reference]
  *Nodes: [id1], [id2] (+N more)* *(Figma)* / *Line [N]* *(code)*
  → Want me to fix this? I can apply it directly.

### 🟡 Warnings (−4pts each)
- **[Issue name]** — [What's wrong] → Fix: [Specific how-to]
  Why: [One sentence rule reference]

### 🟢 Tips (−1pt each)
- **[Issue name]** — [What's wrong] → Fix: [Specific how-to]

(Omit the 🚫 Blockers section entirely if none are found — do not show an empty section)


POSITIVES (always — min 2, max 4)

### ✅ What's Working Well
- [Specific genuine positive — not generic praise]
- [Specific genuine positive]

CROSS-FRAME INCONSISTENCIES (conditional — only when 2+ frames audited)

### ⚡ Cross-Frame Inconsistencies
- **[Property]** differs: [Frame A] = [value] vs [Frame B] = [value]

RE-AUDIT DELTA (conditional — only on 2nd+ audit in session)

### 📈 Progress Since Last Audit
Score: [prev] → [current] ([+/−N] pts)
✅ Fixed: [category] (+Npts), [category] (+Npts)
🔴 Still open: [N] critical issues remaining
New issues found: [N]

REPORT FOOTER (always)

---
*Audit run with Design Auditor Skill v1.2.2 · [input type] · [confidence level]*
*Re-audit after fixes to track progress. / 수정 후 재감사를 실행하여 진행 상황을 추적하세요.*

After the report, immediately render:

  1. Radar chart (Step 3b) — always
  2. Issue Priority Matrix widget — always if 3+ issues
  3. Severity filter widget — if 5+ issues
  4. "What next?" widget (Step 4) — always

Issue Priority Matrix — effort/impact heuristics:

Effort (1–10): 1–2 = single value change · 3–4 = one component rework · 5–6 = multi-component refactor · 7–9 = architectural change

Impact (1–10): 9–10 = breaks a11y or core usability · 6–8 = degrades experience · 3–5 = polish-level · 1–2 = cosmetic

Use deterministic positioning (no random jitter). Render severity as both color AND a letter inside the dot (C/W/T) for colorblind accessibility. Introduce with one sentence in the user's detected language:

  • English: "Here's every issue mapped by how hard it is to fix versus how much it will improve the design — start in the top-left."
  • Korean: "발견된 모든 문제를 수정 난이도와 개선 효과 기준으로 매핑했습니다 — 왼쪽 위부터 시작하세요."

Step 3b: Radar Chart Visualizer (always run after report)

Immediately after presenting the markdown report, use the Visualizer tool to render an interactive radar chart widget. This gives users a visual at-a-glance summary of all category scores.

How to generate it:

  • Extract the per-category scores (X/10) from the audit you just ran
  • Use only the categories that were actually audited (skip ones marked as not applicable)
  • Pass real scores — do not use placeholder data
  • Use exactly two colors:
    #534AB7
    (purple) for the filled shape and dots,
    #E24B4A
    (red) for scores ≤ 5 only
  • Label low-scoring categories (≤5) in red bold, all others in muted gray
  • Each segment and label must be clickable via
    sendPrompt()
    to drill into that category
  • Show a compact score-bar legend below the chart listing all audited categories
  • Display the overall score in the centre of the radar

Session history awareness: Store the current overall score in a JS variable accessible to the widget. If a previous score exists in the session (from a re-audit), show a delta badge next to the centre score:

  • Score improved:
    +N ↑
    in green
  • Score dropped:
    −N ↓
    in red
  • No change: omit the badge

Tone: Introduce the chart with one short sentence in the user's detected language before the Visualizer call:

  • English: "Here's a visual breakdown of how your design scores across each category — red spots are your highest-priority fixes."
  • Korean: "각 카테고리별 디자인 점수를 시각적으로 확인해 보세요 — 빨간 영역이 가장 우선적으로 수정해야 할 부분입니다."

Severity Filter

After presenting the report, offer a filter widget using ask_user_input if there are 5+ issues:

  • question: "Would you like to filter the issues?"
  • type: single_select
  • options: "Show only 🔴 Critical / 심각한 문제만" / "Show 🔴 + 🟡 / 심각 + 경고" / "Show everything / 전체 보기" / "No filter / 필터 없음"

Apply the filter and re-present only the relevant issue sections. Score and category breakdown always remain visible regardless of filter.

Re-audit: Session Progress Tracker

Maintain a running audit history across the session. Every time an audit completes, record:

  • Overall score
  • Accessibility score
  • Count of 🔴 critical / 🟡 warning / 🟢 tip issues
  • Timestamp label (e.g. "Audit 1", "Audit 2")

On re-audit, open with a delta summary before the report in the user's detected language:

  • English: "Since the last audit: score improved from [X] → [Y]. 🔴 issues down from [N] to [N]. Here's what's new..."
  • Korean: "지난 감사 이후: 점수가 [X] → [Y]로 향상되었습니다. 🔴 심각한 문제가 [N]개에서 [N]개로 감소했습니다. 변경된 내용은 다음과 같습니다..."

Then show only the changed or new issues — do not re-list resolved ones. Acknowledge wins explicitly in the user's detected language:

  • English: "✅ Fixed since last audit: Color & Contrast (+3pts), States (+4pts)"
  • Korean: "✅ 지난 감사 이후 수정됨: 색상 대비 (+3점), 상태 (+4점)"

After the radar chart, if 2+ audits exist in the session, render a second small Visualizer widget — a simple horizontal progress bar or sparkline showing score history across audits (e.g. 58 → 71 → 84). Keep it minimal: one line, scores as labels, no axes. This helps users feel the momentum of improvement.


Step 4: Offer to Fix

After every report and radar chart, present a "What next?" widget using the ask_user_input tool:

  • question: "What would you like to do next?"
  • type: multi_select
  • options:
    • "Fix all Critical issues / 심각한 문제 모두 수정"
    • "Fix a specific issue / 특정 문제 수정"
    • "Wireframe to Spec / 와이어프레임 스펙 출력"
    • "Developer handoff report / 개발자 전달 보고서"
    • "Explain an issue / 문제 설명"
    • "Re-audit / 재감사"
    • "Export report / 보고서 내보내기"
    • "Export to Canva / Canva로 내보내기"
    • "Show session progress / 세션 진행 상황"

If "Fix all Critical issues" → loop through each 🔴 issue one by one:

  1. Show issue name + before/after diff (code) or design direction (screenshot)
  2. Ask using ask_user_input:
    • question: "Apply this fix? (Issue N of N)"
    • type: single_select
    • options: "Yes, apply it / 적용" / "Skip this one / 건너뛰기" / "Stop fixing / 중단"
  3. Apply or skip based on response, confirm each applied fix with ✅
  4. Move to the next 🔴 issue
  5. After all 🔴 issues are resolved or skipped: "All critical fixes done. Want to continue with 🟡 warnings?" Never batch-apply all fixes at once without per-issue confirmation.

If "Fix a specific issue" → present a widget listing all 🔴 and 🟡 issues by name, let the user pick one, then show the before/after diff and apply the fix.

If "Explain an issue" → present a widget listing all issues found. When one is selected:

  • Explain what the rule is and why it exists (1–2 sentences)
  • Show a real-world example of what it looks like when broken vs fixed
  • Explain the impact on users (e.g. "users with low vision won't be able to read this")
  • Cite the specific rule from the relevant reference file
  • If beginner mode is active: use plain language with no assumed knowledge
  • If experienced mode: go deeper — cite WCAG success criterion, link to relevant spec

If "Re-audit" → re-run the audit on the same input:

Scope for re-audit:
  - Use the exact same scope as the original audit (same categories, same WCAG level)
  - Do NOT re-ask settings unless the user explicitly requests a scope change
  - State at the top: "Re-audit — same scope as Audit [N]"
  - Show delta summary before the new report (see Re-audit delta section in Strict Output Template)
  - Only list issues that changed — resolved, newly found, or worsened
  - Unchanged issues from the previous audit: do not re-list, just note "N issues unchanged"

If the user shares updated code or a new Figma link before selecting Re-audit:
  → Use the new input automatically. Note: "Re-auditing updated version."

If "Show session progress" → render the session sparkline widget showing all audit scores in the current session, with issue count deltas per audit. Only show this option if 2+ audits have been run.

If "Developer handoff report" → produce a structured handoff document using this exact template:

# Developer Handoff — Design Audit
**Component/Page:** [name]
**Audit date:** [date]
**Overall score:** [X/100] · Accessibility: [X/100]

---

## Critical fixes required before shipping

| # | Issue | Location | Fix | Value |
|---|---|---|---|---|
| 1 | [issue name] | [node ID / line N] | [what to change] | [exact value] |

## CSS / Token spec

| Property | Current | Correct | Token |
|---|---|---|---|
| color | #999 | #666 | --color-text-secondary |
| padding | 13px | 12px | --spacing-3 |
| border-radius | 7px | 8px | --radius-md |

## Accessibility checklist
- [ ] [aria fix needed]
- [ ] [focus state needed]
- [ ] [label needed]

## Warnings (non-blocking)
- [warning 1]
- [warning 2]

## What's already correct
- [positive 1]
- [positive 2]

---
*Generated by Design Auditor Skill v1.2.6*

If "Export report" → create a downloadable

.md
file via file_create containing:

  • The full audit report in the Strict Output Template format
  • All widgets rendered as static markdown tables (scores, issue list, category breakdown)
  • The dev handoff section appended at the end
  • Filename:
    design-audit-[component-name]-[date].md

If "Export to Canva" → generate a visual audit report card in Canva using the Canva connector. This is a stakeholder-friendly format — not the raw markdown report, but a clean visual summary.

When to offer:
  → After any completed audit when Canva connector is available
  → Add "Export to Canva / Canva로 내보내기" as an option in the "What next?" widget

What to generate (use Canva generate-design tool, type: "doc"):
  Query: "Design audit report card with score [X/100], 
          accessibility score [X/100], ethics score [X/100], 
          [N] critical issues, [N] warnings, [N] tips. 
          Professional, clean layout with score rings and issue summary."

Content to include in the Canva doc:
  1. Header: "Design Audit — [component/page name]" + date
  2. Score ring or badge: Overall [X/100] · A11y [X/100] · Ethics [X/100]
  3. Issue summary table: 🚫 [N] Blockers · 🔴 [N] Critical · 🟡 [N] Warnings · 🟢 [N] Tips
  4. Top 3 critical issues with one-line fix each
  5. What's working well (2–3 positives)
  6. Footer: "Generated by Design Auditor Skill v1.2.6"

After generating:
  → Present the Canva design to the user
  → Note: "This is a shareable visual summary — for the full technical report, use Export Report."

If Canva connector is unavailable:
  → Skip this option silently in the widget. Do not mention it.

If "Wireframe to Spec" → produce a structured design specification document. This mode is different from the audit — it doesn't score, it annotates. Use all available context data (Figma layer data, screenshot, code structure) to produce a spec a developer could build from.

## 📐 Design Spec — [Component/Screen Name]

**Input type:** [Figma wireframe / Greyscale screenshot / Sketch]
**Spec generated:** [date]
**Fidelity:** Wireframe — values are recommended, not measured

---

### Layout & Dimensions
| Element | Width | Height | Notes |
|---|---|---|---|
| [Container] | [value or "full width"] | [value or "auto"] | [note] |
| [Section] | [value] | [value] | [note] |
*(Extract from Figma layer data where available. Estimate from screenshot where not.)*
*(Flag estimated values with ~ prefix: ~320px)*

---

### Spacing
| Location | Value | Notes |
|---|---|---|
| Page margins | 16px (mobile) / 48px (desktop) | Standard if not specified |
| Section gap | [value] | Between major sections |
| Component padding | [value] | Inside cards/containers |
*(Recommend 8pt grid values. If wireframe shows rough spacing, snap to nearest grid value.)*

---

### Typography
| Role | Font size | Weight | Line height | Notes |
|---|---|---|---|---|
| Heading | [value] | Bold | 1.2× | |
| Subheading | [value] | SemiBold | 1.35× | |
| Body | 16px | Regular | 1.5× | Minimum recommended |
| Caption | 13px | Regular | 1.4× | |
*(Fill with wireframe values where readable. Use standard scale where not specified.)*

---

### Components Required
List every UI component visible or implied in the wireframe:
| Component | Variant needed | States required | Notes |
|---|---|---|---|
| [Button] | [Primary/Secondary/Ghost] | Default, Hover, Loading, Disabled | |
| [Input] | [Text/Select/Checkbox] | Default, Focus, Error, Disabled | |
| [Card] | [Standard] | Default, Hover | |
*(Infer missing states — every interactive element needs at minimum: default, hover/focus, disabled)*

---

### Copy Placeholders
For every text placeholder in the wireframe, suggest final copy:
| Location | Placeholder shown | Suggested copy | Notes |
|---|---|---|---|
| [CTA button] | "Button" | "Get started" or "[action verb]" | Use specific verb |
| [Empty state] | "No data" | "No [items] yet — [action to create first]" | |
| [Error state] | "Error" | "Something went wrong — [specific next step]" | |
*(Follow microcopy.md rules: buttons are verbs, errors say what went wrong + how to fix)*

---

### Interaction Notes
Document implied interactions not shown in the wireframe:
- [ ] [Element]: on click → [expected behaviour]
- [ ] [Form]: validate on blur, not on submit
- [ ] [Modal trigger]: trap focus when open, restore on close
- [ ] [Scroll]: [sticky header / infinite scroll / paginated?]

---

### Accessibility Requirements
Minimum requirements for this component/screen:
- [ ] All interactive elements have visible focus states
- [ ] Form inputs have visible labels (not placeholder-only)
- [ ] Images have alt text slots
- [ ] Reading order: [describe logical DOM order]
- [ ] Keyboard navigation: [describe tab flow]
- [ ] [Any WCAG-specific requirements based on the component type]

---

### Open Questions
Things the wireframe doesn't answer that must be decided before development:
- [ ] [Question about content/data source]
- [ ] [Question about edge cases — empty state, max content, overflow]
- [ ] [Question about responsive behaviour at mobile breakpoint]
- [ ] [Question about loading state if this fetches data]

---
*Generated by Design Auditor Skill v1.2.6 · Wireframe to Spec mode*
*Values marked ~ are estimated. Confirm with designer before development.*

Wireframe to Spec mode behaviour:

  • Do NOT run a scored audit — no score, no severity labels, no issue list
  • Infer missing values using standard defaults from reference files (spacing.md, typography.md, states.md)
  • Flag every inferred/estimated value clearly with ~ prefix or "(recommended)" note
  • Always include Open Questions — gaps in the wireframe are valuable to surface
  • If Figma MCP is active: use
    get_design_context
    layer data for exact values, fall back to estimates where data is missing
  • If screenshot only: all layout values are estimated — state this clearly at the top
  • Output as a downloadable
    .md
    file:
    design-spec-[component-name]-[date].md

Then respond based on their selection. If they dismiss the widget, fall back to a language-appropriate line:

  • English: "Want me to apply any of these fixes? I can edit the code directly, or if you're in Figma, I can make changes there too. Or if you'd rather learn how to do it yourself, I can walk you through it step by step."
  • Korean: "이 중에서 수정을 도와드릴까요? 코드를 직접 수정하거나 Figma에서 변경할 수 있습니다. 직접 해보고 싶으시면 단계별로 안내해 드릴게요."

Ambiguous Input Widget

If the user triggers the skill but shares nothing (e.g. just says "audit this" with no attachment), use ask_user_input before asking in prose:

  • question: "What are you sharing for the audit?"
  • type: single_select
  • options: "Figma link / Figma 링크" / "Screenshot / 스크린샷" / "Code (HTML/CSS/React) / 코드" / "Written description / 텍스트 설명"

In Figma (🟢 High confidence, MCP active): Call

perform_editing_operations
→ specific node IDs → verify with
get_screenshot
after each change. See F5 and
references/figma-mcp.md
for operation types and safety rules. If
perform_editing_operations
is unavailable, fall back to design direction.

In code: Always show a before/after diff when fixing:

// BEFORE
padding: 13px 22px;
color: #aaa;

// AFTER
padding: 12px 24px;  /* 8pt grid */
color: #666;          /* 4.5:1 contrast on white */

In screenshots (🟡 Medium confidence): Never write code fixes for screenshot input — there is no source to edit. Instead give design direction:

  • Describe the change spatially: > "Increase the gap between the label and input field — it should feel like they breathe, roughly 1.5× the current distance."
  • Give the target value as a design spec, not code: > "Text color needs to be darker — aim for at least 4.5:1 against that background. A dark gray like #333 or #444 would work."
  • Reference visual landmarks: > "The card padding looks tight on the left side — match it to the top padding so all four sides feel equal."
  • If the user needs code, prompt them: > "If you can share the component code or Figma file, I can give you the exact value to change."

Teaching mode: Walk through the fix step by step instead of doing it for them.


Reference Files

  • references/typography.md
    — Font rules, sizing, line height, hierarchy, type scale algorithm
  • references/color.md
    — Contrast ratios, WCAG luminance formula, palette guidance
  • references/spacing.md
    — 8-point grid, layout, proximity rules
  • references/figma-mcp.md
    — Figma MCP workflow, page structure, component health, safe editing patterns
  • references/states.md
    — Loading, empty, error, success & disabled state patterns + code checks
  • references/microcopy.md
    — Button labels, error messages, placeholder rules, per-role audit guide
  • references/tokens.md
    — Design token naming, two-tier system, token health scoring, dark mode architecture
  • references/i18n.md
    — Internationalization, RTL layout, locale-aware formatting
  • references/corner-radius.md
    — Nested radius rule, radius scale, size-proportional rounding
  • references/elevation.md
    — Shadow scale, elevation hierarchy, dark mode depth, code shadow audit
  • references/iconography.md
    — Icon families, optical sizing, touch targets, meaning consistency
  • references/navigation.md
    — Tabs, breadcrumbs, back buttons, mobile nav, active states
  • references/animation.md
    — Easing curves, duration scales, reduced motion, Figma Smart Animate naming, code animation checks
  • references/ethics.md
    — Ethical design & dark patterns: full taxonomy (20 patterns across 5 groups), detection signals, ethics severity model, ethics score, ethical persuasion reference