HaTchi-MaXchi — the Dazzle House Aesthetic¶
Canonical, agent-readable definition of Dazzle's visual taste — the HaTchi-MaXchi style (pronounced "hachi machi"; the capitals spell its HTMX substrate). Consumed by: framework CSS/token authors (now), the authoring agent and the improve loop (follow-ons).
Ship floor vs advisory taste (2026-07-11, epic #1580 Phase D):
| Path | Role | Blocks CI / ship? |
|---|---|---|
Dual-locks + pytest -m gate + package suite |
Structural / DOM regression floor | Yes |
Subscription visual smoke (scripts/hm_visual_smoke.py) + host Read of PNGs |
Cognitive review without metered APIs | No (ship_gate: false in manifest) |
| Pixel-diff of fleet captures (subscription) | Deterministic visual regression when used | Only when the change author opts in as a gate |
dazzle qa taste-panel / component-vision |
Blind LLM aesthetic parity (metered) | No unless a human sets a threshold and API credits are intentional |
CI stays deterministic. Vision scores never block green solely because a model scored low.
Subscription vision scores (no metered API)¶
Two playbooks — both bill cognition to the host harness subscription
(Claude Code Task / Grok Build Read / similar). Neither calls
anthropic.Anthropic().messages.create.
| Scope | Capture | Judge | Strategy / CLI |
|---|---|---|---|
| Example-app fleet | dazzle qa capture |
Subagent Reads PNGs → findings JSON | .claude/commands/improve/strategies/visual_tier2_subagent.md |
| HM dual-lock exemplars | scripts/hm_visual_smoke.py |
Subagent Reads PNG → taste dimension scores | scripts/hm_subscription_vision.py |
| HM GitHub Pages gallery (curated) | scripts/hm_pages_vision.py --capture |
Subagent Reads PNGs → findings + scores | scripts/hm_pages_vision.py |
| All Hyperparts (~90) coherence | hm_pages_vision.py --capture --all-hyperparts |
Subagent Reads PNGs → coherent? + score + issues | improve/strategies/hyperpart_coherence.md |
| Coherence drain queue | (uses last sweep) | Rank + backlog seed coherence_drain <stem> |
scripts/hm_coherence_queue.py + HMC rows |
/improve hm-convergence pick order (floors green): drain if
hm_coherence_queue.py --status shows queue>0 or PENDING coherence_drain *
rows; else investigate if sweep missing/stale; else dual-lock / probes.
Why images for Hyperpart coherence. Dumping each gallery page as HTML/text
and asking a model “is this coherent?” burns discrete tokens on markup the eye
never sees. A viewport PNG is a compact continuous representation of the same
surface — capture is free local Playwright; judgment bills to the host
subscription Read path. Prefer this over metered component-vision for fleet
sweeps. Full taste dimensions remain available when you want rubric parity;
coherence is the cheaper “looks intact?” gate.
# Dual-lock smoke → subscription scores
python scripts/hm_visual_smoke.py --dazzle-emit
python scripts/hm_subscription_vision.py --from-smoke --write-prompt
# → .dazzle/hm-subscription-vision-prompt.txt
# Dispatch host subagent (or Read PNGs in-session); Write scores JSON; then:
python scripts/hm_subscription_vision.py --ingest .dazzle/hm-visual-scores-raw.json
# → .dazzle/hm-visual-scores.json (ship_gate: false, billing: subscription-host-read)
Avoid for day-to-day agent loops: dazzle qa taste-panel / component-vision
(those still use the metered vision client in taste_panel.score_image).
Also enforced by: the opt-in composition analyze taste focus (focus=["taste"]),
and the per-rule gates listed below. The blind taste panel remains available for
parity campaigns when credits exist — not the default ship gate.
- Spec:
docs/superpowers/specs/2026-07-02-taste-house-aesthetic-design.md - Baseline:
dev_docs/taste/baseline-2026-07-02.md(all six dimensions FAIL; fleet 2.4–3.2 vs references 5.1–5.8 on a 10-point scale) - Rubric source of truth:
src/dazzle/core/taste_rubric.py(this document is drift-gated against it bytests/unit/test_taste_doc_drift.py)
Parity target, not mimicry. HaTchi-MaXchi competes with the
shadcn/Tailwind/Vercel dialect on perceived quality, never on mechanism:
semantic root classes + data-dz-* modifiers + design tokens, no
utility-class proliferation, no client-state framework. The judged rubric is
deliberately dialect-neutral (Goodhart guard).
Principles¶
-
Semantic surface, expressive result. One root class per component +
data-dz-*modifiers; tokens carry the aesthetic. Agent-hostile class soup is a rule violation, not a style choice. -
Type does the hierarchy. A disciplined scale and weight system carries visual importance — headings, labels, values and captions each have a recognisable role. Boxes and chrome never substitute for type.
-
One accent; neutrals do the work. Color appears when it means something. Neutral ramps carry structure; the accent marks the primary action or focus; semantic tones (success/warning/danger) are reserved for meaning.
-
Depth is information. Elevation encodes layering and interactivity — never decoration. Shadows are stacked, low-alpha, and consistent per elevation level.
-
Motion confirms, never entertains. 100–200ms, derived from the request lifecycle, honoring
prefers-reduced-motioneverywhere. -
Dark is a material, not an inversion. A designed dark neutral ramp; elevation reads through lightness; accents and semantic tones are recalibrated for the dark context.
-
Density with rhythm. Data-dense, but every gap sits on the spacing scale; related items cluster, unrelated items separate.
-
Every state is designed. Hover, focus, active, disabled, loading, empty, error — no browser defaults showing through, no accidental empty regions.
-
The structure is the style. The HaTchi-MaXchi signature: HTMX4 anatomy is expressed, not hidden. Swap targets read as coherent surfaces,
hx-indicatorand the htmx request-lifecycle classes drive the loading/motion language (skeleton shimmer, settle transitions), boosted navigation feels like part of the material. The design language and the transport share one skeleton — a differentiator no client-state dialect can copy honestly.
Rules¶
Each rule states its motivating baseline evidence and its enforcement. Rules marked advisory become gated as Phases 2–3 land the machinery.
-
TASTE-1 — Every interactive element gets a designed focus ring: 2px accent at ~40% alpha, 2px offset, on
:focus-visible. (Principle 8; evidence: state_completeness gap 2.46 — judges saw browser-default focus/affordances. Enforcement: token + component CSS in Phase 2; panel dimensionstate_completeness.) -
TASTE-2 — Shadows are stacked low-alpha pairs bound to elevation tokens; never a single hard drop-shadow, never ad-hoc values. (Principle 4; evidence: perceived_craft gap 3.24, the widest and most judge-agreed gap. Enforcement: shadow tokens in Phase 2; panel
perceived_craft.) -
TASTE-3 — One accent hue per app, applied to the primary action and active states only; all other structure comes from the neutral ramp; semantic tones only where they carry meaning. (Principle 3; evidence: color_discipline gap 2.59 — accent exists but semantics muddy. Enforcement: token sheet v2 ramps; panel
color_discipline.) -
TASTE-4 — Every gap, padding and margin sits on the spacing scale; no off-scale one-off values in component CSS. (Principle 7; evidence: spatial_rhythm gap 2.75. Enforcement: advisory until the Phase 3 component pass; panel
spatial_rhythm.) -
TASTE-5 — Text roles are typographically distinct: page title, section heading, label, value, caption each differ in at least two of size/weight/color-step. Tabular numerals in data tables. (Principle 2; evidence: typographic_hierarchy gap 2.44 — "near-uniform text sizes". Enforcement: type-scale tokens + Geist in Phase 2; panel
typographic_hierarchy.) -
TASTE-6 — Icons come only from the vendored Lucide registry via the
Iconfragment primitive — inline SVG, sized on the type scale, never icon fonts, emoji-as-UI, or ad-hoc SVGs. (Principle 1/8; evidence: perceived_craft — "no icon language" was a recurring judge note. Enforcement: icon registry drift gate, Phase 2.) -
TASTE-7 — Dark mode uses the designed dark ramp: surfaces lighten with elevation, borders replace shadows where shadows die, accents and semantic tones are dark-recalibrated. Never a naive inversion. (Principle 6; evidence: dark_mode_integrity 3.13 — "inverted-looking". Enforcement: dark ramp tokens + WCAG pair test, Phase 2; panel
dark_mode_integrity.) -
TASTE-8 — Empty states are designed: an icon, one sentence, and the primary action — never a bare "No X yet" line in an otherwise blank card, and never an unexplained blank region. (Principle 8; evidence: worst-scoring screens were empty/denied workspaces, down to 1.27. Enforcement: advisory until Phase 3; panel
state_completeness.) -
TASTE-9 — Loading and settling states derive from the htmx request lifecycle (
hx-indicator, request/settle classes → skeleton shimmer, 100–200ms settle transitions), withprefers-reduced-motionhonored. (Principle 9/5; evidence: not judgeable from stills — enforced by the interaction walks, not the panel. Advisory until Phase 2 motion tokens.) -
TASTE-10 — Corner radii come from the radius scale and are consistent per component family (inputs and buttons share a radius; cards share a larger one). (Principle 1/4; evidence: perceived_craft — "inconsistent radii/borders". Enforcement: token sheet v2; panel
perceived_craft.) -
TASTE-11 — Text contrast meets WCAG AA against its actual surface token in both themes; the token sheet is the unit under test. (Principle 6/3. Enforcement: contrast unit test over token pairs, Phase 2 — closes a named gap: no machine contrast gate exists today.)
-
TASTE-12 — No utility-class accretion: components keep one semantic root class +
data-dz-*modifiers; new visual variation goes through tokens or a modifier, never a class pile. (Principle 1. Enforcement: existing card-safety/contract scanners keep the floor; advisory beyond that.)
Rubric¶
The judged dimensions live in src/dazzle/core/taste_rubric.py (single
source of truth; this table is drift-gated).
| Key | Title | Applies to |
|---|---|---|
typographic_hierarchy |
Typographic hierarchy | both |
spatial_rhythm |
Spatial rhythm | both |
color_discipline |
Color discipline | both |
state_completeness |
State completeness | both |
dark_mode_integrity |
Dark-mode integrity | dark |
perceived_craft |
Perceived craft | both |
Composition note: composition analyze focus=["taste"] runs the
theme-agnostic five; dark_mode_integrity must be requested by name
against known-dark captures (composition captures carry no theme info).
The parity campaign (advisory unless human-thresholded)¶
Metered blind-panel runs measure fleet parity vs references. They feed improve
backlog and Phase 2–4 taste campaigns; they are not a default CI ship
gate (see table above). Prefer hm_visual_smoke.py + host Read for day-to-day
agent review.
# capture references (third-party pixels stay gitignored)
python scripts/taste/capture_references.py
# capture the fleet, per app (above-fold + dark are REQUIRED for parity)
dazzle qa capture --app <app> --url <URL> --above-fold --manifest fleet.json
dazzle qa capture --app <app> --url <URL> --above-fold --dark --manifest fleet-dark.json
# run the blind panel (exit 0 = parity on every dimension) — requires API credits
dazzle qa taste-panel --manifest fleet-merged.json --judges 3 --noise-runs 2
The panel top-crops every image to the reference frame
(normalize_pool_frames), interleaves sources blindly, and applies
per-dimension margins locked by the 2026-07-02 baseline:
margin = max(0.5, 2 × judge noise SD) —
| Dimension | Locked margin | Baseline gap |
|---|---|---|
| perceived_craft | 0.50 | 3.24 |
| spatial_rhythm | 0.82 | 2.75 |
| color_discipline | 1.63 | 2.59 |
| state_completeness | 0.58 | 2.46 |
| typographic_hierarchy | 1.15 | 2.44 |
| dark_mode_integrity | 1.15 | 1.97 |
Phases 2–4 (foundations → component pass → example convergence) end when
the fleet clears every margin. Protocol invariants for re-judging: same
frame, same rubric, unseeded apps, persona-matched workspace sampling
(fixing the _platform_admin over-selection noted in the baseline).