Every other package in this admin was already on its latest published version; vite was two majors behind at 6.4.3. Both plugins support 8 at their latest (@vitejs/plugin-react 6.0.5 peers vite ^8.0.0, router-plugin 1.168.23 peers >=8.0.0), so the pin was the only thing keeping it there. `esbuild` was a direct devDependency that nothing imported: no source file references it and no plugin here peers on it — router-plugin's peer list is rsbuild/vite/solid/webpack/react-router. It only surfaced as a problem because vite 8 peers esbuild ^0.27||^0.28 and 0.25 tripped it, which is how a vestigial dep announces itself. Removing it is what silenced the warning; bumping it would have kept an unused package on the list. `build.rollupOptions.output.manualChunks: undefined` set a default to its own default. Removing it produces byte-identical output (same content hashes), and under rolldown the option is spelled elsewhere anyway, so it was config that could only mislead. Rolldown does the same work in a fraction of the time and ships less of it: 7.9s -> 1.3s, 871 kB -> 836 kB (gzip 269 -> 254). Verified the bundle, not just the build — the whole point of smoke.mjs is that a green build says nothing about what renders, and a bundler swap is exactly the change that could quietly break it. All 19 routes paint, every token resolves, no utility-class residue, no unprobed control, overlays open, confirm dialog measures 384. pnpm build 0 (vite v8.2.0, built in 1.34s) · pnpm smoke SMOKE OK 0 · CGO_ENABLED=0 go build 0 · go test -count=1 ./ui-react/... ./apis/... ok 144.5s Co-authored-by: Hanzo Dev <[email protected]>
Base Admin UI
The Base admin SPA. Source is ./src; pnpm build writes ./dist, and
embed.go compiles that into the Base binary with //go:embed all:dist,
which the server mounts at /_/ when BASE_ENABLE_ADMIN_UI=1.
dist/ is committed so go build is hermetic — CI compiles the binary
without a Node toolchain. Rebuild it in the same commit as any src/ change.
Build
pnpm install
pnpm build # tsc --noEmit && vite build -> ./dist
pnpm dev # vite, proxying /v1 to a local Base on :8090
Two env knobs, both set at build time and both mirrored by the Go server:
| Env | Default | Read by |
|---|---|---|
BASE_ADMIN_UI_PATH |
/_/ |
Vite base + the Go static mount |
VITE_IAM_URL / VITE_IAM_CLIENT_ID |
https://hanzo.id / hanzo-base |
src/lib/iam.ts |
Stack
One framework, all platforms. No Tailwind, no Radix, no shadcn.
| Layer | What |
|---|---|
| Framework | React 19 + TanStack Router (file-based, src/routes/**) + TanStack Query |
| Bundler | Vite 8 (rolldown) |
| Components | @hanzo/ui on @hanzo/gui — the cross-platform primitives, same import on web/native/desktop |
| Icons | @hanzogui/lucide-icons-2 |
| Styling | src/index.css: @hanzo/design tokens (plain CSS custom properties) + ~30 class names for the things this admin has |
| Data | One /v1 fetch layer (src/lib/api.ts) behind a base.collection(x).method() facade (src/lib/base-client.ts); realtime is /v1/realtime SSE |
| Auth | Hanzo IAM, OAuth2 PKCE via @hanzo/iam (src/lib/iam.ts). No local password — the fork retired _superusers password auth and the server 404s auth-with-password for every collection |
Where the line falls: overlays (dialog, dropdown) are @hanzo/ui components,
because they carry a11y, focus management and placement that CSS cannot. Every
other surface is a class in index.css. A route says what a thing IS.
Verifying a change
pnpm build going green does not prove a visual change worked. @hanzo/gui
drops a prop it does not recognise in silence — no error, no type failure, just
an unstyled element — and CSS resolves an undefined var(--x) to nothing. Both
layers fail quietly, so run the browser check:
pnpm smoke # needs playwright; PLAYWRIGHT=/path/to/playwright/index.mjs
It serves the committed dist/ under /_/ exactly as the Go server does, stubs
/v1, and gates on: every route paints, every var(--token) resolves, zero
utility-class residue, every control computes real colours and radii, the
overlays open, and the confirm dialog measures the width DialogContent maxW
asks for. That last one is the shape of the silent-drop bug, so it is asserted,
not just printed.