visual-polish
This skill diagnoses and fixes visual defects in built or redesigned websites by inspecting rendered DOM against intent, creating a complete fix plan, then…
Identity proven by GitHub's verified organisation badge (Automattic)
- Agents
- Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode
- License
- LGPL-2.1 (copyleft)
- Pin
- 870948cpinned Oct 9, 2026
- Context cost
- about 3,200 tokenswhen the skill loads
- GitHub stars
- 533on automattic/studio
- Scripts
- None found
What it does
“Verify and polish a built or redesigned site by diagnosing rendered-DOM defects against intent, fixing them in one planned batch, and re-checking what changed.”
Generated summary from the skill's own text
This skill diagnoses and fixes visual defects in built or redesigned websites by inspecting rendered DOM against intent, creating a complete fix plan, then applying all fixes in one batch and re-checking. It identifies issues like broken layout, overflow, spacing misalignments, and styling problems in headers, sections, buttons, and images across all site pages.
When to use it
- After building or redesigning a WordPress site to polish rendered output
- When generated pages have spacing, alignment, or styling issues not matching intent
- To verify all pages including plugin-provided pages render as designed
- When buttons appear doubled, text touches edges, or sections have wrong width or centering
When not to
- To add new features, motion, scripts, or redesign sections
- When issues are unrelated to rendered DOM defects from markup or styling
- To style individual blocks in isolation without checking the full page
Install
These commands install from the publisher's repository and need no account.
skills CLI
npx skills add automattic/studio --skill visual-polishInstalls from the repository's default branch, which may have changed since the commit we checked.
GitHub CLI 2.90 or later
gh skill install automattic/studio visual-polish@870948cd5cbdba4d05a432c38fa5e8e1e796b3b1Installs the commit we checked (870948c).
Copy by hand
Download the folder at 870948c and put it in .claude/skills/visual-polish for Claude Code and in .agents/skills/visual-polish for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode.
Claude Code plugin
/plugin install build-with-wordpress@claude-plugins-officialThe publisher's plugin in Claude's official marketplace. It may hold more than this skill, and we have not pinned it.
Copy for your agent
A prompt for any coding agent. It installs this skill from GitHub at the pinned commit, copies the files as they are, and runs nothing unless you ask.
Prompt
Install the agent skill "visual-polish" by Automattic. Source, pinned to commit 870948cd5cbdba4d05a432c38fa5e8e1e796b3b1: https://github.com/automattic/studio/tree/870948cd5cbdba4d05a432c38fa5e8e1e796b3b1/apps/cli/ai/skills/visual-polish 1. Download that folder at exactly this commit. Do not use the default branch. 2. Copy every file unchanged, keeping the folder structure, into .claude/skills/visual-polish/ for Claude Code and .agents/skills/visual-polish/ for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode. Create the folders if they do not exist. 3. Do not edit the files, and do not run any script in the folder unless I ask you to. 4. When you are done, list the files you copied and where you put them. Listing and checks: https://www.agentnative.dev/skills/automattic/visual-polish
SKILL.md at the pin
---
name: visual-polish
description: Verify and polish a built or redesigned site by diagnosing rendered-DOM defects against intent, fixing them in one planned batch, and re-checking what changed.
user-invokable: true
---
# Visual Polish
Use this skill to verify a built or redesigned site and fix the design issues that make generated sites feel unpolished. The generated block markup, the editor serialization fixes from `validate_blocks`, and WordPress's own injected layout classes mean the rendered page often differs from what you intended. This skill closes that gap.
The core method is **diagnose from evidence, not from memory**. Do not guess why something looks wrong from the screenshot alone — the rendered DOM usually differs from the markup you wrote. Read the real DOM with `inspect_design`, find the actual cause, then fix it.
## Scope: which pages to polish, and what to fix
Polish **every page of the site**, not just the home page. This includes all user-created pages (Home, About, Contact, and similar) and any plugin-provided pages. A page the user never sees polished feels unfinished, and plugin pages ship with generic default styling that rarely matches the theme.
For a WooCommerce shop, polish each of these pages: Shop, single-product, Cart, Checkout, and My Account, checking the space around `main` on each.
Polish fixes **defects**, where the rendered page departs from what was intended (`DESIGN.md`, the layout map, the `block-content` rules): broken layout, overflow, unreadable text, misaligned or doubled spacing, broken images or hover states. Fix each with the smallest change that removes it. Polish does not add motion, scripts, or sections, and does not redesign a section: those belong to the build.
## Method: one diagnosis, one batch of fixes, one re-check
The most important rule: **do not fix issues one at a time as you find them.** Fixing reactively makes you miss related issues, introduce regressions, and burn expensive screenshot passes. Take each page through these phases once.
### Phase 1 — Diagnose (read-only — make NO edits in this phase)
1. Enumerate every section and component of the page (from the page markup you wrote, or by inspecting the top-level containers).
2. If you can view images, take one `take_screenshot` with `viewport: "all"` (desktop + mobile). A model that cannot view images skips this step: `inspect_design` is then the whole diagnosis, so the sweep below must be complete rather than guided by what looks wrong.
3. Go **section by section**. For each, call `inspect_design` on the relevant selectors and compare the rendered DOM and computed styles against your intent and the theme's `style.css` (read it). The screenshot is the symptom; `inspect_design` is the cause — never diagnose from the screenshot alone, because subtle issues like doubled button padding barely show in pixels. Inspect even sections that look roughly right, and always inspect:
- the header and footer template parts — for `padding-left`/`padding-right` on the bar; a part whose root group is not `layout: constrained` (and has no constrained inner group) gets no gutter, and its text touches the viewport edge,
- every section wrapper — for width and centering,
- every button — BOTH `.wp-block-button` and `.wp-block-button__link`, with `includeHover: true`.
- every block that paints its own box (background, border, shadow — cards, panels, tinted sections) and every non-constrained full-width section — for `padding-left`/`padding-right`, which must not be `0px` when the box holds text.
- every inset box — a block that is rounded, bordered, or narrower than its container and paints its own background — for the edges it meets: compare its `boundingBox` with the neighbouring header, footer, or section, because an inset box must not sit directly against a hard edge such as the footer's border. Full-bleed bands are meant to sit flush; leave them alone.
- every image — that it renders, fills its slot, and keeps overlaid text legible.
4. List the **complete** set of issues before fixing anything — a concise checklist, one short line per issue: the section, the root cause from the DOM, and the exact fix (file, selector, change). A list, not prose.
Do not make a single edit until you have diagnosed every section and listed every issue. **A complete diagnosis is the gate into Phase 2.**
### Phase 2 — Fix the whole batch
Work through the plan with targeted `Edit` calls: one file per turn, with all of that file's fixes as separate entries of one `Edit` call, per the system prompt cadence — never batch files into one turn. Do **not** screenshot or inspect between edits. If an edit changes block markup (not just CSS), re-run `validate_blocks` on that file and re-check its diff, since the serializer can change classes again.
### Phase 3 — Re-check what changed
After the whole batch, take one `viewport: "all"` screenshot — or, when you cannot view images, re-run `inspect_design` on the selectors you changed, and only those. Check each plan item off and look for regressions the fixes introduced; this is not a new diagnosis.
On the home page only, if an item is still broken or the batch broke something, make one follow-up batch for exactly those items and do not check it again. The page is then done: no further edits, no further captures, and never a return to Phase 1.
The home page's last capture is the theme screenshot, even when a follow-up batch came after it. When you cannot view images, that is the only capture you take: one desktop capture of the home page after its last edit.
## Recurring issues and what to inspect
The issues below are common examples, **not an exhaustive list**. Treat them as a starting checklist, not the full scope of what to look for — fix every visual problem the screenshot or the inspection reveals, including ones not listed here, and apply the same method (inspect the rendered DOM, find the real cause, then fix). For each, the cause lives in the DOM — inspect, don't guess.
### Section width or centering is off
Symptom: a section meant to be full-width renders in the narrow content column; a section is wider/narrower than intended; or a section's content sits off-center instead of centered.
Inspect the section wrapper. Check `boundingBox` (`x` and `width`) against `viewportWidth`, and read the `ancestors` chain plus the `margin-left`/`margin-right` computed values. WordPress constrains children of constrained-layout containers via `.is-layout-constrained > *:not(.alignfull):not(.alignwide)`, which custom CSS like `width: 100%` cannot override — and a constrained layout is also what centers inner content (via auto inline margins). For a full-width section with centered content, the outer group needs `{"align":"full","layout":{"type":"constrained"}}`. When width or centering is wrong, the cause is the `align`/`layout` on the block markup (or custom margins/widths fighting the layout) — fix it in the markup, not by forcing width or margins in CSS.
That markup fix only works if the theme declares a content width. If a constrained section still spans the full container after you set it, check the theme's `theme.json` for `settings.layout.contentSize` — with no `contentSize` and no `wideSize`, WordPress emits no `max-width` at all for constrained layouts and the block markup cannot fix it. Declare the widths in `theme.json`, not per-section `max-width` in CSS.
### Button styling is doubled or on the wrong element
Symptom: a button looks too big (doubled padding), its background/border/radius is wrong, or it has two conflicting hover effects.
The button block is **two nested elements**: the `.wp-block-button` wrapper and, inside it, the `.wp-block-button__link` — the actual `<a>`/`<button>`, also carrying `.wp-element-button`. WordPress core applies the button's padding, background, border, and radius to the inner `.wp-block-button__link` / `.wp-element-button`, NOT the wrapper. Your CSS must target that same inner element so it **overrides** the default instead of stacking a second padded box on the wrapper.
The common trap: a custom `className` on a button block lands on the **wrapper** (`.wp-block-button.your-class`), not the link. So `.your-class { padding … }` styles the wrapper, on top of WP's default padding on the inner link — doubled box. Descend to the inner element instead: `.your-class .wp-element-button`.
For **global** button styling (consistent buttons site-wide), target `.wp-element-button` in `style.css`. WordPress puts that class on the inner styled element of every button — the button block and buttons from other blocks (search, file, etc.) — so one rule covers them all and overrides cleanly.
Inspect BOTH selectors with `includeHover: true` and compare their computed styles:
- If padding/background/border is set on BOTH the wrapper and the link, the button renders with **doubled padding and looks too big** — remove that styling from the wrapper (or its custom class) and put it on `.wp-element-button` / `.wp-block-button__link`.
- There must be exactly ONE hover rule, on the inner element (`.wp-element-button:hover` or `.wp-block-button__link:hover`), never the `.wp-block-button` wrapper. If both have hover styles you get **two conflicting hover effects** — delete the wrapper hover. Use the `hover` block in the inspect output to confirm only the inner element changes.
### Text touches a background, border, or the viewport edge
Symptom: copy sits flush against the edge of a card, a tinted panel, a bordered box, or the window — most visible on mobile.
Inspect the box and read `padding-left`/`padding-right`. WordPress pads only full-width constrained groups (the root gutter via `.has-global-padding`) and zeroes it on constrained groups nested inside them; a group, column, or cover with its own background or border gets no padding, and neither does a full-width section with a `default`, `flex`, or `grid` layout. Fix it by giving the block's class horizontal padding in `style.css` (reuse `var(--wp--style--root--padding-left)` for a section gutter) or by moving the text into a constrained inner group — never with margins on the text blocks.
The header is the usual victim: rewriting `parts/header.html` from scratch tends to drop the scaffold's outer `{"layout":{"type":"constrained"}}` group, leaving a flow or flex group that gets no gutter, so the site title sits at `x: 0`. Restore the constrained wrapper in the part's markup rather than padding the bar in CSS.
### An inset box sits on a hard edge
Symptom: a rounded or bordered card is the first or last section and its edge lands directly on the footer's top border or the header's bottom edge, with no breathing room.
Inspect the box and its neighbour and compare their `boundingBox` values: equal edges with no gap is the fault. Scaffolded themes zero the gap between the template's top-level blocks and let sections own their rhythm, which is right for full-bleed bands but leaves an inset box touching whatever follows it. Give that section (not the text inside it) bottom or top spacing, or close the page on a full-bleed band instead. Do not add spacing to full-bleed sections — they are meant to sit flush.
### Spacing between blocks differs from intent
Symptom: gaps between paragraphs, headings, or sections are larger or smaller than the CSS suggests, or one of two side-by-side columns starts lower than the other.
Vertical rhythm is owned by WordPress layout CSS, not your margins: in a default or constrained group, `:root :where(.is-layout-flow) > *` (and `.is-layout-constrained`) gives every child but the first a `margin-block-start` equal to the block gap — a fixed length, 24px unless `theme.json` sets `styles.spacing.blockGap`, so setting `--wp--style--block-gap` changes nothing. Inspect the adjacent blocks and read their `margin-top`/`margin-bottom` computed values. If the gap is fighting your margins, set spacing through `theme.json` `styles.spacing.blockGap` or the block's own spacing, or override knowing that exact selector. A default group laid out side by side in `style.css` keeps these margins, so every column but the first starts lower; give the group the grid layout in its markup (`"layout":{"type":"grid","columnCount":2}` for two columns), which drops them.
### Backgrounds inside grids/columns are wrong
Symptom: a column or grid cell background doesn't appear, doesn't fill the cell, or sits on the wrong element.
Inspect `.wp-block-column` (or the grid cell) and any inner `core/group` you put the background on. Check which node's `background-color`/`background-image` is set and whether its `boundingBox` actually fills the cell. A background on an inner group only covers its content height; to fill the cell, the color belongs on the column/cell node. Columns are flex items — confirm `align-items`/stretch behavior matches intent.
## Notes
- Fix in markup or `style.css` per the `block-content` skill rules — no inline styles, no custom stylesheets, no custom classes on inner DOM elements.
- The site must be running for `take_screenshot` and `inspect_design`.
From automattic/studio at 870948c, shown as plain text under its license: LGPL-2.1 (copyleft). The license text is in apps/cli/LICENSE.md.
View SKILL.md on GitHub at 870948cFiles
- SKILL.md13 KB · 6f0f14b9153f
1 file, 13 KB in total. Each file has a sha256 in the pin's manifest; the first 12 characters are shown.
Skill folder on GitHub at 870948cMore from Automattic
annotate
This skill opens a browser with annotation tools that let users click page elements and leave feedback, then the agent reads those annotations and applies the…
Claude Code, Codex, Cursor +3 · about 1K tokens · pinned 6 days ago
block-content
This skill instructs agents to write editable WordPress block markup for block themes, enforcing use of core blocks over HTML, managing layout cascades, and…
Claude Code, Codex, Cursor +3 · about 4.4K tokens · pinned 6 days ago
hosting-plans-helper
This skill fetches current WordPress.com plan names, pricing, and feature details from live APIs, then answers questions about which plan tier includes…
Claude Code, Codex, Cursor +3 · about 840 tokens · pinned 6 days ago
imagery
This skill guides generating AI images for WordPress sites using the generate_images tool, or alternative image generation tools.
Claude Code, Codex, Cursor +3 · about 2.6K tokens · pinned 6 days ago
liberate
This skill helps agents migrate websites into WordPress by copying the site from a URL or local files using the Studio CLI, then comparing the imported…
Claude Code, Codex, Cursor +4 · about 830 tokens · pinned 6 days ago
need-for-speed
Measures Core Web Vitals and page composition metrics for WordPress sites using synthetic testing, then suggests targeted optimization actions based on…
Claude Code, Codex, Cursor +3 · about 650 tokens · pinned 6 days ago
Related in Frontend and design
next-bundle-optimizer
Audits Next.js applications to identify and reduce browser bundle size and initial-load work by analyzing routes, dependencies and client outputs.
by VercelOfficial publisher
Claude Code, Codex, Cursor +4 · about 2.3K tokens · pinned 6 days ago
next-cache-components-optimizer
Optimize Next.js routes to display meaningful UI instantly on initial load or client navigation by encoding the optimization goal as a failing…
by VercelOfficial publisher
Claude Code, Codex, Cursor +4 · about 5.6K tokens · pinned 6 days ago
next-partial-prefetching-adoption
Enables Partial Prefetching in Next.js 16.3+ apps and guides adoption through auditing Link prefetch behavior, preserving UI in tests or manually, and…
by VercelOfficial publisher
Claude Code, Codex, Cursor +4 · about 6.7K tokens · pinned 6 days ago
remotion-interactivity
This skill teaches how to structure Remotion code so the Studio IDE recognizes and makes it interactive.
by RemotionOfficial publisher
Claude Code, Codex, Cursor +3 · about 2.4K tokens · pinned 6 days ago
remotion-interactivity
This skill teaches how to structure Remotion video components to unlock Studio interactivity features like click selection, drag-and-drop, resizing, rotation…
by RemotionOfficial publisher
Claude Code, Codex, Cursor +3 · about 4.8K tokens · pinned 6 days ago
remotion-maps
This skill enables creating map-based animations with Remotion using four different mapping libraries: Mapbox, MapLibre, MapTiler, or CesiumJS.
by RemotionOfficial publisher
Contains scripts
Claude Code, Codex, Cursor +3 · about 240 tokens · pinned 6 days ago