-
Notifications
You must be signed in to change notification settings - Fork 0
Verify UI
Verify a feature's UI implementation matches the Stitch design at the token level. Produces a two-source token audit (HTML ↔ Code) overlaid with an X-component default-render trap checklist, plus an X-components compliance check.
Invocation:
/verify-ui myfeature-
Python 3 on
PATH(preflight runspython3 --version; used byextract_tokens.py). Missing → the skill stops with install instructions. - Feature implemented and build passing.
-
.claude/docs/_project/stitch-project.jsoncontains afeatures[{featurename}]entry.
If any prerequisite fails, the skill stops and tells you.
The audit always covers success. Loading / Failed / Empty are audited only when features[{featurename}].states.{state} == true — i.e. only the states selected during design. Skipped states are not downloaded, extracted, or audited. (For legacy entries without a states field, it's derived from observable state: loading/failed true, empty true only if an emptyScreenId exists.)
| Source | Purpose |
|---|---|
HTML (Stitch download, persisted in extracted/) |
The design ground truth at audit time |
Code (feature/{name}/src/commonMain/.../presentation/ui/) |
What's actually implemented |
X-Components Catalog (.claude/skills/_shared/X_COMPONENTS_CATALOG.md) |
Default-render behavior overlay (min sizes, hardcoded paddings, default colors) |
The audit is HTML ↔ Code, with the catalog as a third source for default-render checks. The implementation blueprint is not re-read as a token source — by the time verify-ui runs, the blueprint already drove the code. The single exception is the blueprint's Component Overrides table (Step 5.4), which captures per-feature overrides that would otherwise require a costly full sweep.
Preflight → Acquire HTML (reuse or download) → Token Extraction → Catalog →
Token Audit → Trap Checklist → Component Overrides Check →
Icons Manifest Audit → Images Manifest Audit → X-Components Check →
Present Results → Handle Mismatches → Cleanup
Reuse extracted/stitch_{state}.html when present. Only download when missing or empty. Stitch URLs are single-use, so downloads happen sequentially (no parallel) to avoid racing the URL semantics.
python3 .claude/skills/_shared/extract_tokens.py walks every DOM element and emits an inventory with deterministic Tailwind → dp/sp/color conversions plus tailwind-config overrides and global <style> rules. Reuse tokens_{state}.md when present.
For every visual element in the inventory, convert each Tailwind class to its dp/sp/color/modifier value, find the matching code, and emit one block per mismatch using the effective rendered value rule:
effective rendered value = declared parameter, then overridden/constrained by X-component internals
For example, XIconButton without an explicit colors = parameter renders a visible surface-colored circle — so the Code column shows "visible surface background", not "no color declared".
| # | Trap | Trigger |
|---|---|---|
| 1 |
XIconButton default containerColor = surface
|
call without explicit colors, HTML has no bg-*
|
| 2 |
XTextField defaultMinSize(280dp × 48dp)
|
HTML container narrower / shorter |
| 3 |
XTextField extra padding(top = 8.dp) when label != null
|
label present |
| 4 |
XTopAppBar always center-aligned title |
HTML title at ml-4 (left-aligned) |
| 5 |
XDialog always 90% width |
HTML mockup uses different fraction |
| 6 |
XPrimaryScrollableTabRow no divider by default |
HTML shows divider |
| 7 |
XRadioButton unselected color = primary
|
HTML shows outline-coloured ring |
Reads only the ### Component Overrides table inside the blueprint's ## Pre-Implementation Contract. For each row (Component | Property | HTML Value | X-component Default | Override Required), checks the implementation and emits CRITICAL blocks for any missing override. Silent pass when the override is present. Silently skipped if the blueprint or table is missing.
Runs only when icons.json exists and blueprintConsumed == true (design-aware feature). For each entry in the icons manifest it checks:
| Check | Severity |
|---|---|
XML file exists at entry.drawable_path
|
CRITICAL if missing |
Code references entry.res_reference (painterResource(...)) |
MINOR if absent |
Chrome-scope icons use DesignSystemResources.drawable.* (not feature-local Res.drawable.*) |
CRITICAL on wrong scope |
No deprecated import androidx.compose.material.icons.* (use XIcon(painter = …)) |
CRITICAL |
Orphan XMLs not in any manifest and not referenced by any .kt
|
MINOR |
Same structure as the icons audit, for raster assets in images.json: file presence (CRITICAL), code reference (MINOR), wrong scope (CRITICAL), and one extra check — Stitch CDN URL used with AsyncImage (model = "https://lh3.googleusercontent.com/aida-public/…") is CRITICAL: bundled design assets must be Image(painter = painterResource(...)). Runtime-data AsyncImage(model = uiModel.field) is not flagged. Orphan images → MINOR.
Forbidden imports flagged as CRITICAL:
import androidx.compose.material3.* # any M3 component
import coil3.compose.AsyncImage # use AsyncImage from :core:designsystem
MaterialTheme.colorScheme and MaterialTheme.typography are allowed — XTheme wraps MaterialTheme. Only component imports are forbidden.
| Severity | Criteria |
|---|---|
| Critical | Spacing ≥ 4dp off, wrong color role, missing component, wrong font size/weight, wrong corner radius, wrong icon size, wrong border, any trap caught (5.3), any missing override (5.4), any manifest CRITICAL (missing asset, wrong scope, deprecated material-icons import, Stitch-CDN AsyncImage) |
| Minor | Spacing 1–3dp off, shadow omitted, letter-spacing off, decorative detail, design-system-authoritative trap (centre-aligned title, 90% dialog width), manifest MINORs (declared-but-unreferenced asset, orphan XML/image) |
| Data-only | Different mock text/values — ignored |
| File | Content |
|---|---|
.claude/docs/{featurename}/designs/{featurename}_audit.md |
Mismatch blocks only, no OK rows |
stitch-project.json → features[{featurename}].verification
|
{ verified, verifiedAt, auditReport, extractedSources, xComponentsCompliant, criticalIssues, attempts } |
Critical issues: the skill tells you to run:
/modifying-kmp-feature {featurename} fix all UI audit issues based on @.claude/docs/{featurename}/designs/{featurename}_audit.md
verify-ui does not invoke /modifying-kmp-feature itself — you control the pipeline.
Only minor mismatches: offered as "Accept" (recommended) or "Fix all".
The blueprint was previously a fourth source. It got dropped for three reasons:
- Duplicated the HTML — every token in the blueprint is derivable from the HTML.
-
Defined verdicts that never fired —
BLUEPRINT MISMATCHalways produced the same action asCODE MISMATCH. - Expensive — ~5–7K tokens per run for no extra signal.
The Component Overrides table (5.4) is the one exception — a pre-curated catalog sweep that costs ~N rows of work (typically 0–5) instead of hundreds. See .claude/skills/verify-ui/RATIONALE.md for the full reasoning.
- UI-Designer — where the HTML and blueprint come from
- Design-Pipeline — full pipeline overview
- Using-Design-System — the X-components rules verify-ui enforces
Back to Skills