Principles
The product, platform, and accessibility decisions that define Simal UI.
Simal UI is a versioned design system, not a bag of copied components. Every
Simal product consumes the same tokens and public component contracts from
@simal/ui; platform-specific implementations may differ when native behavior
is better than visual sameness.
This differs from copy-paste collections such as NativeWindUI: those are useful source material, but Simal needs one owned package, one release history, and one place to fix an accessibility or token defect for every product.
What the system optimizes for
- Operational clarity. A dispatcher should know the next action and the current state without decoding decoration.
- Native behavior. iOS, Android, and web may use different implementations
behind one public contract. Platform splits are preferred over runtime
Platform.OSbranches. - Semantic tokens. Product code asks for
background,foreground,primary,warning, ordestructive; it does not own brand hex values. - Accessible defaults. Names, roles, states, focus, contrast, and 44-point targets belong in the shared component whenever the requirement is generic.
- Consumer independence. The package owns presentation, never routing, tenancy, domain state, storage, or network behavior.
The four layers
| Layer | Owns | Import |
|---|---|---|
| Foundations | Color, type, spacing conventions, motion, icon treatment | @simal/ui/tokens · CSS/token entries |
| Primitives | Composed Simal components and layout helpers | @simal/ui |
| NativeWindUI surface | Multi-platform controls with native treatment | @simal/ui/nativewindui |
| Product composition | Load cards, negotiation copy, role-specific workflows | The consuming product |
A component moves up only when its behavior is generic. TextField belongs
here; “Assign driver and truck” does not. Product code may compose shared
primitives, but it must not create a competing token set or a local src/ui.
Cross-platform does not mean identical
The public API and semantic meaning stay stable. Geometry and interaction may follow the platform:
- A date field may use a native picker on mobile and a calendar popover on web.
- A sheet may be a bottom sheet on touch devices and a dialog treatment on web.
- Typography and spacing remain token-driven even when the control chrome is platform-specific.
A platform difference is documented in the component story. An accidental difference is a defect.
Accessibility contract
Every interactive component is expected to provide:
- a stable accessible name;
- the correct role and state (
checked,selected,expanded,disabled, orbusywhen applicable); - a minimum 44 × 44 point target unless the platform provides a larger wrapper;
- keyboard and visible-focus behavior on web;
- readable light and dark contrast using semantic tokens;
- error text that is both visible and announced.
Storybook's accessibility panel is the workbench. Playwright baselines verify rendering. Product E2E remains responsible for proving that React Native Web actually forwards important ARIA state into the shipped DOM.
Decision rule
Before adding a local component, ask:
- Is it domain-free and repeated in more than one product or workflow?
- Can its API be named without mentioning a Simal business entity?
- Can Storybook demonstrate all meaningful states without app providers?
- Can the package own its accessibility behavior across supported platforms?
If all four are yes, propose it here. Otherwise keep it in the product and build it from shared primitives.