Ziteox Forge

Component Guidelines

Conventions for adding components to @ziteox/ui.

Component Guidelines

Conventions for adding and maintaining components in @ziteox/ui.

File placement

packages/ui/src/<domain>/
├── index.ts              # Domain barrel — public exports only
├── <component-name>.tsx  # kebab-case filename
└── <local-util>.ts       # Domain-specific utils (e.g. play-tick.ts)

Cross-domain utilities go in shared/. Motion presets go in animations/.

Do not create empty domain folders. Add a domain when the first component ships.

Naming

ItemConventionExample
React componentPascalCaseHamburgerButton
Filekebab-casehamburger-button.tsx
CSS classesziteox-<domain-or-component>-*ziteox-hamburger-button
CSS variables--ziteox-*--ziteox-theme-toggler-ink
SVG mask IDsziteox- prefix + useId()ziteox-attabc123

Public API rules

  1. Export components and prop types from domain index.ts
  2. Re-export from packages/ui/src/index.ts
  3. Add package.json subpath if new domain
  4. Document in docs/ui/<component>.md
  5. Do not export shared/ or animations/ unless deliberately making public (avoid)

Controlled components

Prefer controlled props for interactive state:

interface ExampleProps {
  value: boolean;
  onValueChange: (value: boolean) => void;
}

Parent owns state. Component emits changes. Avoid internal-only state that parent cannot observe for coordination (e.g. button + menu).

Exceptions:

  • Hydration guards (useMounted) — internal render strategy
  • Uncontrolled escape hatches when documented (AnimatedThemeToggler without isDark)

Client vs server

Use "use client" whenOmit when
useState, useEffect, hooksStatic markup only
Event handlersNo interactivity
Browser APIs on mountSSR-safe render
motion components

Styling

  • No Tailwind inside @ziteox/ui. Consumers may use Tailwind; pass via className.
  • Use scoped CSS in <style> tags or co-located constants (current pattern).
  • Prefix all classes with ziteox-.
  • Support dark mode via .dark and [data-theme="dark"] where semantic colors are used.

Icons and assets

  • Inline SVG in component source
  • No lucide-react, @heroicons, etc. in package dependencies
  • No image file imports unless explicitly required
  • Use <a href="..." target="_blank" rel="noopener noreferrer"> for external links
  • Do not import next/link in package source

Props design

  • Use explicit prop names (isOpen, onOpenChange) not generic open/setOpen unless consistent across domain
  • Export interface or type for props: ComponentNameProps
  • Sensible defaults on optional props
  • Document breaking prop changes

className prop

Include when apps need layout/responsive overrides:

className?: string;
// ...
className={[ROOT_CLASS, className].filter(Boolean).join(" ")}

Do not require Tailwind for component to function.

Accessibility

  • Interactive elements: aria-label, aria-expanded, type="button" as appropriate
  • Decorative SVG: aria-hidden
  • Screen-only text: ziteox-sr-only pattern
  • Respect prefers-reduced-motion for motion and transitions

Testing integration

  1. pnpm build from monorepo root
  2. Verify in examples/playground when applicable
  3. Check types export in dist/*.d.ts

Documentation requirement

Every new public component needs docs/ui/<name>.md with:

  • Import path
  • Props table
  • Usage example
  • Architecture notes (non-obvious decisions)