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
| Domain | Path | Owns | Public exports |
|---|---|---|---|
| theme | src/theme/ | Theme switching, provider, toggle UI | ThemeProvider, ThemeToggle, AnimatedThemeToggler |
| navigation | src/navigation/ | Mobile nav triggers, menus (future) | HamburgerButton |
| branding | src/branding/ | Attribution, logo usage | BrandCredit, BRAND_CREDIT_VARIANTS |
Planned domains (not yet implemented)
| Domain | Will own | Will not own |
|---|---|---|
| buttons | Pressable primitives, variants | Menu overlays, nav-specific icons |
| feedback | Toast, loading indicators | Navigation, theme |
Add the folder when the first component in that domain is implemented.
Ownership rules
theme
next-themesintegration- Dark/light toggle UI
- Theme-related audio (
play-tick.ts)
Does not own: navigation, generic buttons, footer attribution.
navigation
- 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
| Module | Role | Imported by |
|---|---|---|
shared/hooks/ | Reusable React hooks | Any domain |
animations/ | motion presets | Domains using motion |
Never export internal modules from domain or root barrels unless promoting to public API (rare; requires docs update).
Adding a new domain
- Confirm it does not fit an existing domain
- Create
packages/ui/src/<domain>/index.ts - Add first component
- Export from root
index.ts - Add
"./<domain>"topackage.jsonexports - Document in
docs/overview.mdanddocs/standards/domain-boundaries.md - Add
docs/ui/<component>.md
Adding to an existing domain
- Verify component belongs per table above
- Add file under domain folder
- Export from domain
index.tsand rootindex.ts - Document in
docs/ui/
Anti-patterns
components/flat folder with mixed concerns- Empty domain scaffolding
- Importing
navigation/frombranding/ - Putting menu overlay logic inside
HamburgerButton - Putting theme persistence inside navigation components
- Exporting
shared/hooksfor 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.tsxDo not create a separate domain for configuration maps.
AI agent instructions
When asked to add a component:
- Read this file
- Pick exactly one domain
- If ambiguous (e.g. icon button vs nav trigger), choose by primary user intent (menu trigger → navigation)
- Do not create new domains without explicit scope
- Update this document when a new domain is added