Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion design/build-shadow-css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export interface BuildShadowCssOptions {
/**
* Absolute path to the primary-ramp override stylesheet, appended AFTER
* the UnoCSS output so its `:host`/`:root, :host` block wins over Wind's
* own primary declarations (see each package's `primary-ramp.css`).
* own primary declarations (the shared `design/primary-ramp.css`).
*/
primaryRampPath: string
/**
Expand Down
35 changes: 35 additions & 0 deletions design/panel-theme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { DevframeBranding } from '../packages/hub-ui/src/types'
import { setupDevframeConnection } from 'devframe/client'

/** Apply startup branding to a built-in devframe's iframe SPA. */
export function applyPanelBranding(): () => void {
if (window.parent === window)
return () => {}

const root = document.documentElement
const previous = root.style.getPropertyValue('--devframe-primary')
const priority = root.style.getPropertyPriority('--devframe-primary')
let disposed = false
let applied = false

void setupDevframeConnection().then(({ connectionMeta }) => {
const configs = connectionMeta.configs as { ui?: { branding?: DevframeBranding } } | undefined
const color = configs?.ui?.branding?.primaryColor
if (disposed || !color || !CSS.supports('color', color))
return
root.style.setProperty('--devframe-primary', color)
applied = true
}).catch(() => {
// Connection failures leave the SPA's default palette intact.
})

return () => {
disposed = true
if (!applied)
return
if (previous)
root.style.setProperty('--devframe-primary', previous, priority)
else
root.style.removeProperty('--devframe-primary')
}
}
Original file line number Diff line number Diff line change
@@ -1,38 +1,43 @@
/*
* Primary color as a single overridable variable: `--devframe-primary`.
*
* Appended AFTER the UnoCSS output (see `scripts/build-css.ts`) so this block
* wins over Wind3's own `:root, :host` primary declarations, and imported after
* `virtual:uno.css` in the Storybook preview for the same reason.
* Primary color as a single overridable variable: `--devframe-primary`. Shared
* by every devframe surface: appended after each shadow-root build's UnoCSS
* output (the hub-ui dock, the json-render renderer module; see
* `build-shadow-css.ts`), imported last by every built-in devframe panel SPA so
* it inherits a rebranded hub's accent, and imported after `virtual:uno.css` in
* the hub-ui Storybook preview. It always wins over Wind's own `:root, :host`
* primary declarations because it comes last.
*
* Each stop (`--colors-primary-*`) is derived from `--devframe-primary` via
* `color-mix`. When `--devframe-primary` is unset, the intermediate
* `--devframe-primary-<stop>` vars become guaranteed-invalid (their `color-mix`
* references an unset var with no fallback), so each `--colors-primary-<stop>`
* falls back to the exact devframe default hex; the default look is preserved,
* and only an explicit override derives a new ramp. The vars inherit through
* the tree, so setting `--devframe-primary` on the dock host retints
* everything, and setting it on a group's chrome container retints just that
* group (the group-accent mechanism).
* the tree, so setting `--devframe-primary` on a host (the dock host, a viewer
* mount container, or a panel's `<html>`) retints everything below it, and
* setting it on a group's chrome container retints just that group (the
* group-accent mechanism).
*
* The dock's shadow root is built on Wind3, which bakes `primary`-based
* utilities (`text-primary`, `bg-primary`, `btn-primary`, `ring-primary-500`,
* …) to literal `rgb()` triplets rather than referencing these variables, so
* `scripts/build-css.ts` rewires those baked colors into CSS relative-color
* syntax reading `--colors-primary-<stop>` (see `rewireBakedPrimaryColors` in
* `design/uno.config.ts`) so this block actually retints them, not just the
* handful of rules (the glow gradient below) that reference the variables
* directly. The stops declared here (`DEFAULT`/`600`/`500`/`400`/`300`) must
* match `OVERRIDABLE_PRIMARY_STOPS` there.
* A shadow root built on Wind3 bakes `primary`-based utilities (`text-primary`,
* `bg-primary`, `btn-primary`, `ring-primary-500`, …) to literal `rgb()`
* triplets rather than referencing these variables, so `build-shadow-css.ts`
* rewires those baked colors into CSS relative-color syntax reading
* `--colors-primary-<stop>` (see `rewireBakedPrimaryColors` in `uno.config.ts`)
* so this block actually retints them, not just the handful of rules (the dock's
* glow gradient) that reference the variables directly. The stops declared here
* (`DEFAULT`/`600`/`500`/`400`/`300`) must match `OVERRIDABLE_PRIMARY_STOPS`
* there.
*
* `:host` is the effective selector inside the dock's shadow root; `:root` is
* for the light-DOM Storybook preview (it matches nothing in the shadow root).
* `:host` is the effective selector inside a shadow root; `:root` is for the
* light-DOM panel SPAs and the Storybook preview (it matches nothing in a
* shadow root).
*
* `.devframes-accent-scope` re-declares the whole block on a group's chrome
* container: because custom properties inherit as computed values, a descendant
* that only overrides `--devframe-primary` would keep the `:host`-computed
* stops; re-running the derivation here recomputes them from the container's
* own `--devframe-primary` (a group's `accentColor`), retinting just that group.
* Inert on surfaces that never apply the class.
*/
:root,
:host,
Expand Down
15 changes: 12 additions & 3 deletions design/uno.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Preset } from 'unocss'
import { fileURLToPath } from 'node:url'
import { presetAnthonyDesign } from '@antfu/design/unocss'
import { presetAnthonyDesign, resolvePrimary } from '@antfu/design/unocss'
import {
defineConfig,
presetIcons,
Expand Down Expand Up @@ -60,7 +60,16 @@ export function createDesignConfig(options: CreateDesignConfigOptions = {}) {
* shared border color (matching `border-base`) for unqualified borders.
*/
preflights: [{ getCSS: () => '*,::before,::after{border-color:#8882}' }],
theme: {
// Stable palette for status marks and preview content that intentionally
// keeps the default accent when the surrounding panel adopts a theme.
colors: { devframe: resolvePrimary('#3a6a45') },
},
shortcuts: {
/** Fixed semantic colors stay independent of the panel's primary accent. */
'color-status-positive': 'color-devframe-600 dark:color-devframe-300',
'color-preview-accent': 'color-devframe-600 dark:color-devframe-300',
'bg-preview-accent': 'bg-devframe',
/** Fixed navbar height, shared by every surface's top nav. */
'h-nav': 'h-10',
/** Named z-index layers, shared across every surface. */
Expand Down Expand Up @@ -131,8 +140,8 @@ export const shadowSurfaceSafelist: string[] = [
]

/**
* The primary-ramp stops a shadow-root surface's `primary-ramp.css` exposes
* as overridable `--colors-primary-<stop>` custom properties (derived from
* The primary-ramp stops the shared `design/primary-ramp.css` exposes as
* overridable `--colors-primary-<stop>` custom properties (derived from
* `--devframe-primary`). Must match that file's declarations exactly.
*/
const OVERRIDABLE_PRIMARY_STOPS = ['DEFAULT', '600', '500', '400', '300'] as const
Expand Down
2 changes: 1 addition & 1 deletion docs/content/1.guide/18.hub-initiate.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ interface DevframeHubUi {
`@devframes/hub-ui`'s `createUi()` is the reference (standalone `viewer` SPA + floating dock); its `setup(ctx)` publishes config to `ctx.staticConfig.ui` (`ConnectionMeta.configs.ui`):

- **`viewer`**: set to `false` to disable the standalone viewer.
- **`branding`**: rebrand the UI (logo, name, primary color). `background` accepts any CSS `background` value (color, gradient, image, or `transparent`) or `{ light, dark }` variants. These flat forms apply everywhere. Use `{ standalone, iframe? }` to specialize the framed viewer; an omitted `iframe` value falls back to `standalone`.
- **`branding`**: rebrand the UI (logo, name, primary color). `background` accepts any CSS `background` value (color, gradient, image, or `transparent`) or `{ light, dark }` variants. These flat forms apply everywhere. Use `{ standalone, iframe? }` to specialize the framed viewer; an omitted `iframe` value falls back to `standalone`. Built-in devframe panels read `branding.primaryColor` from the existing connection metadata at startup and map it to CSS primary tokens. Standalone SPAs keep their default accent; status and preview palettes remain independent. Reload panels after changing startup branding.
- **`dockPreferences`** tunes the dock rail: `categoryOrder`, floating-dock `maxVisibleItems`, first-run `defaultMode` (`'float'`/`'edge'`) and `defaultPosition`.
- **`embeddedVisibility`** sets the floating dock's reveal policy:
- `'normal'` (default): shows immediately.
Expand Down
2 changes: 1 addition & 1 deletion examples/hub-deno/src/hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const hub: HubInstance = globalRef.__hubDenoMinimal ??= initHub({
* Rebrand the reference UI to Deno's own navy in one field, no CSS:
* `createUi`'s `branding` option publishes `ConnectionMeta.configs.ui.branding`,
* which the dock reads at connect time and feeds into `--devframe-primary`
* (see `@devframes/hub-ui`'s `primary-ramp.css`).
* (see the shared `design/primary-ramp.css`).
*/
ui: createUi({ branding: { primaryColor: '#70ffaf', productName: 'Devframes on Deno' } }),
/**
Expand Down
2 changes: 1 addition & 1 deletion examples/hub-fastify/src/hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const hub: HubInstance = globalRef.__hubFastifyMinimal ??= initHub({
* Rebrand the reference UI to Fastify's own black in one field, no CSS:
* `createUi`'s `branding` option publishes `ConnectionMeta.configs.ui.branding`,
* which the dock reads at connect time and feeds into `--devframe-primary`
* (see `@devframes/hub-ui`'s `primary-ramp.css`).
* (see the shared `design/primary-ramp.css`).
*/
ui: createUi({ branding: { primaryColor: '#2f2f2f', productName: 'Devframes on Fastify' } }),
/**
Expand Down
2 changes: 1 addition & 1 deletion examples/hub-hono/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const hub: HubInstance = globalRef.__hubHonoMinimal ??= initHub({
* Rebrand the reference UI to Hono's own orange in one field, no CSS:
* `createUi`'s `branding` option publishes `ConnectionMeta.configs.ui.branding`,
* which the dock reads at connect time and feeds into `--devframe-primary`
* (see `@devframes/hub-ui`'s `primary-ramp.css`).
* (see the shared `design/primary-ramp.css`).
*/
ui: createUi({ branding: { primaryColor: '#e36002', productName: 'Devframes on Hono' } }),
/**
Expand Down
2 changes: 1 addition & 1 deletion examples/hub-next/src/client/hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ async function loadHub(): Promise<HubInstance> {
// `@devframes/next/hub` runs the socket on a side-car (Next routes can't
// accept WS upgrades). `createUi`'s `branding` option rebrands the dock via
// `ConnectionMeta.configs.ui.branding`, read at connect time and fed into
// `--devframe-primary` (see `@devframes/hub-ui`'s `primary-ramp.css`).
// `--devframe-primary` (see the shared `design/primary-ramp.css`).
return createNextDevframeHub({
devframes,
ui: (hubUi.createUi as typeof CreateUi)({ branding: { primaryColor: '#3f8ba9', productName: 'Devframes on Next.js' } }),
Expand Down
2 changes: 1 addition & 1 deletion examples/hub-nitro/hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const hub: HubInstance = globalRef.__hubNitroMinimal ??= initHub({
* Rebrand the reference UI to Nitro's own pink/red in one field, no CSS:
* `createUi`'s `branding` option publishes `ConnectionMeta.configs.ui.branding`,
* which the dock reads at connect time and feeds into `--devframe-primary`
* (see `@devframes/hub-ui`'s `primary-ramp.css`).
* (see the shared `design/primary-ramp.css`).
*/
ui: createUi({ branding: { primaryColor: '#ff2056', productName: 'Devframes on Nitro' } }),
/**
Expand Down
2 changes: 1 addition & 1 deletion examples/hub-rsbuild/rsbuild.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export default defineConfig({
* CSS: `createUi`'s `branding` option publishes
* `ConnectionMeta.configs.ui.branding`, which the dock reads at
* connect time and feeds into `--devframe-primary` (see
* `@devframes/hub-ui`'s `primary-ramp.css`).
* the shared `design/primary-ramp.css`).
*/
ui: createUi({ branding: { primaryColor: '#ff5e00', productName: 'Devframes on Rsbuild' } }),
/**
Expand Down
2 changes: 1 addition & 1 deletion examples/hub-sveltekit/src/hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const hub: HubInstance = globalRef.__hubSvelteKitMinimal ??= initHub({
* Rebrand the reference UI to Svelte's own orange in one field, no CSS:
* `createUi`'s `branding` option publishes `ConnectionMeta.configs.ui.branding`,
* which the dock reads at connect time and feeds into `--devframe-primary`
* (see `@devframes/hub-ui`'s `primary-ramp.css`).
* (see the shared `design/primary-ramp.css`).
*/
ui: createUi({ branding: { primaryColor: '#ff3e00', productName: 'Devframes on SvelteKit' } }),
/**
Expand Down
2 changes: 1 addition & 1 deletion examples/hub-vite/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export default defineConfig({
* Rebrand the reference UI to Vite's own purple in one field, no CSS:
* `createUi`'s `branding` option publishes `ConnectionMeta.configs.ui.branding`,
* which the dock reads at connect time and feeds into `--devframe-primary`
* (see `@devframes/hub-ui`'s `primary-ramp.css`). Passing `ui` overrides
* (see the shared `design/primary-ramp.css`). Passing `ui` overrides
* the default `createUi()` the plugin would otherwise use.
*/
ui: createUi({ branding: { primaryColor: '#646cff', productName: 'Devframes on Vite' } }),
Expand Down
2 changes: 1 addition & 1 deletion packages/hub-ui/.storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import 'virtual:uno.css'
import '@antfu/design/styles.css'
import '../src/client/style.css'
// After uno so the primary-ramp override wins, matching the shadow-root build.
import '../src/client/primary-ramp.css'
import '../../../design/primary-ramp.css'

// Drive the shared `@antfu/design` tokens off the toolbar theme toggle: dark mode
// is the `.dark` class on `<html>`, and the canvas takes the semantic
Expand Down
2 changes: 1 addition & 1 deletion packages/hub-ui/scripts/build-css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const { sourceCount, css } = await buildShadowCss({
srcDir: SRC_DIR,
globs: ['components/**/*.{ts,vue}', 'state/**/*.ts', 'embedded/**/*.ts', 'standalone/**/*.{ts,html}'],
config,
primaryRampPath: join(SRC_DIR, 'primary-ramp.css'),
primaryRampPath: fileURLToPath(new URL('../../../design/primary-ramp.css', import.meta.url)),
userStylePath: join(SRC_DIR, 'style.css'),
varPrefix: '--un-hub-',
})
Expand Down
2 changes: 1 addition & 1 deletion packages/json-render-ui/scripts/build-css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const { sourceCount, css } = await buildShadowCss({
srcDir: SRC_DIR,
globs: ['components/**/*.ts', 'renderer.ts', 'dock-renderer.ts', 'renderer-module/**/*.ts'],
config,
primaryRampPath: join(SRC_DIR, 'renderer-module/primary-ramp.css'),
primaryRampPath: fileURLToPath(new URL('../../../design/primary-ramp.css', import.meta.url)),
userStylePath: [
moduleRequire.resolve('@antfu/design/styles/scrollbar.css'),
join(SRC_DIR, 'renderer-module/style.css'),
Expand Down
37 changes: 0 additions & 37 deletions packages/json-render-ui/src/renderer-module/primary-ramp.css

This file was deleted.

8 changes: 8 additions & 0 deletions plugins/a11y/app/main.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
/// <reference types="vite/client" />

/* @refresh reload */

import { render } from 'solid-js/web'
import { applyPanelBranding } from '../../../design/panel-theme'
import { App } from './app.tsx'
import 'virtual:uno.css'
import '@antfu/design/styles.css'
import './styles.css'
import '../../../design/primary-ramp.css'

// Shared design tokens flip on the `.dark` class; mirror the OS preference onto
// <html> (the other devframe plugins follow the same approach).
Expand All @@ -20,3 +25,6 @@ if (!root)
throw new Error('#app mount node missing from index.html')

render(() => <App />, root)

const stopPanelTheme = applyPanelBranding()
import.meta.hot?.dispose(stopPanelTheme)
5 changes: 5 additions & 0 deletions plugins/assets/app/main.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { createApp } from 'vue'
import { applyPanelBranding } from '../../../design/panel-theme'
import App from './app/App.vue'
import 'virtual:uno.css'
import 'floating-vue/dist/style.css'
import '@antfu/design/styles.css'
import '../../../design/primary-ramp.css'

// Shared design tokens flip on the `.dark` class; mirror the OS preference
// onto <html> (the built-in devframe plugins all follow this approach).
Expand All @@ -15,3 +17,6 @@ applyScheme(mq.matches)
mq.addEventListener('change', e => applyScheme(e.matches))

createApp(App).mount('#app')

const stopPanelTheme = applyPanelBranding()
import.meta.hot?.dispose(stopPanelTheme)
7 changes: 7 additions & 0 deletions plugins/code-server/app/main.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
/// <reference types="vite/client" />

import { createApp } from 'vue'
import { applyPanelBranding } from '../../../design/panel-theme'
import App from './App.vue'
import 'virtual:uno.css'
import '@antfu/design/styles.css'
import './style.css'
import '../../../design/primary-ramp.css'

// The shared design tokens flip on the `.dark` class; mirror the OS preference
// onto <html> (the other devframe plugins follow the same approach).
Expand All @@ -15,3 +19,6 @@ applyScheme(mq.matches)
mq.addEventListener('change', e => applyScheme(e.matches))

createApp(App).mount('#app')

const stopPanelTheme = applyPanelBranding()
import.meta.hot?.dispose(stopPanelTheme)
2 changes: 1 addition & 1 deletion plugins/data-inspector/app/components/QueryEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ ul.discovery-view-editor-hints-popup {
--at-apply: 'bg-#8882 color-active';
}
.discovery-view-editor-hint .match {
--at-apply: 'color-primary-700 dark:color-primary-300';
--at-apply: 'color-active';
font-weight: 600;
}
.discovery-view-editor-hint::before {
Expand Down
7 changes: 7 additions & 0 deletions plugins/data-inspector/app/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
/// <reference types="vite/client" />

import { createApp } from 'vue'
import { applyPanelBranding } from '../../../design/panel-theme'
import App from './App.vue'
import 'virtual:uno.css'
// floating-vue's base popper/transition structure, then @antfu/design's themed
Expand All @@ -8,5 +11,9 @@ import 'floating-vue/dist/style.css'
import '@antfu/design/styles/floating-vue.css'
import '@antfu/design/styles.css'
import './style.css'
import '../../../design/primary-ramp.css'

createApp(App).mount('#app')

const stopPanelTheme = applyPanelBranding()
import.meta.hot?.dispose(stopPanelTheme)
1 change: 1 addition & 0 deletions plugins/git/app/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Metadata } from 'next'
import type { ReactNode } from 'react'
import './globals.css'
import '@antfu/design/styles.css'
import '../../../../design/primary-ramp.css'

export const metadata: Metadata = {
title: 'Git Dashboard',
Expand Down
Loading
Loading