| name | design-craft-rubric |
| description | JUDGE whether an already-rendered UI surface is good enough to ship โ quality evaluation of something that EXISTS, not creation of something new. GLM-5.2 has no vision, so judgment runs on Playwright-MCP computed-style probes (getComputedStyle JSON: fontSize / color / padding / gap / contrast), not on eyeballing pixels. Scores 7 dimensions PASS/FAIL (visual hierarchy / spacing-rhythm / alignment / contrast+WCAG / density / design-token conformance / DESIGN.md-tone) + runs a 13-tell "AI-slop" gate (warm-cream hero, near-black+neon, Inter-only, white+purple, uniform sizing, vague headlines, scattered motionโฆ). ROUTE BY INTENT, NOT KEYWORDS โ load this whenever the intent is to evaluate quality of a built surface, including indirect signals: a shared screenshot the user reacts to with dissatisfaction, hesitation, or silence; "์ด๊ฑฐ ์ด์ํ์ง ์์?", "๊ด์ฐฎ์?", "์ด๋"; a ship/no-ship decision moment ("์ถ์ํด๋ ๋ผ?", "์ด๋๋ก ๊ฐ๊น?"); any qualitative complaint about a rendered screen ("AI์ค๋ฌ์", "์ฌ๋ํด", "ํธ๋ ๋ํ์ง ์์", "์ดํํ๊ณ ์ถ์ด์ ธ"); explicit review/audit/craft-check/WCAG requests. Returns per-dimension PASS/FAIL + prioritized fix-list + PROCEED/REVISE verdict, never a rolled-up score. NOT for designing something new (that is ux-design-baseline) and NOT for a repeated improvement loop (that is ui-iterate). |
Design craft rubric โ GLM-5.2 edition (blind-model, probe-driven)
The judging knowledge for any rendered UI surface. GLM-5.2 has no vision (config-confirmed: modalities.input: ["text"] only) โ so this rubric does NOT rely on the model "looking at" a screenshot. It judges through Playwright-MCP computed-style probes that return text the model can reason over. A screenshot is captured only as an audit artifact for a human (or vision-capable) reviewer, never as GLM-5.2's own verification input.
The core constraint and how this rubric compensates
| GLM-5.2 reality | Consequence | Compensation in this rubric |
|---|
| Text-only input | Cannot self-interpret a screenshot | browser_evaluate returns computed styles as JSON โ judge those |
| 1M-token context | Can hold full token system + CSS + probe transcript at once | No need to split across sub-agents; load everything in main context |
| Strong coding + tool use | Excels at probe scripts and rule application | The rubric is rule-based (PASS/FAIL with concrete bars), not vibes |
The blind loop: render โ DOM probe (text) โ computed-style probe (text) โ rubric (rules). The model never needs to "see" โ it reads structured probe output and applies concrete pass-bars.
Fail-fast: environment โ craft defect (apply BEFORE the rubric)
The trigger is the server being UNREACHABLE at the TRANSPORT layer โ dev server won't start, or a base-URL probe gives connection-refused / timeout (no HTTP response). An HTTP 4xx/5xx means the server is UP โ that is a render-blocker to flag per rubric step 3 (or ํ์ธ ๋ถ๊ฐ), NOT environment.
If transport-unreachable: STOP, report ํ๊ฒฝ ๋ฌธ์ โ ์๋ฒ ๋ฏธ๊ธฐ๋ (transport-unreachable), ํ๊ฒฝ ์ ๊ฒฐ in one line and end. Do NOT retry / re-screenshot / long static-fallback. Cold next dev / vite compile is not down โ browser_wait_for an anchor first; only a refused transport after that is environment.
Probe procedure (the blind model's "eyes" โ run BEFORE judging)
Every judgment must be backed by a probe result, never inferred from reading the source CSS. The order matters: text-readable structure first, then computed values.
- Identify the surface. From
git diff --name-only (or the user's request), identify the changed directory and its dev URL + route. Each repo maps its own dir โ port; check the repo's package.json / dev script. If a repo has multiple frontends, identify which one.
- Start the dev server if not running;
browser_wait_for a stable anchor (cold compiles on demand โ do not capture mid-compile).
browser_navigate the route. browser_resize to 1440ร900 (desktop) AND 390ร812 (mobile) โ both, every time. Single-viewport judgment misses responsive failures.
browser_snapshot โ the accessibility tree. This is the text representation of layout/roles/labels and is the primary structural input for a text-only model (more useful than a screenshot). Verify: every interactive element has a role + accessible name; heading order is hierarchical; landmarks present; no vague labels.
browser_evaluate โ the computed-style probe. Run a function that returns a JSON report (template below). This is the blind model's substitute for "looking."
browser_console_messages (level: warning) โ catch font-load 404s, CSS parse errors, specificity overrides. A 4xx/5xx on a real call is a render-blocker.
- Load the owning DESIGN.md for the token scale + tone before scoring. If the repo has no DESIGN.md โ honest-degrade per dim 6 (N/0 + heuristic flags), do NOT block.
The computed-style probe template (browser_evaluate)
Adapt selectors to the surface. This returns text the model can judge directly โ no pixel interpretation needed.
() => {
const probe = (sel) => {
const el = document.querySelector(sel);
if (!el) return null;
const cs = getComputedStyle(el);
const rect = el.getBoundingClientRect();
return {
tag: el.tagName.toLowerCase(),
text: (el.textContent || '').slice(0, 60),
fontSize: cs.fontSize, fontWeight: cs.fontWeight,
fontFamily: cs.fontFamily.split(',')[0].trim(),
color: cs.color, backgroundColor: cs.backgroundColor,
padding: cs.padding, margin: cs.margin,
borderRadius: cs.borderRadius, gap: cs.gap,
rect: { w: Math.round(rect.width), h: Math.round(rect.height) },
};
};
const sels = ['h1', 'h2', 'button', '[class*="primary"]', '[class*="card"]'];
const contrast = (fg, bg) => { };
return {
viewport: { w: innerWidth, h: innerHeight },
elements: sels.map(s => ({ selector: s, ...probe(s) })),
fontLoaded: { display: document.fonts.check('16px "YourDisplayFont"'),
body: document.fonts.check('16px "YourBodyFont"') },
reducedMotion: matchMedia('(prefers-reduced-motion: reduce)').matches,
transitionCount: document.querySelectorAll('[class*="fade"],[class*="anim"]').length,
};
}
The probe converts "is this good?" into "do these values satisfy the rubric?" โ a question a text model can answer reliably.
The 7-dimension rubric (each = PASS or FAIL with a concrete bar; NO aggregate score)
Every dimension is PASS or FAIL with a concrete pass-bar; on FAIL emit the specific fix (surface/element + what is wrong + the target value). NO vibes, NO single aggregate score, NO "8/10." A rolled-up number is not actionable; per-dimension P/F + a fix list is.
| # | Dimension | PASS-bar (concrete, probe-checkable) | On FAIL โ the fix |
|---|
| 1 | Visual hierarchy | The primary action is the single most prominent element above the fold (largest/highest-weight/strongest color). Headingโbody type contrast โฅ 1.25ร size step OR a clear weight step. Probe: fontWeight of primary CTA > siblings; fontSize step โฅ 1.25ร. | Name the element that wrongly out-competes the primary action; give the target (e.g. "demote ๋ณด์กฐ button to variant=ghost; primary stays solid --color-primary"). |
| 2 | Spacing / rhythm | Gaps come from ONE spacing scale (e.g. 4/8/12/16/24/32). Vertical rhythm between sibling blocks is consistent (same gap token repeated). Probe: enumerate gap/margin values โ all snap to the scale. | List the off-scale gaps with rendered px (probe.margin/probe.gap) + the nearest scale token to snap to (e.g. "13px โ 12px (space-3)"). |
| 3 | Alignment / grid | Elements share edges / a grid; labels and fields, card rows, action clusters align to a common left/right edge. Probe: getBoundingClientRect().x of aligned elements equal within 1px. | Name the misaligned elements + the shared edge they should snap to (target px / grid column). |
| 4 | Contrast + WCAG AA | No low-contrast text. Body text โฅ 4.5:1, large text โฅ 3:1. Probe: parse computed color/backgroundColor RGB โ compute ratio. (If the repo runs an axe CI gate, defer the formal verdict there; do NOT re-run axe and self-report.) | Flag the low-contrast pair with rendered fg/bg hex + measured ratio (e.g. "3.2:1, AA 4.5:1 ๋ฏธ๋ฌ โ darken fg to --text-strong"). |
| 5 | Density vs whitespace | Content breathes โ no cramped wall of equal-weight controls. Related controls grouped with whitespace. Probe: count siblings in one cluster; flag clusters of >6 equal-weight controls with no grouping. | Count the competing controls; recommend grouping/collapsing + the whitespace token (e.g. "group 8 filters into a Toolbar w/ space-6 between groups"). |
| 6 | Design-token conformance | Rendered values match the owning repo's OWN token scale โ color/space/radius/type from that repo's DESIGN.md + token CSS. Each repo owns its tokens; there is NO canonical cross-repo scale and you NEVER judge surface A against repo B's tokens. A finding = a rendered value NOT on the scale: ad-hoc text-[Npx], raw hex (#1a1a1a), arbitrary gap-[13px]. If no named scale โ honest-degrade: "token conformance = ํ์ธ ๋ถ๊ฐ (N/0)" + HEURISTIC flags (grep / probe for raw hex + text-[Npx] + *-[โฆpx]). Do NOT block on a missing scale. | List each ad-hoc value (selector + rendered) + the named token it should map to within that repo. |
| 7 | DESIGN.md tone adherence | The rendered surface matches the owning repo's DESIGN.md tone. Color temperature, radius, density, voice match โ never cross-applied across repos. If the repo has no DESIGN.md โ "tone = ํ์ธ ๋ถ๊ฐ (DESIGN.md ๋ถ์ฌ)"; do NOT invent a tone. | Quote the DESIGN.md line violated + the rendered mismatch (e.g. "DESIGN.md L## says warm cream #FDFBF7; rendered bg is cold #FFFFFF"). |
Concrete values only โ "๋๋น๋น 3.2:1 (AA 4.5:1 ๋ฏธ๋ฌ)" not "๋๋น ๋ฎ์ ๋ณด์". Hedge BANNED. Layout reachability (scroll / viewport / button reach) is a CORE requirement, never deferred as "์ํ."
The 13 AI-slop tells (apply as a hard-fail gate AFTER the rubric)
GLM-5.2 โ like every LLM โ defaults to the most probable token. That statistical pull reasserts on every generation unless explicitly overridden. These 13 tells are the concrete shape of "generic AI design." Run them as a checklist: present/absent + evidence (probe value). Any "present" โ REVISE.
| # | Tell | Why it reads as AI | Concrete fix |
|---|
| 1 | Warm cream hero (~#F4F1EA) + high-contrast serif + terracotta accent | Default cluster #1 (Anthropic-named) | Derive palette from the brief's actual subject/materials โ 4โ6 named hex tied to content |
| 2 | Near-black bg + single acid-green / vermilion accent | "Dark + neon" shortcut | โฅ2 non-neutral color roles; neon reserved for one signal |
| 3 | Broadsheet pastiche: hairline rules, border-radius:0, dense newspaper columns | "I want to look serious" costume | Use only when content is genuinely editorial; otherwise vary radius intentionally |
| 4 | "Big number + small label + supporting stats + gradient accent" hero | THE template hero answer | Open with the subject's most characteristic artifact (live demo, real screenshot, interactive moment) |
| 5 | Inter (or system sans) as the only typeface | "The Inter Default" โ ships everywhere | โฅ2 type roles: characterful display (with restraint) + complementary body (+ optional mono for data) |
| 6 | White + purple / purple-to-blue gradient | Most common LLM color default | Semantic color via CSS custom properties (--color-action-primary), never --color-gradient-start |
| 7 | Uniform component sizing: same radius/padding/height everywhere | Flat, no hierarchy | Vary radius/padding/scale intentionally to encode hierarchy |
| 8 | 01 / 02 / 03 numbered markers | Decorative numbering encoding nothing | Use only for an actual ordered sequence/process/timeline; otherwise cut |
| 9 | Scattered fade-in / hover-that-does-nothing / button snap | "Extra animation = AI feeling" | One orchestrated motion moment > many. Motion must do exactly one of: state change / direct attention / reinforce brand |
| 10 | Vague aspirational headline ("Build the future of work") | Averaged from training data | Be specific (Stripe โ "Financial infrastructure to expand access to the global economy") |
| 11 | Hedging / superlative copy ("may help", "best-in-class") | Statistically safe, commitment-free | Active voice; control label = outcome ("Save" not "Submit"); error explains + how to fix, no apology |
| 12 | Stock/AI imagery: diverse-group-at-laptop, 3D blobs, plastic illustrations | Placeholder tells | Real screenshots, real photos, or custom illustrations tied to brand |
| 13 | Over-designing (parallax + custom cursor + animated bg + layered gradients) | Noise mistaken for distinctiveness | Restraint: one signature element, everything around it quiet (Chanel rule: remove one accessory before shipping) |
Self-uniqueness test (mandatory pre-build gate, not just post-build): re-prompt yourself generically โ "design a [category] page" โ and if you arrive at the same design, that part is a default. Revise it and state what you changed and why. Only write code after the plan passes this test.
Output contract (per-dimension PASS/FAIL + slop gate + prioritized fix-list + verdict)
- BLUF header โ
๊ฒฐ๋ก (3์ค): ๋ ๋ craft P/F ์์ฝ / ์ ์ ์ค์ (DESIGN.md ํค ์ผ์น / WCAG) / ํ๊ฒฐ (PROCEED | REVISE) + ๋ค์ ์ก์
.
- Sections: โ DESIGN.md/FLOWMAP ์ค์ (์ ์ , file:line; DESIGN.md ๋ถ์ฌ์ "ํ์ธ ๋ถ๊ฐ") โก ์ ๊ทผ์ฑ (WCAG, guideline# + ์ธก์ ๊ฐ) โข ๋ ์ด์์ ๋๋ฌ์ฑ โฃ ๋ ๋ craft ํ (7ํ ์ ์ โ skip BANNED) โค AI-slop ๊ฒ์ดํธ (13 tells present/absent + evidence) โฅ ์ฐ์ ์์ FIX ๋ฆฌ์คํธ (
surface/element ยท ๋ฌด์์ด ์๋ชป ยท target value, ๋์ "๋ณด๋ฅ / ํ ๋์์ธ ์ ์ง" option; ๋ง์ฐํ a/b ๋ฉ๋ด BANNED) โฆ ํ๊ฒฐ.
FAIL 0๊ฑด + slop 0๊ฑด = PROCEED. FAIL โฅ1 OR slop โฅ1 = REVISE + ์ FIX ๋ฆฌ์คํธ. ๋จ์ผ ์ข
ํฉ ์ ์ (7.5/10 ๋ฅ) BANNED โ ์ ์๋ actionable ํ์ง ์๋ค.
Workflow for a text-only model to self-verify visual craft (the loop)
Step 0 โ Pre-build gate (no browser yet). Produce token system (4-6 color + โฅ2 type roles + 1 signature element) as a structured artifact. Run the self-uniqueness test. Only then write code, deriving every value from the token system.
Step 1 โ Render. browser_navigate โ browser_resize mobile (390ร812) AND desktop (1440ร900).
Step 2 โ Structural probe. browser_snapshot (a11y tree) + browser_console_messages (warnings).
Step 3 โ Computed-style probe. browser_evaluate (template above). Assert hex values match the token system; flag any purple/cream/Inter defaults; check font actually loaded; compute contrast; count transitions.
Step 4 โ Rubric scoring. Fill the 7-dim table from probe values (not vibes). Any FAIL โ revise โ re-run Steps 1-3.
Step 5 โ Slop gate. Run the 13-tell checklist from probe values. Hard-fail tells: vague headline, Inter-only, purple gradient, uniform sizing, decorative numbering, scattered motion. Any present โ revise.
Step 6 โ Restraint pass. Enumerate every decorative element (gradient, divider, eyebrow, motion) and force-remove at least one (Chanel rule). Re-probe.
Step 7 โ Screenshot (audit artifact only). browser_take_screenshot (fullPage, mobile + desktop) โ save. GLM-5.2 cannot interpret it; it exists for a human or vision-model reviewer and the audit trail. Do not treat a screenshot as GLM-5.2's own verification.
Loop termination: Steps 1-5 pass clean on both viewports + zero slop tells + one justified signature element present โ ship.
Honesty limits
- This rubric is rule-application over probe output โ a strong signal, not ground truth. Keep real backstops: human merge, axe CI for formal WCAG (defer there โ never self-report axe), runtime verification for behavior, visual-regression CI for change.
- A probe can lie (CSS specificity cancellation, late hydration, fonts loading after probe). Mitigate:
browser_wait_for anchor before probe; document.fonts.ready await; cross-check browser_console_messages for overrides.
- The model cannot catch what the probe does not return. If a craft dimension is not in the probe output, do NOT fabricate a judgment โ mark it "ํ์ธ ๋ถ๊ฐ (probe ๋ฏธ์ํ)".