Skip to main content
This page documents why the system was built the way it is — not just what exists today, but the alternatives considered and the reasoning behind each choice. Useful to revisit decisions when the context changes.

Base library: why Lit?

~5 KB, declarative reactivity, maintained by Google, no proprietary compiler — it’s a thin layer over the platform standard. Easy to hire for and learn. If Lit disappeared tomorrow, the components would still be valid custom elements.
Lit is the middle ground: productivity without leaving the platform standard.

Design tokens: Style Dictionary as the source of truth

Tokens are authored once as plain JSON (src/colors.json, src/size.json, src/font.json) and compiled by Style Dictionary into CSS, SCSS, JS, TS, and JSON — one source feeding every consumption style (CSS custom properties, SCSS maps, typed JS/TS constants, or a flat JSON for APIs/config).
This is a stronger starting point than a single hand-written tokens.css: adding a new output platform (e.g. a future React Native or mobile target) means adding a new Style Dictionary platform entry in config.json, not hand-maintaining a parallel file.

CSS separated from behavior, per component

Each component’s style lives in its own style.css, imported with the ?inline Vite suffix and applied via Lit’s unsafeCSS() — instead of a css tagged-template string embedded inside the component’s .ts file.
Keeping style in a real .css file means editor tooling (syntax highlighting, linting, autocomplete) works normally, and a change to visual design never touches the behavior file’s diff.

Unit tests: real browser instead of a simulated DOM

Vitest runs unit tests in real Chromium via Playwright, not jsdom. Web components depend on platform APIs (Shadow DOM, custom elements) that DOM simulators implement incompletely — testing in a real browser removes a whole class of false positives/negatives.
Storybook’s addon-vitest (which turns story play functions into interaction tests) is not wired in yet: as of this writing it assumes Vitest 4’s @vitest/browser-playwright architecture, and Vitest 4 is still in beta. Revisit once Vitest 4 stabilizes.

Validation: an exported utility, not a Lit dependency

@root-axis/design-system exports Zod schemas (nameSchema, emailSchema, passwordSchema, createPasswordSchema) and a validationErrorMessage(schema, value) helper — but no component imports Zod.
The validation rule lives in one place so it isn’t duplicated (and doesn’t drift) across React, Next, Vue, Solid, and Svelte consumers, but the design system itself stays framework- and validation-library-agnostic. A consuming app calls schema.safeParse(value) (or validationErrorMessage) and passes the resulting message into whichever error-display prop a future input component exposes.

Versioning and distribution

  • Each repository (design-system, design-tokens, docs, examples) is versioned and published independently — there’s no unified monorepo today, so design-system and design-tokens can be on different release cadences.
  • Published to the public npm registry under the @root-axis scope — these are open-source packages, not internal-only.