Simal UI

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

  1. Operational clarity. A dispatcher should know the next action and the current state without decoding decoration.
  2. Native behavior. iOS, Android, and web may use different implementations behind one public contract. Platform splits are preferred over runtime Platform.OS branches.
  3. Semantic tokens. Product code asks for background, foreground, primary, warning, or destructive; it does not own brand hex values.
  4. Accessible defaults. Names, roles, states, focus, contrast, and 44-point targets belong in the shared component whenever the requirement is generic.
  5. Consumer independence. The package owns presentation, never routing, tenancy, domain state, storage, or network behavior.

The four layers

LayerOwnsImport
FoundationsColor, type, spacing conventions, motion, icon treatment@simal/ui/tokens · CSS/token entries
PrimitivesComposed Simal components and layout helpers@simal/ui
NativeWindUI surfaceMulti-platform controls with native treatment@simal/ui/nativewindui
Product compositionLoad cards, negotiation copy, role-specific workflowsThe 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, or busy when 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:

  1. Is it domain-free and repeated in more than one product or workflow?
  2. Can its API be named without mentioning a Simal business entity?
  3. Can Storybook demonstrate all meaningful states without app providers?
  4. 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.

On this page