Skip to content

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.

Who reads the image. The acting host agent inspects the PNGs itself, in session, using its own image-reading path. Dispatching a reviewer subagent or workflow is an optional extra pass for an independent second opinion, not a required step — shell, Dazzle CLI, image inspection, and an agent's own judgement are enough. Any host that cannot read images in session should dispatch a reviewer rather than silently downgrading to a text-only review.

Scope Capture Judge Strategy / CLI
Example-app fleet dazzle qa capture Host agent inspects PNGs → findings JSON (optional reviewer dispatch) dazzle qa capture
HM dual-lock exemplars scripts/hm_visual_smoke.py Host agent inspects PNG → taste dimension scores (optional reviewer dispatch) scripts/hm_subscription_vision.py
HM GitHub Pages gallery (curated) scripts/hm_pages_vision.py --capture Host agent inspects PNGs → findings + scores (optional reviewer dispatch) scripts/hm_pages_vision.py
All Hyperparts (~90) coherence hm_pages_vision.py --capture --all-hyperparts Host agent inspects PNGs → coherent? + score + issues scripts/hm_pages_vision.py
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 per-rule gates listed below. The blind taste panel remains available for parity campaigns when credits exist — not the default ship gate.

  • Spec: archive/docs-superpowers/specs/2026-07-02-taste-house-aesthetic-design.md
  • Baseline: archive/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 by tests/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

  1. 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.

  2. 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.

  3. 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.

  4. Depth is information. Elevation encodes layering and interactivity — never decoration. Shadows are stacked, low-alpha, and consistent per elevation level.

  5. Motion confirms, never entertains. 100–200ms, derived from the request lifecycle, honoring prefers-reduced-motion everywhere.

  6. 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.

  7. Density with rhythm. Data-dense, but every gap sits on the spacing scale; related items cluster, unrelated items separate.

  8. Every state is designed. Hover, focus, active, disabled, loading, empty, error — no browser defaults showing through, no accidental empty regions.

  9. The structure is the style. The HaTchi-MaXchi signature: HTMX4 anatomy is expressed, not hidden. Swap targets read as coherent surfaces, hx-indicator and 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 dimension state_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 Icon fragment 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), with prefers-reduced-motion honored. (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

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) and interleaves sources blindly. It recomputes per-dimension margins from judge noise on each run using margin = max(0.5, 2 × judge noise SD). The following values describe the 2026-07-02 baseline; they are historical calibration, not fixed thresholds:

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

The July baseline used unseeded pages and a mixed reference pool. It cannot serve as a before/after score for a particular example app. Any fleet rerun must report its sampling and reference eligibility alongside the scores.

Measuring a change to an example app

For a visual improvement, compare the same seeded persona and job before and after the change at the same viewport, theme, URL, and browser state. Capture the 1440×900 first viewport and a full page; include a dark capture when the change affects shared styling. dazzle qa capture --persona <role> --workspace <desk> --above-fold selects a single scene without walking every workspace.

Use the six taste dimensions above for visual craft. Record job clarity separately: whether the next relevant item, its status, and its available action are visible and understandable in the first viewport. Report each scene's paired delta and the worst scene, then an equal-scene app summary. Do not average all screens from different apps or roles into a claimed app improvement. A valid shadcn reference is an analogous job scene with its primary heading and content intact in the frame; a 404, blank, clipped, or documentation-heavy capture is ineligible. Reference pixels are a quality comparison, not a requirement to copy their layout or implementation.