zudo-doc
GitHub repository

Type to search...

to open search from anywhere

Component First Strategy

Created Mar 16, 2026Updated Oct 9, 2026Takeshi Takatsudo

Why zudo-doc uses components with utility classes instead of custom CSS class names.

The Rule

The component is the UI abstraction. Compose zudo-react .tsx components with the supported zudo-wind utilities and semantic design tokens. Use props for variants. For a feature the utility compiler does not support, add narrowly scoped authored CSS with a zd- name and register its authored-class coverage; do not invent a utility spelling and assume it emits CSS.

In zudo-doc

Server components return Child/Description and produce HTML without client JavaScript. Client components add "use client" and use scope-owned signals. Both use class and the JSX source @takazudo/zfb/zudo-react, not React/Preact node types.

Server-rendered components

src/components/my-badge.tsx
/** @jsxRuntime automatic */
/** @jsxImportSource @takazudo/zfb/zudo-react */
import type { Child } from "@takazudo/zfb/zudo-react";

export function MyBadge({ children }: { children?: Child }) {
  return (
    <span class="rounded-full bg-surface px-hsp-sm py-vsp-2xs text-caption text-fg">
      {children}
    </span>
  );
}

Client-hydrated islands

Use a fixed-target server boundary and serializable props. Follow Custom Components for scanner reachability and lifecycle requirements.

src/components/counter.tsx
/** @jsxRuntime automatic */
/** @jsxImportSource @takazudo/zfb/zudo-react */
"use client";

import { computed, getScope, signal } from "@takazudo/zfb/zudo-react";

export default function Counter() {
  const scope = getScope();
  const count = signal(0);
  const label = computed(() => `Count: ${count.value}`);

  scope.onActivate(() => {
    // Browser-only setup belongs here. Return cleanup when needed.
  });

  return (
    <button
      type="button"
      class="rounded-full bg-surface px-hsp-sm py-vsp-2xs text-caption text-fg"
      on:click={() => { count.value += 1; }}
    >
      {label}
    </button>
  );
}

Component Variants via Props

Keep variants typed. A static class lookup inside a presentational component is sufficient; a signal-dependent variant needs computed so it updates after setup.

Component Composition

Compose small components. For changing lists use keyed For; ordinary array mapping is appropriate for fixed server content. A retained Show(true) child does not reset its local state on unrelated value changes. Keep structural boundaries outside restricted table parents.

Using zudo-doc's Design Tokens

Use text-fg, bg-surface, p-hsp-md, and text-small. Defining a CSS custom property changes an existing token value. Adding a new utility name also requires wind.tokens configuration. See Design System and Color.

When Custom CSS Is Acceptable

The package owns .zd-content typography, reset patches, and component seams. Projects import public stylesheets rather than copying them. Put custom-property overrides in :root after the package imports. Use scoped authored CSS for unsupported utility families or explicit component details, and validate cascade winners: generated utilities are unlayered and follow authored CSS, so layers alone cannot put an authored override after them. Prefer a component-scoped selector over global !important.

Rules Summary

  1. Compose components and use typed variant props.

  2. Use supported utilities with semantic tokens.

  3. Use scoped authored CSS where required by the compiler contract.

  4. Keep state and browser cleanup within the owning scope.

  5. Check generated CSS and real interactions after customization.

Revision History

Takeshi TakatsudoCreated: 2026-03-17T01:50:11+09:00Updated: 2026-10-09T17:04:38Z

AI Assistant

Ask a question about the documentation.

Preview theme

Loading theme previews…