/src/CLAUDE.md
CLAUDE.md at /src/CLAUDE.md
Path: src/
Source Code Rules
Components
All components are Preact
.tsx— there are no.astrofiles. Pages, layouts, and component overrides are all written as Preact function components.Default to server-rendered Preact (no
client:*directive) — emits zero JS for static markup.Promote a component to a client island only when it needs interactivity. zfb hydration is opt-in — islands are wired by direct
<Island>wrapping (no central registry file).Island wiring locations (package-owned since the minimal-scaffold cutover, epic zudolab/zudo-doc#2651): the doc-route stubs (
pages/docs/[[...slug]].tsxand its[locale]/v/**variants) callcreateChrome(routeCtx, chromeBindings)(@takazudo/), which builds the header/sidebar/toc/footer chrome and mounts the body-end islands — there is no more project-sidezudo- doc/ chrome _header-with-defaults.tsx/_sidebar-with-defaults.tsx/_doc-page-shell.tsxwrapper (those moved into the package asheader-with-defaults/,sidebar-with-defaults/,doc-page-shell/).pages/is this repo's showcase-only host body-end implementation (ClientRouterBootstrap, AiChatModal, ImageEnlarge, MermaidEnlarge); it is threaded in vialib/ _ body- end- islands. tsx src/'schrome- bindings. tsx BodyEndIslandsslot andzfb.config.ts'schromeBindingsModulesetting.DesignTokenPanelBootstrapis package-owned and composed alongside that host override at thechrome/seam, while a fresh scaffold gets it inside the package-default body-end islands.derive. tsx DocHistoryis NOT auto-defaulted (its package default is a deliberate no-op stub) — a project that wants real doc-history data must thread it throughchromeBindingsModule, same as this showcase does.Current LOCAL client islands (
src/components/):client-router-bootstrap.tsxandpreset-generator.tsx(the latter registered through thepages/shim — see that file's header for why the static import chain is load-bearing; its logic lives inlib/ _ preset- generator. tsx src/). The design-token panel bootstrap and everything else that used to be a local island —lib/ preset- generator- logic. ts sidebar-toggle.tsx,sidebar-tree.tsx,doc-history.tsx,image-enlarge.tsx,ai-chat-modal.tsx,desktop-sidebar-toggle.tsx, ThemeToggle, Toc, MobileToc — are now PACKAGE-OWNED (@takazudo/zudo-doc/{design-token-panel-bootstrap,sidebar-toggle-island,sidebar-tree-island,doc-history,image-enlarge,ai-chat-modal,desktop-sidebar-toggle-island,theme-toggle,toc}); their npm-dist"use client"modules are scanned by zfb >= 0.1.0-next.39 (zfb#999/#1001), so no local scanner-visible shims exist (re-adding one creates an island marker-name collision). Every ejectable component is listed inpackages/'szudo- doc/ src/ eject/ index. ts EJECTABLEmap.Content typography components: server-rendered Preact functions that override HTML elements emitted by MDX (headings h2-h4, paragraph, link, strong, blockquote, lists ul/ol, table). Since the package-first migration (epic #2321) this whole system — the component implementations AND the
<Content components={...} />mapping (component-map.ts) — is package-owned (packages/, wired byzudo- doc/ src/ content/ @takazudo/); there is no more hostzudo- doc/ mdx- components pages/. The former host re-export shims_ mdx- components. ts src/andcomponents/ content/ code- group. tsx content-admonition.tsxwere deleted (zudolab/zudo-doc#3160) once no@/components/content/*call sites remained — call sites now importCodeGroup/ContentAdmonitiondirectly from@takazudo/zudo-doc/{code-group,content-admonition}.
Design Token System
Uses a ramp-native color system: a ColorScheme is { ramps, map } (package-owned since the minimal-scaffold cutover — see packages/; the former host copy src/ was byte-identical dead weight, deleted in epic zudolab/zudo-doc#2651 Wave 6 #2661). Minimized to base 5 / accent 3 / state 4 (#2602) — see the color-scheme-a11y skill and src/ for the full model.
Three-Tier Color Strategy
Tier 1 — Ramps (injected by ColorSchemeProvider on :root):
--zd-bg,--zd-fg,--zd-selection-bg,--zd-selection-fg--palette-base-0…--palette-base-4(5 stops),--palette-accent-0…--palette-accent-2(3 stops),--palette-state-{danger,success,warning,info}No raw-palette Tailwind utilities exist (no
bg-p0-style classes) — the ramps feed Tier 2 only.
Tier 2 — Semantic tokens (in global.css @theme, resolved per scheme):
Base:
bg,fg→bg-bg,text-fgUI:
surface,muted,accent,accent-hover,sel-bg,sel-fgContent:
code-bg,code-fg,success,danger,warning,info
Tier 3 — Component tokens (scoped to specific components):
Content:
.zd-contentdirect element styling in the shared@takazudo/(imported byzudo- doc/ content. css global.css; consumes Tier 2 tokens the project defines)
Each tier only references the tier above it.
Color Rules
NEVER use Tailwind default colors (
bg-gray-500,text-blue-600) — they are reset toinitialNEVER use hardcoded color values (
rgba(),#hex,rgb()) — use semantic tokens orcolor-mix()with tokensALWAYS use project tokens:
text-fg,bg-surface,border-muted,text-accent, etc. Semantic tokens are the only Tailwind-facing color surface — there is no utility for a raw ramp stop (nop0–p15-style classes).
Raw var(--palette-*) usage, overlays/backdrops, the role-split highlight tokens, and the exact list of acceptable exceptions to the no-hardcoded-color rule live in the zudo-doc-design-system skill — invoke / before writing color CSS.
Changing Scheme
Edit
colorSchemeinsrc/config/ settings. ts Available:
Default Light,Default Dark— the only two bundled schemes, sharing one set of ramps. There is no bundled catalog of community/terminal presets (a legacy 50+ preset "Scheme…" dropdown was dropped in the ramp restructure)Add schemes in
packages/(or pass azudo- doc/ src/ color- schemes- defaults/ index. ts colorSchemesoverride tozudoDoc({...})inzfb.config.tsfor a project-local scheme without touching the package): each is{ ramps, map }—ramps(base 5-stop + accent 3-stop + 4 state colors) plus a per-modemapwiring the 4 base roles and 23 semantic roles to a ramp stop or literal OKLCHRampReftype:{ base: n } | { accent: n } | { state: role } | string— a shared ramp stop, or a literal OKLCH string used as-is (typically a per-mode AA tune)Accessibility: any scheme add/edit/tweak must clear WCAG contrast floors — consult the
color-scheme-a11yskill (.) for the pair matrix, thresholds, OKLCH tweak methodology, and the new-scheme checklistclaude/ skills/ color- scheme- a11y/ SKILL. md
Design Token Panel (zdtp)
Enabled via
designTokenPanel: truein settingsImplemented by the external
@takazudo/zdtp(zdtp) package; the package-ownedDesignTokenPanelBootstrapisland callsbootstrapDesignTokenPanel(buildDesignTokenPanelConfig)with the mode-scoped builder, so the panel rebuilds per light/dark mode. The static route → chrome → derive → bootstrap import chain is load-bearing for island registration; there is no host bootstrap bridge. That chain statically imports the bootstrap MODULE only —@takazudo/zdtpitself is lazy-loaded (#3282):bootstrapDesignTokenPanelcarries no top-level@takazudo/zdtpimport, deferringimport("@takazudo/zdtp")to the firsttoggle-design-token-paneldispatch, or loading it eagerly (before any click) when a mount-time probe finds persisted panel state (a saved override, an open panel, or an owner-mode flag) under the active storage prefix. See the docblock indesign-token-panel-bootstrap.tsxand the "Lazy loading" section ofsrc/for the exact contract.content/ docs/ reference/ design- token- panel. mdx Interactive tabbed panel for live editing of spacing, font, size, and color tokens; includes JSON export/import workflow for AI-assisted token round-trips
The header trigger button dispatches
toggle-design-token-panelonwindow; zdtp listens for this event nativelyStorage prefix is
zudo-doc-tweak. The installed zdtp package owns its current persisted-state format; the host must not read or rewrite private storage keys. The prefix is set viastoragePrefixin the package-default builder (@takazudo/, source atzudo- doc/ design- token- panel- config packages/) and is guaranteed not to change. Toggling light/dark does NOT delete saved tweaks:zudo- doc/ src/ design- token- panel- config/ index. ts ThemeToggle(packages/) dispatcheszudo- doc/ src/ theme- toggle/ color- scheme- sync. ts color-scheme-changed. Two listeners react: (1) zdtp's own listener clears applied inline styles and re-seeds the color slice from the newly active scheme while preserving persisted state; (2) the panel bootstrap (@takazudo/, #2610) coalesces the toggle onto a macrotask, thenzudo- doc/ design- token- panel- bootstrap destroy()s and reconfigures the panel with the new mode's mode-scoped semantic defaults (buildDesignTokenPanelConfig(mode)), re-mounting if it was open. This keeps the Color tab's per-mode defaults faithful. A saved color override is still mode-agnostic here because this host switches its scheme-less color cluster outside zdtp's owncolorModefield.
Three-Tier Font-Size Strategy
Uses the same three-tier approach as colors: abstract scale → semantic roles → component usage.
Tier 1 — Abstract scale (--text-scale-* in :root, NOT @theme):
Raw size values only:
2xs(12px),xs(14px),sm(16px),md(19.2px),lg(22.4px),xl(48px),2xl(60px)Kept in
:rootintentionally — avoids generating Tailwindtext-scale-*utility classes that would bypass the semantic layerNEVER use scale tokens directly in components — they exist only as a single source of truth for Tier 2
Tier 2 — Semantic tokens (--text-* in @theme, reference Tier 1):
micro(2xs/12px),caption(xs/14px),small(sm/16px),body(md/19.2px),title(lg/22.4px),heading(xl/48px),display(2xl/60px)Each is a pure
var(--text-scale-*)reference — Tier 2 carries the role, Tier 1 carries the value. The Design Token Panel models this exactly: the Font tab's role tier is areferencesTier: "font-scale"tier (dropdowns picking a scale step), mirroring the Color tab's semantic→palette tier. Editing a scale step propagates to every role live.Use these via Tailwind classes:
text-body,text-caption,text-micro,text-heading, etc.Name roles by their role, broadly enough to cover every usage (
titlecovers h2 / card / modal / section headings).subheadingwas renamed totitlebecause its name implied a narrower scope than its actual broad use. A role used in only one place should instead be a scoped Tier 3 token.
Tier 3 — Component usage (Tailwind classes in markup):
Components consume Tier 2 tokens:
<p class="text-body">,<h1 class="text-heading">.zd-contenttypography (shipped in@takazudo/, imported byzudo- doc/ content. css global.css) also references Tier 2 tokensFor a genuinely component-specific size that should not become a global role, add a scoped CSS custom property on the component (e.g.
--_card-amount: var(--text-scale-2xl)) referencing Tier 1/Tier 2 — do NOT widen a Tier 2 role to fit one component.
To add a new font size: add the raw value to Tier 1, then create a semantic token in Tier 2 that references it. Keep the panel in sync by adding the role→scale mapping in FONT_ROLE_TO_SCALE (@takazudo/, source at packages/).
Two-Tier Size Strategy
Element dimensions (icons, toggles, etc.) follow a two-tier approach:
Tier 1 — Semantic tokens (in global.css @theme): shared design decisions with meaningful names.
Icon sizes:
icon-xs(12px),icon-sm(16px),icon-md(20px),icon-lg(24px)Usage:
w-icon-sm h-icon-sm,w-icon-md h-icon-md, etc.Add new tokens only when a size is used in 2+ unrelated components with the same semantic role
Tier 2 — Arbitrary values: one-off component dimensions that don't recur.
Example:
w-[1.575rem]for a breadcrumb home icon,h-[3rem]for a toggle button heightKeep as arbitrary values until the pattern recurs enough to justify a token
Rules:
No abstract numeric scale (no
size-4,size-8) — semantic names onlyTokenize when 2+ components share the same size for the same purpose (e.g., "standard icon")
Keep arbitrary values for layout dimensions, modal sizes, and component-specific one-offs
Z-index tokens
The 13 semantic --z-index-* tiers ship unconditionally from @takazudo/ (imported by src/) — the showcase does not customize beyond the package default, so the former project-side src/ + gen-z-index/check:z-index codegen (S9b #2334) was retired in zudolab/zudo-doc#2661 (it produced this exact tier list byte-for-byte). A project that DOES want a custom tier overrides the specific --z-index-<name> token in the @theme { … } block at the bottom of global.css (after the package imports, so it wins the cascade) — see packages/'s "Shipped CSS artifacts" section.
CSS & Components
Before writing or editing CSS, Tailwind classes, color tokens, or component markup, invoke
/to load project-specific ruleszudo- doc- design- system Tailwind v4: imports
tailwindcss/preflight+tailwindcss/utilities(no default theme)@themehas--color-*: initial;at the top — project tight-token guardrail: wipes all Tailwind default color tokens so only project-defined tokens are available. The upstream split-import fix (zfb#159 / 9e37551) shipped in f68a9ba and eliminated the original leak cause; the reset is retained as an explicit design rule per the "NEVER use Tailwind default colors" policy. Do NOT remove.Content typography: component-first approach — major HTML elements (h2-h4, p, a, strong, blockquote, ul, ol, table) are overridden via package-owned Preact components (
packages/) registered through the package's ownzudo- doc/ src/ content/ component-map.ts, not a host file. Everything else (minor elements, flow-space/heading/hash-link structural rules, admonitions) lives in.zd-contentinpackages/— the single source of truth; never re-inline it into anyzudo- doc/ src/ content. css global.css(#2188). Canonical rules and rebuild duty:packages/.zudo- doc/ CLAUDE. md# shipped- css- artifacts- five global.csskeeps only@themetokens, feature styles, and slots.Component-first strategy: always use Tailwind utility classes directly in component markup — never create CSS module files or custom CSS class names. The component itself is the abstraction.
Tight token strategy: prefer existing spacing (
hsp-*,vsp-*), typography (text-caption,text-small, etc.), and color tokens. Avoid arbitrary values (text-[0.8rem],py-[0.35rem]) when an existing token is close enough.