Skip to main content
LAW 1: definitions own ZERO style values. Token names only. The SDK renderer owns no styles either β€” it only resolves token names against the theme served live from the platform (unoverse://theme/<name> β€” an MCP resource, never baked into a bundle). A color change is a refresh, not a release.

🚫 The law

  • No px / rem / em / #hex anywhere in any component, atom, or template definition. Studio’s publish lint scans for exactly this and blocks (08).
  • Sizes use the space scale ("width": "8" = 2rem, Tailwind-style: step N = N Γ— 0.25rem) β€” and only real steps: 0 1 1.5 2 3 4 5 6 7 8 10 12 16 20 24 28 40 50 75 90 100 120 140 160 180 200 (+ full/auto). An invented step ("26", "3.5") is NOT rounded β€” it falls through as broken CSS and the element silently reverts to auto sizing. Studio’s publish lint rejects off-scale values.
  • ❌ No invented component-named tokens (cardMin, wizardWidth) β€” use the generic scale steps. If the scale genuinely lacks a step, extend the scale in styles/, don’t smuggle a value into a definition.

πŸ”’ Style KEYS are closed too β€” the cross-platform contract

It’s not just values: the set of style keys (padding, direction, radius, hover, …) is a closed vocabulary β€” exactly the neutral intents the SDK style interpreter maps, and the contract every renderer (web today; iOS, Android, React Native, Flutter as they land) implements. An unknown key is ignored by every renderer, so it is always a typo (colour) or a web-ism that would never port (backdropFilter, gridTemplateColumns). Both the schema (editor squiggle) and Studio’s publish lint (error) enforce the key set, including inside hover/active and when[].apply. If a design genuinely needs an intent the vocabulary lacks, that’s a platform conversation (a new key every renderer must implement) β€” never a definition-side workaround. Why so strict: brand and dark-mode swaps must touch styles/ only. One raw hex in one definition breaks that guarantee for the whole org.

πŸ—‚οΈ The token layers

Definitions speak semantic. Themes remap semantic β†’ base per brand or mode; a theme-contract guard keeps token names consistent so every theme satisfies every definition.

Standard app sizes (semantic/app-sizes.json)

The named width blocks a template’s appWidth can reference (05 β€” Sizing). The starter set: Every size carries a viewport ceiling (min(100vw, …)): the full designed width on desktop, never wider than the screen on mobile. (Panels side-by-side can still exceed a phone’s width together β€” the dedicated mobile layout pass will decide stacking/overlay behavior; the ceiling keeps each panel individually safe until then.) Names are optional β€” raw CSS in appWidth is always valid β€” but prefer them: every template in the org stays on the same scale, and retuning a size is one edit here. Add org-specific names freely (the linter validates that any name a template uses is declared). Unlike every other token home these values are raw host-facing CSS on purpose: they size the app’s outer panel, which the embedding page applies β€” they are never inner styles.

🏒 Org scoping

  • Components live in TWO tiers. The design system (the installed marketplace package β€” generic, universal: cards, charts, media) is shared by every org and references token names only. An org component (rx/<project>/components/ β€” the client’s own microapp: their finder, their page) is org-private: it belongs to that client and is served under their org. Names are unique across all tiers (lint-enforced), so a bare reference is unambiguous.
  • Atoms are universal and authoring-time only β€” rx/atoms/, one copy; the server expands every Ref before serving (atoms are never served or enumerable).
  • Templates and styles are org-scoped β€” rx/<project>/. Each org gets its own complete token set and templates. There are no overlays: if two orgs need different looks from the same universal component, that difference is 100% in their styles/. A component only becomes an org component when it IS the client’s product, not to restyle a shared one.
Starting a new org: copy the neutral baseline and re-token it β€”
(Every project starts from the same default set, so rebranding is editing values, never inventing structure.)

πŸ“‹ Styling checklist

  • Zero raw values in any definition (linter enforces)
  • Only semantic token names referenced (text.primary, not a palette entry)
  • No component-named tokens invented
  • New brand/mode = a new themes/ file, zero definition edits
  • Theme keeps the full token contract (guard test)

Next: 07 β€” Studio β€” see it, test it, on every channel.