visual-review
Verify UI/layout/styling changes are correct by computing occlusion, clipping, and alignment from the browser's resolved paint order via the chrome-devtools…
Skill by nubjs
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/nubjs-visual-review
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: design, agent-building, frontend-development
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
- Runs as
- Node.js package
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/visual-review
What's inside
- Skill name
visual-review- Description
- Verify UI/layout/styling changes are correct by computing occlusion, clipping, and alignment from the browser's resolved paint order via the chrome-devtools MCP `evaluate_script` tool — instead of eyeballing a flat screenshot. Invoke BEFORE declaring any UI, site, or styling/layout change correct. Screenshots have no depth buffer, so z-index/occlusion/clip bugs are exactly where "just look at it" fails; the `evaluate_script` routines below turn those fuzzy visual judgments into deterministic measurements — including optical center-of-mass, which measures the glyph ink's true visual center so differently-sized labels can be aligned by more than eye.
- License
- MIT
- Metadata
- internal: true
- Instructions
- 223 lines in SKILL.md · view on GitHub
Before installing
- Ships executable scripts
Files (2)
- SKILL.md
- optical-center.js
SKILL.md (first lines)
# Visual review — compute occlusion, don't perceive it
A flat PNG has no depth buffer and no stacking-context model, so layering and clipping bugs (z-index, `overflow: hidden`, fixed/sticky overlays) are exactly where eyeballing fails. The browser already resolved the paint order; query it with `evaluate_script` (chrome-devtools MCP).
**Run both passes.** Geometry catches occlusion the eye misses; the eye catches font-metric and color issues geometry misses.
## Optical ≠ mathematical
A layout can be geometrically consistent and still look wrong. Never declare a spacing/alignment/centering change correct from measurements alone — change it, screenshot it, look, and adjust by eye. Expect to nudge *against* the math when:
- **Rounded caps (pills, `rounded-full`) eat edge space** — content at `px-3.5` from a rounded end looks tighter than against a square edge, so a pill with symmetric padding and a leading icon looks lopsided; the text-adjacent cap needs *more* padding (keep symmetric `px` on the pill, add `pr-*`/`pl-*` per button on its text side).
- **Icon ink ≠ icon box** — a `w-4` icon whose glyph is 14px and visually light leaves dead space, inflating the perceived gap beyond the measured flex `gap`.
- **Optical ≠ geometric centering** — a glyph can be box-centered and ride visually high/low because the font's ink sits asymmetrically in the em.
Geometry decides occlusion/clipping; the eye decides balance/scale/centering.
---
## The `evaluate_script` routines
Replace `'SELECTOR'` with a real CSS selector before running.
### 1 — Occlusion (the non-negotiable check)
Reports what fraction of the element is actually visible, and names anything covering it.
```js
(selector => {
const el = document.querySelector(selector);
if (!el) return { error: 'not found' };
const r = el.getBoundingClientRect();
if (r.width === 0 || r.height === 0) return { error: 'zero-size box' };
const N = 5; // 5×5 = 25 sample points across the box
let visible = 0; const coveredBy = new Set();
for (let i = 0; i < N; i++) for (let j = 0; j < N; j++) {
const x = r.left + (i + 0.5) / N * r.width;
const y = r.top + (j + 0.5) / N * r.height;
const top = document.elementFromPoint(x, y); // topmost painted element here
if (top === el || el.contains(top)) visible++;
else if (top) coveredBy.add(top.tagName.toLowerCase() +
(top.id ? '#' + top.id : '') +
(top.className ? '.' + String(top.className).split(' ')[0] : ''));
}
return { coverage: visible / (N * N), coveredBy: [...coveredBy] };
})('SELECTOR')
```
`coverage === 1` → no occlusion. `coverage < 1` with a `coveredBy` entry that is not an ancestor/descendant → occlusion bug; the array names the covering element. No z-index reasoning required — `elementFromPoint` returns the browser's resolved paint order.
### 2 — Ancestor overflow / clip
```js
(selector => {
const el = document.querySelector(selector);
const r = el.getBoundingClientRect();
for (let p = el.parentElement; p; p = p.parentElement) {
const o = getComputedStyle(p).overflow;
if (o === 'visible') continue;
const pr = p.getBoundingClientRect();
if (r.left < pr.left || r.top < pr.top || r.right > pr.right || r.bottom > pr.bottom)
return { clippedBy: p.tagName + (p.id ? '#' + p.id : ''),
overflow: o, target: r, clip: pr };
}
return { clipped: false };
})('SELECTOR')
```
`clipped: false` is clean. Anything else → the element is cropped by that ancestor.
### 3 — Alignment and spacing
```js
([a, b] => {
const A = document.querySelector(a).getBoundingClientRect();
const B = document.querySelector(b).getBoundingClientRect();
return {
leftAligned: Math.abs(A.left - B.left), // px delta; ~0 = aligned
centerXdelta: Math.abs((A.left+A.right)/2 - (B.left+B.right)/2),
gap: B.top - A.bottom, // vertical spacing between them
};
})(['SEL_A', 'SEL_B'])
```
State verdicts in px: "left-edge delta 2px (clean)", "gap 28px vs expected 24px."
> `getBoundingClientRect` centers the **line box**, not the visible ink. Fine at the same font-size; for
> elements at **different** font-sizes that must look centered together, use §5.
### 4 — Viewport and off-screen
```js
(selector => {
const r = document.querySelector(selector).getBoundingClientRect();
return {
inViewport: r.top >= 0 && r.left >= 0 && r.bottom <= innerHeight && r.right <= innerWidth,
rect: r,
viewport: { w: innerWidth, h: innerHeight },
};
})('SELECTOR')
```
### 5 — Optical center of mass (different font-sizes)
Measures the alpha-weighted centroid of the actual glyph ink by rasterizing each label's computed font to a canvas. Implementation lives beside this skill in [`optical-center.js`](optical-center.js); inline it into one `evaluate_script` call.
```js
// after inlining optical-center.js in the same evaluate_script:
opticalCenter(['.wordmark', 'a[href="/docs"]', 'a[href="/blog"]'])
// → results:[{selector, comY, deltaFromAnchor}], cssHint:[{selector, nudge}]
// deltaFromAnchor ~0 = optically aligned; cssHint gives the ready-to-paste translate.
```
- **One call does measure + fix + verify.** `{ apply: true }` nudges each non-anchor toward the anchor, re-measures the post-nudge DOM, and iterates until converged, returning `{ before, after, appliedTranslateY }`. Don't hand-derive nudges — transcribe `appliedTranslateY` to CSS.
- **`{ overlay: true }` paints the analysis onto the page** (anchor solid-green, others dashed-red, px deltas labelled) so the next screenshot self-documents.
- **Anchor choice matters** — a *filled* pill/badge is optically centered by its BOX, not its caps ink; anchor to a bare-glyph sibling.
- **Auto-handled:** the baseline probe uses `vertical-align:baseline`, which flex/grid ignore, so the tool descends from a flex `<a>` to the inline element hosting the text.
- **Scope:** exact for a single line of plain text. Letter-spacing, `text-shadow`, `-webkit-text-stroke`, gradient text, and raster content aren't in the font render — screenshot the element's clip box and centroid the real pixels instead, or trust the eye.
**Keep it to ONE tool call.** To avoid re-inlining ~6KB, define it once via `navigate_page`'s `initScript` so `window.opticalCenter` exists in every later `evaluate_script`.From nubjs/nub, MIT.
More from nubjs/nub
- 0.848score
nub-charts
nubjs
Build a performance chart for nubjs.com — the SVG bar figures in blog posts, docs pages and social posts (a runtime augmentation against plain node, an…
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.844score
disk-reduction
nubjs
Reclaim disk on the maintainer's Mac when the volume is full or filling — ENOSPC, "no space left on device", a failed build or agent harness, or a routine…
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.844score
cpu-reduction
nubjs
Diagnose and clear CPU, memory, and disk contention on the maintainer's dev host.
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
plan-thread
nubjs
Use when an effort's APPROACH isn't settled yet and needs collaborative design BEFORE implementation — fleshing out how a feature/change should work, weighing…
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
probe-platforms
nubjs
Bring a test harness or probe up on a NEW operating system (Windows or Linux), or debug one that fails there.
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
pm-perf-tracing
nubjs
Performance-trace Nub package-manager installs using the existing phase timings, structured diagnostics, and sampling-profiler workflow.
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
type-declarations
nubjs
Add, audit, or update @nubjs/types declarations for Nub runtime APIs.
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
research-thread
nubjs
Use when the deliverable is a FACT — not code, not a design, not a gap catalog.
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
address-issue
nubjs
End-to-end playbook for working a GitHub issue (or bug-fix PR) on nubjs/nub: triage → acknowledge an external report with an "Investigating" comment →…
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
windows-vm-test
nubjs
Run ad-hoc Nub tests and debugging probes in the maintainer's existing local Windows 11 ARM64 QEMU/HVF VM when its external disk and runbook are present.
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
release
nubjs
Cut a Nub patch release end-to-end in one invocation.
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
ci-triage
nubjs
Find and diagnose RED CI with the gh CLI — answer "is anything failing?" and "why?" correctly, without being handed a URL.
SkillAFreshness: A★ 4.4KClaude Code +9
Compare and alternatives
Featured in
Signals
- Stars
- 4,417
- Forks
- 70
- Open issues
- 27
- Last commit (this folder)
- Aug 3, 2026 (2 months ago)
- Last release
- Oct 19, 2026 (7 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/nubjs/nub
- Website
- nubjs.com
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from nubjs
- 0.848score
nub-charts
nubjs
Build a performance chart for nubjs.com — the SVG bar figures in blog posts, docs pages and social posts (a runtime augmentation against plain node, an…
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.844score
disk-reduction
nubjs
Reclaim disk on the maintainer's Mac when the volume is full or filling — ENOSPC, "no space left on device", a failed build or agent harness, or a routine…
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.844score
cpu-reduction
nubjs
Diagnose and clear CPU, memory, and disk contention on the maintainer's dev host.
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
address-issue
nubjs
End-to-end playbook for working a GitHub issue (or bug-fix PR) on nubjs/nub: triage → acknowledge an external report with an "Investigating" comment →…
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
windows-vm-test
nubjs
Run ad-hoc Nub tests and debugging probes in the maintainer's existing local Windows 11 ARM64 QEMU/HVF VM when its external disk and runbook are present.
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
type-declarations
nubjs
Add, audit, or update @nubjs/types declarations for Nub runtime APIs.
SkillAFreshness: A★ 4.4KClaude Code +9
Cite this page
visual-review skill. Agent Mart. https://agentmart.dev/skills/nubjs-visual-review (last updated 2026-10-24)