# Visual Polish

> Use after features are built and before shipping, when the UI needs a quality pass, when the product needs to look premium instead of functional, or when preparing for a demo or investor meeting.

- Skill: `aneja5/visual-polish` (Agent Skill)
- Install (CLI): `npx skillmds@latest add aneja5/visual-polish`
- Raw SKILL.md: https://api.skillmd.com/api/skills/aneja5/visual-polish/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: aneja5 (https://skillmd.com/u/aneja5)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/aneja5/visual-polish

---


# Visual Polish

## Overview

Run the systematic quality pass that turns "it works" into "this feels professional." Output is `.forge/polish-checklist.md` — pass/fail per item, with `file:line` for every failure. Reads `design-system`, `component-library`, `page-composition` (all optional, but each makes the pass sharper). Sits between `/build` and `/ship` in the pipeline.

## When to Use

- Features are built, tests pass, and the UI is about to be shown to anyone outside the team
- A demo or investor meeting is scheduled within the week
- The product "works" but the team feels something is off and can't name it
- A redesign was promised and the team needs to confirm the redesign actually landed
- Before promoting a beta to general availability

## When NOT to Use

- Features aren't built yet — polish is the last 10%, not the first 90%
- A backend-only service
- Polish was done this sprint and nothing visual changed since

## Common Rationalizations

| Thought | Reality |
|---------|---------|
| "It works, that's enough" | Working is table stakes. Users judge quality in the first 3 seconds. Polish is the difference between "this works" and "this feels professional." |
| "We'll polish before launch" | Launch is always tomorrow. If polish isn't in the process, it never happens. |
| "Users don't notice these details" | Users can't articulate what they notice. They feel "janky" or "solid." Polish is what they're feeling. |
| "It's just a demo" | Demos are the highest-stakes UI moment. Every missing hover state says "prototype." |
| "We'll fix it after the demo" | After the demo means after the impression. The first impression is the only impression. |

## Red Flags

- Browser default focus rings (`outline: auto`)
- Empty state that says only "No data" with no context or CTA
- Error message that says "Something went wrong" with no next step
- Layout shift on content load (CLS > 0.1)
- Missing favicon — the tab is generic
- Page title that says `localhost:3000` or `Vite App`
- Horizontal scroll appearing at 320px or 1440px

## Core Process

### Step 1: Read available UI artifacts

Load whichever of these exist: `.forge/design-system.md`, `.forge/component-library.md`, `.forge/page-composition.md`, `.forge/brand-identity.md`, `.forge/interaction-patterns.md`, `references/motion-system.md`. Use them as the rubric — polish is "the runtime matches the system."

### Step 2: Micro-interaction audit

For every interactive element:
- Every button has `hover` + `active` + `focus-visible` states
- Every toggle/switch transitions smoothly (not instant swap; per `references/motion-system.md`)
- Every link has a hover state (underline or color shift)
- Focus rings are visible and consistent (not browser defaults; not `outline: none` without replacement)
- Form inputs have `focus` + `error` + `disabled` styling

### Step 3: Empty state audit

For every list, table, chart, dashboard panel, sidebar group:
- An empty state exists with helpful headline + description + CTA
- No blank areas; no "No data" without context
- Empty-state voice matches `brand-identity` voice rules

### Step 4: Error state audit

- Every form has inline validation with **specific** messages (not just a red border)
- Every network call has error handling with retry where applicable
- A 404 page exists and matches the product's visual language
- Toast notifications for async errors are styled (not `alert()`)
- Error messages name *what happened* and *what to do next*

### Step 5: Loading audit

- Every async content area has a skeleton screen (not a spinner; per `interaction-patterns`)
- Skeletons match the shape of real content (same dimensions, same approximate density)
- Zero layout shift when content loads (skeleton → content occupies identical box)
- Spinners only inside buttons (`spinner-in-button` + disabled state)

### Step 6: Responsive edge case audit

Test at the extremes:
- **320px** — smallest common phone; no horizontal scroll, no text overflow without ellipsis
- **1440px+** — large desktop; content respects the max-width cap from `page-composition`
- Touch targets ≥ 44pt on mobile
- Long content (multi-word labels, 100-character strings, pasted URLs) tested in every container

### Step 7: Meta audit

- Favicon exists and matches `brand-identity`
- Apple touch icon for iOS home-screen save
- OG meta tags: `og:title`, `og:description`, `og:image` — image is 1200×630 and matches brand
- `<title>` is descriptive per route (not the same string everywhere)
- `<meta name="theme-color">` set for mobile browser chrome

### Step 8: Dark mode audit (if applicable)

- Every screen rendered in dark mode
- All text/background pairings pass WCAG AA (4.5:1 for body, 3:1 for large text)
- Images and illustrations have dark-mode variants or transparent backgrounds
- No hardcoded `#FFFFFF` leaking through (grep for raw hex in component files)
- Inline SVGs use `currentColor` where appropriate

### Step 9: Write `.forge/polish-checklist.md`

For every audit, list each item as pass/fail. Every failure includes:
- The audit it belongs to
- A `file:line` (or route + selector) pointer
- The smallest specific fix

Prepend a `forge:meta` header (`generated_by: visual-polish`, `generated_at: <ISO 8601 UTC with Z>`, `depends_on: [<each UI artifact that existed at run time>]` — paths only, never hashes, `generated_from: {<each upstream path>: <its content_hash AT generation time>}`, `content_hash: <sha256 first 8 of THIS file's body>`).

After writing, report a summary: total items checked, total failed, the three highest-impact failures.

## Verification

- [ ] Every interactive element has `default / hover / active / focus-visible / disabled` states (loading + error where relevant)
- [ ] Every list/table/chart has an empty state with CTA
- [ ] Every async area has a skeleton, not a spinner on the content
- [ ] No layout shift on content load (CLS ≈ 0)
- [ ] Favicon, Apple touch icon, OG tags, and route-specific `<title>` all present
- [ ] No horizontal scroll at 320px or 1440px+
- [ ] Touch targets ≥ 44pt on mobile
- [ ] Dark mode parity (if shipped) — all pairings pass WCAG AA
- [ ] `.forge/polish-checklist.md` written with `file:line` pointers for every failure
- [ ] Summary reported with the three highest-impact failures called out

