zudo-doc

Type to search...

to open search from anywhere

EN/JA/DE

Color

Erstellt11. März 2026Aktualisiert11. März 2026Takeshi Takatsudo

zudo-doc's three-tier color strategy, palette system, color schemes, and customization.

zudo-doc uses a three-tier color strategy to keep every color on the site themeable. All Tailwind default colors are reset to initial — only project tokens work. This ensures switching a color scheme updates the entire site at once.

Three-Tier Color Strategy

Colors are organized into three tiers. Each tier only references the tier above it:

TierNamePurposeDefined In
1PaletteRaw color values from the active color schemeColorSchemeProvider:root
2SemanticDesign meaning — what each color representssrc/styles/global.css @theme
3ComponentScoped overrides for specific components.zd-content in global.css

This layering means you can:

  • Swap the palette (change color scheme) → entire site updates
  • Remap a semantic token (e.g. make accent blue instead of cyan) → every component using accent updates
  • Override a component token without affecting other components

Tier 1: The 16-Color Palette

zudo-doc’s color system uses a 16-color palette. Each color scheme provides values for 16 indexed color slots plus background, foreground, and selection colors.

Palette Slots

SlotNameBright SlotBright NameTypical Use
0Black8Bright BlackSurfaces, muted text, borders
1Red9Bright RedDanger, errors
2Green10Bright GreenSuccess, tips
3Yellow11Bright YellowWarnings
4Blue12Bright BlueInfo, notes
5Magenta13Bright MagentaCode highlights
6Cyan14Bright CyanAccent, links
7White15Bright WhiteText, bright elements

📝 Note

The actual colors are not fixed — “Red” in Dracula is #ff5555, while “Red” in Nord is #bf616a. The names are conventions. That’s why zudo-doc uses abstract numbered tokens (p0p15) instead of color names.

How Palette Colors Are Injected

The ColorSchemeProvider component (src/components/color-scheme-provider.astro) reads the active color scheme and injects CSS custom properties on :root at build time:

:root {
  --zd-bg: #282a36;
  --zd-fg: #f8f8f2;
  --zd-cursor: #f8f8f2;
  --zd-sel-bg: #44475a;
  --zd-sel-fg: #ffffff;
  --zd-0: #21222c;   /* palette slot 0 (Black) */
  --zd-1: #ff5555;   /* palette slot 1 (Red) */
  /* ... through --zd-15 */
}

These --zd-* properties are the source of truth. Everything downstream — semantic tokens, component tokens, Tailwind utilities — resolves back to them.

Tier 2: Semantic Tokens

In src/styles/global.css, the @theme block maps palette properties into Tailwind-compatible tokens with design meaning:

@theme {
  --color-*: initial;  /* reset ALL Tailwind defaults */

  /* Base */
  --color-bg: var(--zd-bg);
  --color-fg: var(--zd-fg);
  --color-sel-bg: var(--zd-sel-bg);
  --color-sel-fg: var(--zd-sel-fg);

  /* Raw palette access (p0–p15) */
  --color-p0: var(--zd-0);
  --color-p1: var(--zd-1);
  /* ... through --color-p15 */

  /* Semantic aliases */
  --color-surface: var(--zd-surface);
  --color-muted: var(--zd-muted);
  --color-accent: var(--zd-accent);
  --color-accent-hover: var(--zd-accent-hover);
  --color-code-bg: var(--zd-code-bg);
  --color-code-fg: var(--zd-code-fg);
  --color-success: var(--zd-success);
  --color-danger: var(--zd-danger);
  --color-warning: var(--zd-warning);
  --color-info: var(--zd-info);
}

Once registered in @theme, these become standard Tailwind utility classes: bg-surface, text-accent, border-muted, etc.

Semantic Token Reference

TokenDefault Palette SlotUsage
bg--zd-bgPage background
fg--zd-fgPrimary text
surfacep0 (Black)Panel/sidebar surfaces
mutedp8 (Bright Black)Muted text, borders, comments
accentp6 (Cyan)Links, active states, CTA
accent-hoverp14 (Bright Cyan)Hover state for accent
sel-bg--zd-sel-bgSelection background
sel-fg--zd-sel-fgSelection foreground
code-bgp0 (Black)Code block background
code-fgp13 (Bright Magenta)Inline code text
successp2 (Green)Success states, confirmations
dangerp1 (Red)Errors, destructive actions
warningp3 (Yellow)Warning messages
infop4 (Blue)Informational highlights

Per-Scheme Semantic Overrides

Each color scheme can override the default palette-slot mapping via the semantic property in src/config/color-schemes.ts. For example, Dracula overrides muted to a custom gray, and Solarized Light remaps accent to magenta:

"Solarized Light": {
  // ...palette, background, foreground...
  semantic: {
    accent: "#d33682",     // magenta instead of default cyan
    accentHover: "#6c71c4",
    surface: "#f4efdd",
  },
},

The resolution logic lives in src/config/color-scheme-utils.ts — each semantic property falls back to its default palette slot when not explicitly overridden.

Tier 3: Component Tokens

Some components define their own color variables that consume Tier 2 semantic tokens. These are internal implementation details.

Content Typography

The .zd-content class provides direct element styling using semantic tokens — no external typography plugin:

.zd-content {
  color: var(--color-fg);
  font-size: var(--text-body);
  line-height: var(--leading-relaxed);
}

.zd-content :where(a) {
  color: var(--color-accent);
}

.zd-content :where(code:not(pre code)) {
  color: var(--color-code-fg);
  background-color: var(--color-code-bg);
}

.zd-content :where(li::marker) {
  color: var(--color-muted);
}
/* ... */

ℹ️ Info

Tier 3 tokens are internal to their components. When building your own UI, use Tier 2 semantic tokens or Tier 1 palette tokens directly.

Using Color Tokens

Prefer semantic tokens

Use semantic tokens for standard UI patterns:

<!-- Text -->
<p class="text-fg">Primary text</p>
<p class="text-muted">Secondary text</p>
<a class="text-accent hover:text-accent-hover">Link</a>

<!-- Backgrounds -->
<div class="bg-bg">Page background</div>
<div class="bg-surface">Panel or sidebar</div>

<!-- Borders -->
<div class="border border-muted">Bordered element</div>

Fall back to palette tokens when needed

Use p0p15 when no semantic token fits — for badges, decorative elements, or status indicators:

<span class="text-p1">Error text (red)</span>
<span class="text-p2">Success text (green)</span>
<span class="text-p3">Warning text (yellow)</span>
<span class="text-p4">Info text (blue)</span>

Color Schemes

To change the active color scheme, edit src/config/settings.ts:

export const settings = {
  colorScheme: "Dracula",  // Change this
  siteName: "zudo-doc",
  // ...
};

Default Themes

zudo-doc includes a light and dark default theme. See src/config/color-schemes.ts for available schemes.

Adding a Custom Color Scheme

Add a new entry to the colorSchemes object in src/config/color-schemes.ts:

"My Theme": {
  background: "#1a1a2e",
  foreground: "#e0e0e0",
  cursor: "#e0e0e0",
  selectionBg: "#3a3a5e",
  selectionFg: "#e0e0e0",
  palette: [
    "#16213e", "#e74c3c", "#2ecc71", "#f39c12",  // 0-3: Black, Red, Green, Yellow
    "#3498db", "#9b59b6", "#1abc9c", "#ecf0f1",  // 4-7: Blue, Magenta, Cyan, White
    "#2c3e50", "#e67e73", "#55d98d", "#f5b041",  // 8-11: Bright Black–Yellow
    "#5dade2", "#bb8fce", "#48c9b0", "#fdfefe",  // 12-15: Bright Blue–White
  ],
  shikiTheme: "one-dark-pro",
  // Optional: override semantic defaults
  semantic: {
    muted: "#7f8c8d",
    surface: "#1f1f3a",
  },
},

The ColorScheme interface requires:

  • background, foreground, cursor, selectionBg, selectionFg — base colors
  • palette — 16-element tuple (color slots 0–15)
  • shikiTheme — a Shiki theme name for syntax highlighting
  • semantic (optional) — override default palette-slot mappings for any semantic token

What NOT to Do

🚨 Color Anti-Patterns

Don’t use Tailwind defaults — they are reset to initial:

<!-- WRONG -->
<div class="bg-gray-800 text-blue-500">No visible color</div>

<!-- RIGHT -->
<div class="bg-surface text-accent">Works correctly</div>

Don’t hardcode hex values — it breaks theming:

<!-- WRONG -->
<div class="bg-[#1e1e2e]">Breaks on theme switch</div>

<!-- RIGHT -->
<div class="bg-p0">Adapts to any theme</div>

Don’t reference component-scoped variables in your own components:

/* WRONG — don't use hardcoded colors */
.my-component {
  color: #3b82f6;
}

/* RIGHT — use semantic tokens */
.my-component {
  color: var(--color-accent);
}