| name | markup-assist |
| description | General-purpose deterministic verification for any HTML/CSS work — no API key, no reference design required, no project setup. Route by task; run the matching vlmkit gate (integrity / copy / layout / breakpoints / scroll / handlers / interactions / verify markup); read the kickback; fix; re-run to green. Use whenever you wrote or edited markup and want to know if it is actually correct — broken-page defects, copy fidelity, responsive boundaries, keyboard operability, design-target match — instead of eyeballing a screenshot. Works standalone in any repo via `npx vlmkit`; this is the drop-in generalist skill, distinct from the full-workflow skills (auto-markup / mock-markup / dynamic-markup). |
| metadata | {"internal":true} |
markup-assist
Deterministic gates for markup work. Each gate renders in headless
Chromium, measures (DOM + pixel math, no VLM), and prints a
kickback: a next-fix list with selector attribution. Your job is
the loop: run → read kickback → fix the reported thing → re-run.
Prereqs: Node 24+, Playwright Chromium. If vlmkit is not installed:
npm i -D @mizchi/vlmkit, then use npx vlmkit …. Sources are file
paths or URLs. Every gate supports --json (one envelope shape:
{gate, command, verdict, counts, findings, report}), exits 1 on a
suspect, takes --advisory to print and exit 0, and takes
--rule <gateId>/<ruleId>=off|warn|suspect to re-tune one rule
(--rules lists them). --fail-on-suspect is accepted and does
nothing — failing on a suspect is the default.
Route by task
Wrote/edited a page, no reference design:
| Question | Gate |
|---|
| Is anything broken? (JS errors, empty render, failed resources, text collision/clipping/protrusion, collapsed containers, overflow, invisible text, occlusion, misalignment, unstyled page — 3 viewports) | vlmkit check integrity page.html |
| Is the required copy present, exactly? | vlmkit check copy page.html --manifest copy.txt |
| Does the structure match the brief? (widths, per-row counts, order, per-viewport visibility) | vlmkit check layout page.html --contract layout.json |
| Tokens / theme / a11y / long-text survival | check tokens · check theme · check a11y contrast|touch|focus · stress i18n |
| Is the page consistent with itself? (no tokens file needed — component styles reused, spacing on its own scale) | vlmkit check design page.html |
Behavior:
| Question | Gate |
|---|
| Responsive boundaries exact, no overflow at any width | vlmkit check breakpoints page.html --sweep |
| Scroll containers / page overflow-x inventory | vlmkit scan scroll page.html |
| Sticky/fixed/snap actually behave | vlmkit check scroll page.html |
| Animations visibly run, settle, respect reduced-motion | vlmkit check animation page.html |
| Everything keyboard-operable, ARIA states transition | vlmkit check interactions page.html |
Clickable <div>s / pointer-only controls | vlmkit scan handlers page.html |
Drag and drop wired but unfireable (a dragstart source that is not draggable, a drop target with no dragover) | vlmkit scan handlers page.html |
Drag and drop that looks wired but is not (dragover without preventDefault, dragstart transferring nothing) | vlmkit scan handlers page.html --probe-drag |
| A scripted flow reaches its post-conditions | vlmkit verify flow page.html --flow flow.json |
Against a target design:
| Question | Gate |
|---|
| Am I done? (one verdict + full kickback) | vlmkit verify markup attempt.html --target target.png |
| What components are missing/extra/misordered? | vlmkit build page target.png attempt.html |
| Converge one component crop | vlmkit build component crop.png attempt.html |
| Normalize a @2x/@3x mock first | vlmkit scan mock export@2x.png |
| Copy vs target pixels (no manifest) | vlmkit check copy attempt.html --target target.png → read the contact sheets with your own vision; a different reader than whoever transcribed |
Maintain: vlmkit heal selector page.html ".broken" (selector
died in a refactor) · vlmkit diff html a.html b.html (visual
equivalence of two pages).
Done condition
Fix loops need a FIXED gate set decided up front. Default for a
reference-free page build:
check integrity → CLEAN
check copy --manifest → 0 missing, 0 placeholders
scan scroll → no page-overflow-x
scan handlers → no pointer-only suspects
check interactions → no suspects
(+ check breakpoints --sweep when responsiveness is specified)
Re-run only the failing gate while iterating; re-run the whole set
once before declaring done. Warns are acceptable; suspects are not.
Rules the gates enforce (do not fight them)
- Never hide copy to pass
check copy. Matching is against
visibly rendered text; font-size:0 / opacity:0 / transparent /
off-screen / clipped / camouflaged / sr-only matches report as
copy-invisible with a reason class. If the requester wants hidden
copy accepted (e.g. sr-only), THEY pass
--allow-invisible <class> — you don't.
- Never ship disclosures open just for the copy gate. Closed
<details> / unselected tabs / aria-expanded=false content is
swept automatically and passes with provenance.
- Don't delete required content or controls to silence a finding
— fix what the kickback names.
- Intentional patterns (sr-only, image replacement, hero overlays,
ellipsis) don't false integrity — they appear under
exempted.
- A residual is real unless the tool itself demotes it
(near-miss / pixel-confirmed / exempted annotations).
Reading kickbacks
Kickback lines carry: the defect class, the selector, the measured
value, and often the direction (move it instead, size-delta caveat). Trust the measurement over your recollection of the code;
when a kickback surprises you, re-render and look — do not argue with
the gate from memory. Iteration counts are recorded in
.vlmkit/run-ledger.jsonl; report progress from there.
Escalation
If the tool itself fails to run (unknown subcommand, missing
browser, install error), STOP and report the tool failure verbatim —
do NOT silently substitute hand-rolled screenshot scripts and then
claim the work was "verified". A hand-rolled check covers a fraction
of what the gates measure (the black-box validation run that added
this rule missed 2 of 5 seeded defects that way), and a report that
hides the fallback is worse than a report that says "the tool didn't
run". An "Unknown check subcommand" error usually means the installed
release predates these gates — say so and ask for an update.
If a gate keeps failing after 3 honest fix attempts on the same
finding, stop and report: the finding, your attempts, and your best
hypothesis — a stuck loop is information, not something to hide with
a workaround. For full-workflow builds (a mock image to reproduce, a
motion brief, page composition from scratch) use the dedicated
skills: mock-markup, dynamic-markup, auto-markup.