Skip to content
UI Craft/ docs

Design-quality score

UICraftScore — a deterministic 0-100 composite grade from anti-slop, token discipline, and a11y. Plus UsabilityScore, the judged companion.

Updated 2026-06-25

UICraftScore turns three static-analysis signals into one defensible 0-100 grade. It’s objective, reproducible evidence of design quality — not vibes.

score = 100
      − (antiSlop_critical × 8) − (antiSlop_major × 4) − (antiSlop_warn × 1)
      − (token_findings × 2)
      − (a11y_critical × 8) − (a11y_major × 4)

clamped [0,100]  ·  A ≥ 90  ·  B ≥ 80  ·  C ≥ 70  ·  D ≥ 60  ·  F < 60

Three dimensions, each with its own subscore:

Dimension Source Penalty
anti_slop 33 rules from ui-craft-detect critical −8 · major −4 · warn −1
token_discipline raw hex / off-scale radius / spacing / z-index −2 per finding (flat)
a11y 5 static checks (no overlap with the detector): img-no-alt, non-semantic-interactive, positive-tabindex, aria-invalid-no-describedby, no-reduced-motion critical −8 · major −4

It’s deterministic by design — identical input always yields the identical score, which is exactly what makes it safe to gate CI on. Formula, weights, and bands are published in evals/quality/score.mjs; hand-authored fixtures + baselines.json form the regression gate.

CLI

# score a single file
node scripts/eval.mjs src/components/Hero.tsx

# score a directory
node scripts/eval.mjs src/components/

# run the full regression gate (slop vs. designer separation)
node scripts/eval.mjs --baseline

# JSON output (CI-friendly)
node scripts/eval.mjs src/components/Hero.tsx --json

# fail below a threshold (default 70)
node scripts/eval.mjs src/components/Hero.tsx --threshold 80

Exit codes: 0 clean / in-band · 1 below threshold or out of band · 2 arg error.

Via MCP

The score_ui tool exposes the same scorer to any MCP client:

{ "code": "<your tsx source>" }   // or: { "path": "src/components/Hero.tsx" }

Returns { overall: { score, grade }, dimensions: { anti_slop, token_discipline, a11y }, version } — the same envelope as the CLI --json.

UsabilityScore

UICraftScore is deterministic, which also bounds it: static analysis can’t see experience friction — a confusing flow, a missing undo, a 2-second save with no feedback. UsabilityScore covers that axis. It rolls the /heuristic scorecard (Nielsen’s 10 + 6 design laws) into one 0-100 number:

heuristic_base = round( ((mean(nielsen_scores) − 1) / 4) × 100 )   # 10 scores, each 1–5
UsabilityScore = clamp( heuristic_base − 5 × (failed design laws) , 0 , 100 )

Same bands as UICraftScore: A ≥ 90 · B ≥ 80 · C ≥ 70 · D ≥ 60 · F < 60

It is judged, not deterministic — computed by the host agent from the rubric (zero deps, no API key, every harness), so it may vary run to run. Gate CI on UICraftScore; use UsabilityScore for review depth.

Extended report

When you want the full picture, the two render side by side — each labeled for reproducibility:

Score Value Reproducible?
UICraftScore 82 / B ✅ deterministic — identical every run
UsabilityScore 33 / F ❌ judged — LLM judgment, may vary

They are deliberately never averaged: one is reproducible and one is judged, and collapsing them would hide that distinction — the exact property the deterministic score exists to protect. A high UICraftScore with a low UsabilityScore means the code is clean but the experience has friction the static rules can’t see.

Source


Spotted something out of date? Open an issue on GitHub →