Color Rooms
A token-only overlay on top of the Urbicon UI library that drives the look of this docs site — Schibsted Grotesk on warm cream paper, with the accent set to the room (section) you are in. Activated via a single root class; everything else is CSS custom properties.
What it is
Color Rooms is the visual identity of this docs site — one grotesk (Schibsted Grotesk) for
both display and body, JetBrains Mono for meta and code, on warm cream paper. Its signature is
that the accent is the room you are in: each product area owns a saturated
colour, and the component-page and section-landing headers become a full-width colour field
that spans everything right of the app sidebar. It is implemented as a single CSS file (apps/docs/src/lib/style/rooms-docs.css) that defines a private --docs-* token namespace and
re-derives the library's primary-token family from the active room when .docs-rooms is present on the <html> root.
Nothing in the library has been forked or duplicated. Every primitive still ships with its
library-default look; Color Rooms is a thin token-override sheet over the top. Without .docs-rooms the same page renders in the library
defaults — that's the test of whether the docs site really is theme-only. The Docs theme toggle in the sidebar footer (Rooms / Library) removes the class live
to prove it.
Color Rooms vs. Consumer Apps
Color Rooms is docs-only. Consumer apps that depend on @urbicon-ui/blocks do not import rooms-docs.css and do not set .docs-rooms; they live on the library defaults and
customise via the regular customization ladder (class / slotClasses / preset / defaults / overrides / unstyled).
The Rooms
Four rooms, one per product area. The top-level route segment picks the room; everything
outside the four areas falls back to the blocks green. The two custom properties (--room-accent and its foreground --room-accent-fg) are fed in from the layout onto a .docs-room-scope wrapper, and the whole primary-token
family is re-derived from them via color-mix().
Switching route therefore repaints every real component on the page — segment indicator,
slider, toggle, buttons, TOC-active, badges — with no per-component override.
| Room | Route | Accent · Foreground |
|---|---|---|
| Blocks (default) | /blocks/** | #00845c · #f6f3ec — green |
| Table | /table/** | #7c1f2d · #f6f3ec — wine |
| Auth | /auth/** | #e3a31c · #17150f — amber |
| AI & DX | /ai/** | #e8500f · #17150f — orange |
Route → room
The derivation is re-declared in two scopes on purpose. Content reads the room from the .docs-room-scope wrapper — SSR-correct, so the first
paint already carries the right accent with no flash. Portaled popovers (the Select / Combobox
/ Menu dropdowns) mount at <body>, outside that
wrapper, so the same accent is mirrored onto <html> after mount — they only open on interaction, always post-hydration, so there is no first-paint concern
for them.
color-mix derivation
A scoped theme has to re-declare its derived semantic tokens: a var() inside a :root token definition substitutes at the cascade level where it is defined, so overriding the ramp alone
won't re-resolve --color-primary & co. — see Tier System.
Token Catalogue
The --docs-* namespace holds the paper/ink hierarchy;
the primary family is derived from the room (above). Color Rooms also binds a handful of
library semantic tokens to the --docs-* values so
components rendered inside .docs-rooms pick up the warm palette
without per-component opt-in.
Docs-private tokens
| Token | Default (light · dark) | Used for |
|---|---|---|
--room-accent | per section | The active room colour — source of the whole primary family. |
--room-accent-fg | per section | On-accent ink/cream — text on the field + on primary fills. |
--docs-bg | #f7f5f0 · #1a1816 | Ground — the page surface outside content blocks. |
--docs-paper | #fbfaf6 · #232220 | Content surface — where library components sit. |
--docs-ink | #17150f · #f0ede5 | Primary text ink. |
--docs-soft | #6e6b64 · #a5a299 | Body-soft / meta ink (ON THIS PAGE labels, descriptions). |
--docs-softer | #b8b5ad · #5a574f | Decoration ink — kicker separators. |
--docs-hair | ink/8% · cream/8% | Hairline — barely-visible structural lines. |
--docs-accent | var(--color-primary) | Link colour, section markers — couples to the room primary. |
--docs-radius-card | var(--radius-contain) | Bento-cards, recipe-stages — tight for the hard-edge poster. |
--docs-shadow-page | var(--blocks-shadow-lg) | Lift shadow — bento, recipe, showcase stages. |
--font-display / --font-sans | Schibsted Grotesk Variable | Display + body — one grotesk (self-hosted via @fontsource). |
--font-mono | JetBrains Mono · system mono | Code, kbd, meta kickers (self-hosted via @fontsource). |
Library semantic overrides
Color Rooms rebinds these library semantic tokens inside .docs-rooms so library components automatically pick up the
warm palette and the room accent. The surface ladder, warm-neutral border/state ramp and warm-tuned
intent colours (secondary/success/warning/danger) are re-pointed too so the whole page reads warm
rather than cool.
| Library token | Color Rooms value |
|---|---|
--color-primary | var(--room-accent) |
--color-text-on-primary | var(--room-accent-fg) |
--color-surface-base | var(--docs-paper) |
--color-surface-quiet | var(--docs-bg) |
--color-border-hairline | var(--docs-hair) |
--color-text-primary | var(--docs-ink) |
--color-text-secondary | var(--docs-soft) |
Activation
The theme activates when .docs-rooms sits on a parent
of your content. The docs site puts it on <html> as the shipped default so the whole app inherits the warm canvas — and so the app.html head script can flip it before first paint
(the root element exists there, <body> does not
yet). The per-route room accent then lives on the .docs-room-scope wrapper in the layout.
1. Set the root class
And import the stylesheet alongside the library base:
2. Import rooms-docs.css
Remove the class (the sidebar's Docs theme → Library toggle does exactly this) and the page falls back to the bare library skin — no field header, no Schibsted, the library's blue primary. The underlying override mechanism is the same CSS-custom-property cascade documented in Tier System — Color Rooms just rebinds a wider set of tokens and derives them from the room.
Light & Dark
Color Rooms supports both light and dark out of the box — Light is warm cream paper with warm dark ink, Dark is warm coffee paper with warm cream ink. The room accent is orthogonal to the mode: it repaints the primary family, not the paper, so a section stays the same colour in both modes and the docs' ThemeSwitcher keeps working.
Implementation uses CSS's light-dark() function. Each
paper/ink token resolves at use-site based on the page's color-scheme, which the library's semantic layer sets
via :root.light / :root.dark. A single rule block covers both modes — no
second sheet, no JS-driven swap.
Light/Dark via light-dark()
The same mechanism powers semantic.css — see Design Tokens for the library-side details.
Override Recipes
Because every value lives on a CSS custom property, you can adjust the theme at any scope —
globally inside .docs-rooms, per-room on .docs-room-scope, or inline. A few common adjustments:
Common overrides
For brand-wide theming (the library's own primary colour, the tier radii) reach for the Theme Builder or write a custom @theme block — Color Rooms sits on top of that and, per room, overrides the primary chain with the room accent.