Architecture decisions

Progressive compiler

motif has a compiler, but it is never required. The runtime resolves every style on its own; the compiler, when installed, moves the work it can prove static from render time to build time.

Updated 4 days agoEdit on GitHubWeb & native

Status

Accepted.

Context

A styling library with a build step has to decide how load-bearing that step is. A mandatory compiler buys the best performance but taxes every consumer: the library does not work in a REPL, a test, or a bundler the compiler does not support. An optional compiler that produces different output from the runtime is worse — now two code paths can disagree, and a bug appears only with the plugin on, or only with it off.

Decision

motif takes a progressive compiler, designed in from day one.

  • The runtime path is complete. A motif app with no build plugin renders correctly — every style prop, every variant, every responsive object resolves at render time.
  • The compiler is an optimisation. Install the Babel, SWC, or Metro plugin and it statically extracts the style bags it can prove have no runtime dependency, emitting CSS rules (web) or hoisted StyleSheet entries (native) into the build output.
  • Both paths produce identical output. The compiler shares its resolver with the runtime, so an extracted class name is byte-identical to what the runtime would have emitted. A half-compiled codebase deduplicates correctly.

The identical-output property is not a hope — it is enforced by a differential test that runs the same cases through both paths and asserts they match.

Consequences

  • motif works everywhere React works, with no build configuration. The compiler is something a team adds when they want it, not a precondition for using the library.
  • Adding the compiler to an existing app is safe by construction — output does not change, only when it is computed. There is no behavioural migration.
  • The compiler can only ever be conservative. It extracts a style bag only when it can prove the bag is fully static; anything it cannot prove falls through to the runtime. "Extracts less than it theoretically could" is the correct failure mode, and the accepted cost of the guarantee.