Skip to content

fix(hub-ui,json-render-ui): rewire baked Wind3 colors so branding's primaryColor actually retints the UI - #203

Merged
antfu merged 2 commits into
mainfrom
social-paths-type
Aug 14, 2026
Merged

fix(hub-ui,json-render-ui): rewire baked Wind3 colors so branding's primaryColor actually retints the UI#203
antfu merged 2 commits into
mainfrom
social-paths-type

Conversation

@antfubot

@antfubot antfubot commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Problem

Branding's primaryColor (--devframe-primary) didn't affect the actual dock UI — hover states, settings panels, buttons, etc. all stayed the default sage green no matter what was configured.

Root cause

@devframes/hub-ui's dock and @devframes/json-render-ui's renderer both compile their shadow-root utility CSS with Wind3 (Wind4's --un-*/:root theme mechanism doesn't reach a shadow tree). Wind3 resolves theme colors to literal baked rgb(r g b / alpha) triplets at build time rather than referencing CSS variables. primary-ramp.css already derived --colors-primary-* from --devframe-primary, but nothing in the compiled Wind3 output ever read those variables — every text-primary/bg-primary/btn-primary/ring-primary-* utility (i.e. the whole branded surface) was permanently hardcoded to the default green.

Fix

Added rewireBakedPrimaryColors() in design/uno.config.ts, run in both packages' scripts/build-css.ts right after generator.generate(...). It rewrites each baked primary-ramp triplet into CSS relative-color syntax:

/* before */
background-color: rgb(87 136 97 / var(--un-hub-bg-opacity));
/* after */
background-color: rgb(from var(--colors-primary-500, #578861) r g b / var(--un-hub-bg-opacity));

This keeps Wind3's existing opacity mechanics (slash modifiers, --un-*-opacity vars) intact while sourcing the base color from the live variable primary-ramp.css already derives from --devframe-primary. Also corrected both primary-ramp.css files' comments, which inaccurately described a Wind4-only derivation that never actually applied to these Wind3-compiled shadow roots.

Verified end-to-end in Chromium that overriding --devframe-primary on the shadow host now retints text-primary, bg-primary, and btn-primary (default, hover, and focus-ring states). Regenerated both packages' .generated/css.ts build artifacts.

Dogfooding the fix in the examples

Each hub-*-minimal example (Vite, Next.js, Nitro, Rsbuild, Hono) already calls createUi() with no branding, so all five rendered the identical default sage-green dock. Passed branding.primaryColor (+ a matching productName) to createUi() in each, so the dock now visibly reflects the host it's running on:

Example primaryColor Source
hub-vite-minimal #646cff (Vite purple) vite.dev's <meta name=theme-color>
hub-next-minimal #000000 (Next.js/Vercel monochrome) Next/Vercel's official brand has no signature hue
hub-nitro-minimal #ff2056 (Nitro pink/red) nitro.build's computed --ui-primary + hero screenshot
hub-rsbuild-minimal #ff5e00 (Rsbuild orange) rsbuild.rs's computed --rp-c-brand
hub-hono-minimal #e36002 (Hono orange) hono.dev's computed --vp-c-brand-1

hub-vite and hub-next (the two hand-rolled-UI reference hosts) are intentionally left on the shared devframe design system, per this repo's parity/one-product principle for those two.

Verified end-to-end in Chromium (hub-vite-minimal, hub-nitro-minimal) that branding.json is served and --devframe-primary resolves to the configured color inside the dock's shadow root.

pnpm lint, pnpm knip, pnpm typecheck, pnpm build (full monorepo), and the full vitest suite (1105 tests) all pass.


This PR was created with the help of an agent.

…rimaryColor actually retints the UI

Both shadow-root packages compile their utility CSS with Wind3, which bakes
theme colors to literal rgb() triplets at build time instead of referencing
CSS variables. --devframe-primary (branding's primaryColor) was only ever
consumed by the unused --colors-primary-* variables in primary-ramp.css, so
overriding it never reached any actual primary/bg-primary/btn-primary utility
- the dock, its hover states, and the settings panels stayed the default
sage green regardless of branding.

Post-process the compiled Wind3 CSS to rewrite each baked primary-ramp
triplet into CSS relative-color syntax (rgb(from var(--colors-primary-<stop>,
<hex>) r g b / <alpha>)), preserving Wind3's existing opacity mechanics while
sourcing the base color from the live variable. Verified end-to-end in
Chromium that overriding --devframe-primary now retints text-primary,
bg-primary, and btn-primary (including its hover/focus-ring states).
@netlify

netlify Bot commented Aug 14, 2026

Copy link
Copy Markdown

Deploy Preview for devfra ready!

Name Link
🔨 Latest commit 9c91b6e
🔍 Latest deploy log https://app.netlify.com/projects/devfra/deploys/6a7e72f1c756d200086344af
😎 Deploy Preview https://deploy-preview-203--devfra.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

…ork's color

Each minimal example (Vite, Next.js, Nitro, Rsbuild, Hono) already calls
createUi() with no branding, so all five render the identical default sage
green dock. Pass branding.primaryColor (and a matching productName) to
createUi() in each so the dock visibly reflects the host it's running
on - Vite's purple (#646cff), Next.js/Vercel's monochrome black (#000000),
Nitro's pink/red (#ff2056), Rsbuild's orange (#ff5e00), and Hono's orange
(#e36002). Colors verified against each project's live site (theme-color
meta / computed CSS custom properties).

hub-vite and hub-next (the two hand-rolled-UI reference hosts) are left on
the shared devframe design system, per this repo's parity/one-product design
principle for those two.

Verified end to end in Chromium (hub-vite-minimal, hub-nitro-minimal) that
branding.json is served and --devframe-primary resolves to the configured
color inside the dock's shadow root.
@antfu
antfu merged commit 2fb9bf9 into main Aug 14, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants