Ziteox Forge

Domain Boundaries

Domain ownership and import rules.

Domain Boundaries

Rules for organizing code in @ziteox/ui.

Principle

Group by product concern (domain), not by technical type. Each domain owns a cohesive set of UI and exports through its own barrel.

Active domains

DomainPathOwnsPublic exports
themesrc/theme/Theme switching, provider, toggle UIThemeProvider, ThemeToggle, AnimatedThemeToggler
navigationsrc/navigation/Mobile nav triggers, menus (future)HamburgerButton
brandingsrc/branding/Attribution, logo usageBrandCredit, BRAND_CREDIT_VARIANTS

Planned domains (not yet implemented)

DomainWill ownWill not own
buttonsPressable primitives, variantsMenu overlays, nav-specific icons
feedbackToast, loading indicatorsNavigation, theme

Add the folder when the first component in that domain is implemented.

Ownership rules

theme

  • next-themes integration
  • Dark/light toggle UI
  • Theme-related audio (play-tick.ts)

Does not own: navigation, generic buttons, footer attribution.

  • Hamburger / menu triggers
  • Mobile menu sheets and overlays (future)
  • Nav-specific composition

Does not own: generic Button variants, theme provider, brand credit.

HamburgerButton is navigation-specific despite being button-shaped. It lives here because it coordinates with menu open state and aria-expanded.

branding

  • Ziteox attribution lines
  • Logo presentation in attribution context

Does not own: product navigation, theme, marketing page content.

Cross-domain imports

domain component  →  shared/     ✅
domain component  →  animations/ ✅
domain A          →  domain B    ⚠️  only for deliberate composition exports
shared/           →  domain/     ❌
animations/       →  domain/     ❌

Composition example (future): a MobileNav export in navigation/ might render HamburgerButton + menu sheet together. That stays in navigation/index.ts, not split across domains.

Internal modules

ModuleRoleImported by
shared/hooks/Reusable React hooksAny domain
animations/motion presetsDomains using motion

Never export internal modules from domain or root barrels unless promoting to public API (rare; requires docs update).

Adding a new domain

  1. Confirm it does not fit an existing domain
  2. Create packages/ui/src/<domain>/index.ts
  3. Add first component
  4. Export from root index.ts
  5. Add "./<domain>" to package.json exports
  6. Document in docs/overview.md and docs/standards/domain-boundaries.md
  7. Add docs/ui/<component>.md

Adding to an existing domain

  1. Verify component belongs per table above
  2. Add file under domain folder
  3. Export from domain index.ts and root index.ts
  4. Document in docs/ui/

Anti-patterns

  • components/ flat folder with mixed concerns
  • Empty domain scaffolding
  • Importing navigation/ from branding/
  • Putting menu overlay logic inside HamburgerButton
  • Putting theme persistence inside navigation components
  • Exporting shared/hooks for app convenience (apps should copy pattern or request promotion)

Variant maps vs domains

Small variant data (e.g. BRAND_CREDIT_VARIANTS) stays in the domain:

branding/
  variants.ts
  brand-credit.tsx

Do not create a separate domain for configuration maps.

AI agent instructions

When asked to add a component:

  1. Read this file
  2. Pick exactly one domain
  3. If ambiguous (e.g. icon button vs nav trigger), choose by primary user intent (menu trigger → navigation)
  4. Do not create new domains without explicit scope
  5. Update this document when a new domain is added