docs(gui): unified design-system architecture

One substrate (@hanzo/gui), one component library (@hanzo/ui), brands as
values (@luxfi/ui, @zooai/ui). Two orthogonal axes (substrate×components),
substrate-splits-by-domain-not-brand rule, dependency spine, invariants,
and the visual-regression-gated migration phases.
This commit is contained in:
2026-06-24 14:29:56 -07:00
parent 51ee01709f
commit 040e3773d5
3 changed files with 74 additions and 1 deletions
@@ -0,0 +1,70 @@
---
title: Architecture
description: One substrate, one component library, brands as values — the unified cross-platform design system.
icon: Layers
---
Hanzo GUI is the **substrate** for one unified design system shared across every Hanzo, Lux, and Zoo surface — web, native, and desktop. This page is the canonical architecture: the layering, the rules that keep it de-duplicated, and the dependency spine every app inherits.
## The problem it removes
Historically the ecosystem grew three parallel component systems for one concept: `@hanzo/gui` (cross-platform substrate), a standalone `@hanzo/ui` (with no relation to it), and per-brand forks (`@luxfi/ui`, `@zooai/ui`). The same button existed three times. That is the complection this architecture cuts: **a component is defined once, themed many times, and rendered everywhere.**
## Two orthogonal axes
Design-system concerns separate cleanly along two independent axes. Brand is only one of them — and it is *not* the substrate axis.
| | **Substrate** (mechanism: render anywhere) | **Components** (composition: the parts) |
|---|---|---|
| **Generic** | `@hanzo/gui` | `@hanzo/ui` |
| **Domain** (web3 / wallet / crypto) | `@hanzo/gui-web3` | `@hanzo/ui-web3` |
| **Brand** (lux / zoo values) | *— nothing here —* | `@luxfi/ui`, `@zooai/ui` |
- **Substrate** = *how* to render on web, native (React Native / Expo), and desktop (Tauri): tokens, themes, the compiler, accessible primitives. Knows nothing of brand or app.
- **Components** = *what* the parts are — Hanzo's design language, composed from substrate primitives. The single public surface apps import.
- **Brand** = *values*, not places. Theme tokens (color, type, radius, motion) plus a few brand-specific compositions. Brands re-implement **nothing**.
## The two rules that keep it "exactly one way"
> **Rule 1 — Substrate splits by *domain*, never by *brand*.**
> `@hanzo/gui` (generic) + `@hanzo/gui-web3` (crypto primitives: wallet-connect, chain selector, address/amount inputs, QR scanner with native camera, hardware-wallet bridge). These are cross-platform *mechanism* and brand-agnostic — Lux needs them and so does Zoo. A brand-scoped substrate (`@luxfi/gui`) would re-fork the same primitives and re-introduce the complection. Resist brand-at-the-substrate.
> **Rule 2 — Brand is a *value*, not a *place*.**
> `@luxfi/ui` / `@zooai/ui` = theme tokens + brand-only compositions over `@hanzo/ui`. A `Button` is defined once, in `@hanzo/ui`. Brands never redefine it; they theme it.
## The dependency spine
```text
@hanzo/gui ──┬── @hanzo/gui-web3 substrate: generic + crypto domain
│ (shared, never brand-forked)
▼
@hanzo/ui ───┴── @hanzo/ui-web3 components composed from the substrate
▼
@luxfi/ui = @hanzo/ui(+web3) + lux theme + lux-only compositions
@zooai/ui = @hanzo/ui(+web3) + zoo theme + zoo-only compositions
▼
Lux apps import @luxfi/ui only
Zoo apps import @zooai/ui only
Hanzo apps import @hanzo/ui only
```
An app imports **one** package. From it, transitively, it inherits web + native + desktop rendering, the crypto primitives, and its brand theme. No app imports `@hanzo/gui` directly — the substrate is internal, like React's own internals.
## Invariants
1. **One definition.** Every component is defined exactly once (in `@hanzo/ui` / `@hanzo/ui-web3`). No duplicate exists anywhere in the tree.
2. **One import per app.** Apps depend only on their brand `ui` (`@hanzo/ui` for Hanzo). Importing `@hanzo/gui` from an app is a smell.
3. **Brand = theme.** A new brand is a token set + theme, not a component fork. Adding `@parsdao/ui` means writing a theme, not 60 components.
4. **Substrate by domain.** New cross-platform capability goes in `@hanzo/gui` (generic) or a domain module (`@hanzo/gui-web3`) — never in a brand package.
5. **Stable public API.** Re-basing `@hanzo/ui` onto `@hanzo/gui` preserves its import surface, so consuming apps upgrade without rewrites.
6. **Look/feel is a contract.** Migrations are gated by Playwright visual-regression: the rendered output must match the pre-migration baseline within tolerance before a phase merges. Decomplecting the implementation must not change a single pixel the user sees.
## Migration phases
Each phase builds green and passes the visual-regression gate before the next begins — no big-bang.
- **Phase 0 — Foundation.** `@hanzo/ui` takes a dependency on `@hanzo/gui` and re-bases its components as substrate compositions, **keeping the public API identical**. The standalone primitives are deleted.
- **Phase 1 — Brands.** `@luxfi/ui` and `@zooai/ui` become token/theme overlays on the new `@hanzo/ui`; brand-unique widgets re-expressed as substrate compositions.
- **Phase 2 — Adopt.** Sites and apps move to the unified brand `ui` and inherit web / native / desktop.
The canonical, binding form of this standard is the corresponding HIP; this page is its developer-facing reference.
+1
View File
@@ -9,6 +9,7 @@ Hanzo GUI is a universal UI framework for building cross-platform applications w
## Start Here
- [Introduction](/docs/intro) — What Hanzo GUI is and why it exists
- [Architecture](/docs/architecture) — One substrate, one component library, brands as values
- [Core Concepts](/docs/core) — Styled, variants, tokens, themes, and animations
- [Guides](/docs/guides) — Framework setup for Next.js, Vite, Expo, and more
- [Components](/docs/components) — 50+ universal components
+3 -1
View File
@@ -2,6 +2,8 @@
"pages": [
"---Getting Started---",
"intro",
"---Architecture---",
"architecture",
"---Core---",
"core",
"---Components---",
@@ -9,4 +11,4 @@
"---Guides---",
"guides"
]
}
}