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
| Item | Convention | Example |
|---|---|---|
| React component | PascalCase | HamburgerButton |
| File | kebab-case | hamburger-button.tsx |
| CSS classes | ziteox-<domain-or-component>-* | ziteox-hamburger-button |
| CSS variables | --ziteox-* | --ziteox-theme-toggler-ink |
| SVG mask IDs | ziteox- prefix + useId() | ziteox-attabc123 |
Public API rules
- Export components and prop types from domain
index.ts - Re-export from
packages/ui/src/index.ts - Add
package.jsonsubpath if new domain - Document in
docs/ui/<component>.md - Do not export
shared/oranimations/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 (
AnimatedThemeTogglerwithoutisDark)
Client vs server
Use "use client" when | Omit when |
|---|---|
useState, useEffect, hooks | Static markup only |
| Event handlers | No interactivity |
| Browser APIs on mount | SSR-safe render |
motion components | — |
Styling
- No Tailwind inside
@ziteox/ui. Consumers may use Tailwind; pass viaclassName. - Use scoped CSS in
<style>tags or co-located constants (current pattern). - Prefix all classes with
ziteox-. - Support dark mode via
.darkand[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
Links
- Use
<a href="..." target="_blank" rel="noopener noreferrer">for external links - Do not import
next/linkin package source
Props design
- Use explicit prop names (
isOpen,onOpenChange) not genericopen/setOpenunless consistent across domain - Export
interfaceortypefor 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-onlypattern - Respect
prefers-reduced-motionfor motion and transitions
Testing integration
pnpm buildfrom monorepo root- Verify in
examples/playgroundwhen applicable - 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)