zudo-doc
GitHub repository

Type to search...

to open search from anywhere

Design System

Created Mar 13, 2026Updated Jul 13, 2026Takeshi Takatsudo

zudo-doc's tight token strategy — spacing, typography, colors, and more.

zudo-doc uses a tight token strategy — instead of importing the full Tailwind framework, only preflight and utilities are loaded, skipping the default theme entirely. The package then supplies a small, intentional set of design tokens. This page covers the full system: spacing, typography, colors, border radius, breakpoints, and layering.

Tight Token Strategy

Tailwind CSS ships with hundreds of built-in values — colors, spacing scales, font sizes, and more. In a themeable project, these defaults cause problems: hardcoded values ignore theme changes and create inconsistency.

The scaffold's src/styles/global.css imports only Tailwind's preflight (reset) and utilities (functional classes), then imports the package's token stylesheet. The default Tailwind theme layer is deliberately skipped:

@layer zd-preflight, zd-flow;
@import "tailwindcss/preflight" layer(zd-preflight);
@import "tailwindcss/utilities";
@import "@takazudo/zudo-doc/theme.css";
@import "@takazudo/zudo-doc/safelist.css";
@import "@takazudo/zudo-doc/content.css";
@import "@takazudo/zudo-doc/page-loading.css";
@import "@takazudo/zudo-doc/features.css";

/* Override package defaults here when the project needs to. */
@theme {
  /* e.g. --color-accent: oklch(0.6 0.2 250); */
}

@takazudo/zudo-doc/theme.css ships the default @theme tokens. Its package source, packages/zudo-doc/src/theme.css, is the source of truth; the scaffold's later, empty @theme block is an override point. The shipped block opens with --color-*: initial to wipe Tailwind's default palette before adding back project color tokens.

Info

This is the core philosophy: define only what the project needs. Every value in the system is intentional, and accidental use of Tailwind defaults is immediately visible.

What Tailwind vocabulary you have

The tight token strategy does not remove Tailwind's utility syntax. It narrows which token-backed utilities have values. Use the project vocabulary below when writing MDX or components.

WorksDead (silently unstyled)
project tokens: text-caption, bg-accent, px-hsp-*, py-vsp-*, w-icon-*, z-index tiers such as z-modalnumeric spacing: p-4, mt-8, w-64
tracking-tight, tracking-normal, tracking-wide, tracking-widertracking-tighter, tracking-widest
rounded, rounded-lg, rounded-fulldefault type scale: text-sm, text-lg
static utilities: flex, grid-cols-3, hidden, ...default palette: bg-blue-500 (reset by design)
arbitrary values: m-[13.37px], w-[42%] — the full escape hatchshadow-md (only shadow-lg exists)

The scaffold scans MDX content, src/components/, src/chrome-bindings.tsx, and pages/ through its @source coverage and Tailwind's automatic detection. A class used in a location that is not scanned is emitted as unstyled markup, so add an @source directive when you introduce a new source location.

To re-enable a default Tailwind vocabulary, import tailwindcss/theme, or define the specific tokens you need in the project's @theme override block. Prefer a local semantic token when the value is part of the project's design language.

For examples of using this vocabulary in MDX components, see Custom Components.

Spacing

zudo-doc separates spacing into two axes: horizontal (hsp) and vertical (vsp). They serve different purposes in layout — horizontal spacing controls inline rhythm and gutters, while vertical spacing controls content flow and section separation. The vertical scale is stretched at larger sizes to give content more breathing room.

Horizontal Spacing (hsp)

TokenValueExample Class
hsp-2xs0.125rem (2px)px-hsp-2xs
hsp-xs0.375rem (6px)px-hsp-xs
hsp-sm0.5rem (8px)gap-x-hsp-sm
hsp-md0.75rem (12px)px-hsp-md
hsp-lg1rem (16px)px-hsp-lg
hsp-xl1.5rem (24px)px-hsp-xl
hsp-2xl2rem (32px)px-hsp-2xl

Vertical Spacing (vsp)

TokenValueExample Class
vsp-3xs0.25rem (4px)py-vsp-3xs
vsp-2xs0.4375rem (7px)py-vsp-2xs
vsp-xs0.875rem (14px)py-vsp-xs
vsp-sm1.25rem (20px)gap-y-vsp-sm
vsp-md1.5rem (24px)py-vsp-md
vsp-lg1.75rem (28px)py-vsp-lg
vsp-xl2.5rem (40px)py-vsp-xl
vsp-2xl3.5rem (56px)py-vsp-2xl

Both axes also include 0 (0px) and px (1px) utility values.

<!-- Horizontal padding + vertical padding -->
<div class="px-hsp-lg py-vsp-md">Content with asymmetric spacing</div>

<!-- Grid with dual-axis gaps -->
<div class="grid gap-x-hsp-md gap-y-vsp-lg">Grid items</div>

Tip

Notice that hsp-lg is 1rem while vsp-lg is 1.75rem — the vertical axis is stretched at larger sizes. This is intentional — vertical flow needs more room than horizontal rhythm.

Element Sizes

Icon Sizes

TokenValueExample Class
icon-xs0.75rem (12px)w-icon-xs h-icon-xs
icon-sm1rem (16px)w-icon-sm h-icon-sm
icon-md1.25rem (20px)w-icon-md h-icon-md
icon-lg1.5rem (24px)w-icon-lg h-icon-lg

Elevation

Only one shadow token is defined.

TokenValueExample Class
lg0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)shadow-lg

Typography

Font Sizes

Each semantic role references an abstract scale step (--text-scale-*); the values below are the resolved sizes.

TokenValueExample
micro0.75rem / 12pxtext-micro
caption0.875rem / 14pxtext-caption
small1rem / 16pxtext-small
body1.2rem / 19.2pxtext-body
title1.4rem / 22.4pxtext-title
heading3rem / 48pxtext-heading
display3.75rem / 60pxtext-display

Font Weights

TokenValueExample
normal400font-normal
medium500font-medium
semibold600font-semibold
bold700font-bold

Line Heights

TokenValueExample
tight1.25leading-tight
snug1.375leading-snug
normal1.5leading-normal
relaxed1.625leading-relaxed

Letter Spacing

Four steps from negative to open. Use tracking-tight on large headings to keep them optically tight; tracking-normal resets to the browser default; tracking-wide/tracking-wider suit labels and small-caps text.

TokenValueExample
tight-0.025emtracking-tight
normalnormaltracking-normal
wide0.05emtracking-wide
wider0.1emtracking-wider

Font Families

TokenStackExample
sansSystem sans-serif stackfont-sans
monoSystem monospace stackfont-mono
<h1 class="text-heading font-bold leading-tight">Page Title</h1>
<p class="text-body font-normal leading-normal">Body text</p>
<code class="text-small font-mono">inline code</code>

Border Radius

TokenValueExample
DEFAULT0.25rem (4px)rounded
lg0.5rem (8px)rounded-lg
full9999pxrounded-full
<button class="rounded bg-accent text-bg">Default radius</button>
<div class="rounded-lg bg-surface">Card with larger radius</div>
<span class="rounded-full bg-muted">Pill badge</span>

Breakpoints

TokenValueExample
sm640pxsm:flex
lg1024pxlg:grid-cols-2
xl1280pxxl:max-w-5xl
<div class="px-hsp-sm sm:px-hsp-md lg:px-hsp-lg xl:px-hsp-xl">
  Responsive horizontal padding
</div>

Z-index Tiers

Use semantic z-index tiers instead of arbitrary stack values.

TokenValueExample Class
content0z-content
local-11z-local-1
local-22z-local-2
local-33z-local-3
sidebar10z-sidebar
toolbar20z-toolbar
dropdown30z-dropdown
popover40z-popover
modal-backdrop50z-modal-backdrop
modal60z-modal
toast70z-toast
tooltip80z-tooltip
drag90z-drag

Colors

Colors use a three-tier strategy: raw palette values (Tier 1) flow into semantic tokens (Tier 2), which feed into component-scoped tokens (Tier 3). Each tier only references the tier above it, so swapping a color scheme updates the entire site at once.

See the Color reference for full details on the color token system, color schemes, and customization.

Usage Rules

Tailwind's default theme is disabled

The default Tailwind theme is not imported. Project tokens come from @takazudo/zudo-doc/theme.css, then the project's @theme override block. A token that is not defined produces unstyled markup.

Do

<!-- Semantic color tokens -->
<p class="text-fg">Primary text</p>
<div class="bg-surface border border-muted">Panel</div>
<a class="text-accent hover:text-accent-hover">Link</a>

<!-- Spacing tokens -->
<div class="px-hsp-lg py-vsp-md">Proper spacing</div>
<div class="gap-x-hsp-sm gap-y-vsp-md">Grid gaps</div>

<!-- Typography tokens -->
<h2 class="text-title font-semibold leading-tight">Heading</h2>

Don't

<!-- DON'T: Tailwind defaults — not defined and produce nothing -->
<div class="p-4 bg-gray-500 text-sm">Broken</div>

<!-- DON'T: Hardcoded hex — breaks theming -->
<div class="bg-[#1e1e2e] text-[#f8f8f2]">Breaks on theme switch</div>

Default tokens ship from @takazudo/zudo-doc/theme.css; packages/zudo-doc/src/theme.css is their source of truth. The scaffold's src/styles/global.css imports those defaults and provides the project override point.

Interaction Rules

Elements that navigate (rendered as <a href> or behave as a link) must show an underline on hover and on keyboard focus. Buttons, toggles, and controls do not — they use a border/background change instead.

Do (navigational links):

<a class="text-accent hover:underline focus-visible:underline">Link</a>
<a class="text-fg hover:text-accent hover:underline focus-visible:underline">Sidebar item</a>

Don't (missing focus-visible parity):

<!-- DON'T: mouse users get an underline, keyboard users don't -->
<a class="text-fg hover:underline">Inaccessible to keyboard focus</a>

Don't (controls):

<!-- DON'T: buttons use border/bg hover, not underline -->
<button class="hover:underline">Use hover:bg-accent/10 instead</button>

The pair hover:underline focus-visible:underline is the canonical form — always add both, never one without the other. Precedents: packages/zudo-doc/src/header/header.tsx, src/components/site-tree-nav.tsx, packages/zudo-doc/src/footer/footer.tsx.

For the broader reasoning (light-mode / dark-mode contrast, when an underline is sufficient vs when a color shift is also needed), consult the /css-wisdom skill locally — its light-mode/dark-mode and three-tier token strategy sections cover the trade-offs.

Revision History

Takeshi TakatsudoCreated: 2026-03-14T08:13:16+09:00Updated: 2026-07-13T23:55:15+09:00

AI Assistant

Ask a question about the documentation.

Preview theme

Loading theme previews…