@daopk/tokens
v0.1.0-rc.1
Published
Design tokens for the daopk web OS: the framework-agnostic style contract every micro-frontend consumes.
Readme
@daopk/tokens
The style contract every daopk micro-frontend consumes. CSS custom properties only — no framework, no components — which is what lets an app written in any framework look native to the OS.
Why daopk owns --dk-* instead of using ropav's --rp-*
--rp-* is the public surface of a component library published for other
people. Adopting it as the OS style contract would tie every
independently-deployed app's appearance to ropav's release cadence: an app
shipped six months ago would break when ropav renamed a semantic token, at a
moment daopk does not control. That is the same coupling this project exists to
remove — just moved from JS down into CSS.
Owning --dk-* gives the style contract its own version and its own ratchet
(umberkit check, umberkit diff), which is what makes it a contract rather
than a coincidence of variable names.
How the ropav bridge stays cheap
umberkit.config.ts uses ropav's seed palettes verbatim. Identical seeds
mean umberkit derives identical token IDs and values on both sides, so
scripts/build-ropav-bridge.mjs can generate the entire mapping:
--rp-color-blue-9: var(--dk-color-blue-9);355 aliases, generated. Only the primitive tier is bridged — ropav's semantic and component tiers are defined in terms of its own primitives, so they re-resolve on top of the aliases with no mapping at all.
Rebranding is therefore a seed change here, and the bridge follows automatically.
Deliberately unbridged
37 ropav primitives have no daopk counterpart; the bridge script names them all
on every run, so drift is visible rather than silent. Almost all are naming
divergence, not gaps — line-height.md vs line-height.normal,
transition.fast vs motion.duration.fast, font.weight.normal vs
font.weight.regular, plus ropav's heading.h1…h6 and size.control.* scales.
They stay unbridged on purpose. Renaming the OS's scales to match a component library's vocabulary is exactly the dependency direction this package exists to avoid. ropav keeps its own values for these; nothing breaks.
Layer order is load-bearing
src/layers.css must be the first, blocking stylesheet in the document —
a <link> or inline <style>, never JS-injected. A layer's position is fixed
by its first mention, so if an app's CSS parses first, the app defines the order.
This one statement is what makes Module Federation's nondeterministic chunk
order irrelevant to styling. daopk.tokens sits after ropav.tokens precisely
so the bridge wins; swapping them silently disables the whole package.
Scripts
pnpm tokens:generate # palettes → tokens/generated/{light,dark}.tokens.json
pnpm tokens:emit # DTCG → src/generated/tokens.{css,ts} + tokens/manifest.json
pnpm tokens:bridge # ropav/styles-manifest + our manifest → ropav-bridge.css
pnpm tokens:check # lint + contract ratchet
pnpm build # all of the abovetokens/ (DTCG sources + manifest.json) is committed — the manifest is what
the ratchet diffs against. src/generated/ is not.
