feat(dashboard): stable theme token contract and plugin overlay layering (#2415)
## Summary Gives dashboard integrators (plugin views, embedded panels, theming tools) a supported way to match the dashboard's look and to layer overlay UI correctly — instead of scraping computed styles and guessing z-index values. This implements the CSS-token bridge slice of `docs/proposals/2026-07-01-dashboard-theme-plugin-system.md`. Two additions, both inert unless used: 1. **Documented theme-token contract.** A "Theme tokens" section in `docs/dashboard-guide.md` (referenced from `docs/PLUGIN_AUTHORING.md`) declares the stable set of CSS custom properties — colors, surfaces, status colors — that integrators may read. Tokens resolve to raw color strings (e.g. `#161b22`) in every theme, including the newer ones. A sync test (`theme-token-contract-docs.test.ts`) parses the doc's token table and asserts each documented token has a real definition in the dashboard CSS, so the contract cannot silently drift from the code. 2. **Overlay layering surface.** Overlay-style UI (palettes, pickers, floating panels) currently has no supported way to sit above the floating-window stack — the effective max z-index is runtime state inside `floatingWindowStack.ts`. This PR exposes it: - `--fusion-max-z` on `:root` — kept in sync by `floatingWindowStack` (written at module load and after every `nextFloatingZ()` claim), so it always reflects the true top of the dashboard-managed stack. Boot/floor value is `11001`, chosen to clear the highest statically-declared layer (the body-portaled model-combobox dropdown at `z-index: 11000`). - `#plugin-overlay-root` — an empty, `pointer-events: none` sibling of `#root` stacked at `calc(var(--fusion-max-z) + 1)`. React never renders into it, so it is hydration-safe; integrators portal into it and re-enable pointer events on their own elements. - The layer bands (base UI / floating windows / toasts / dropdown / overlay root) are documented in `styles.css` and the guide, and a guard test (`dashboard-max-z-guard.test.ts`) scans the structural + component CSS and fails if any static `z-index` is ever introduced above the floor — keeping the contract honest as the codebase evolves. ## Behavior No visual or behavioral change for existing users: `floatingWindowStack` still returns the same values from `nextFloatingZ()`; the overlay root is empty and click-through; tokens were already defined — this only documents and guards them. ## Tests - `theme-token-contract-docs.test.ts` — docs ↔ CSS sync (non-tautological: anchored matching against real definitions). - `floatingWindowStack.max-z.test.ts` — `--fusion-max-z` boot value and live tracking as the stack claims z-indexes. - `dashboard-max-z-guard.test.ts` — no static dashboard z-index above the floor (decorative `public/theme-data.css` INT_MAX scanline overlay deliberately excluded; it's non-interactive grain, documented in the test). - Changeset included (`minor`, `category: feature`). Typecheck clean. ## Open question for maintainers The token is named `--fusion-max-z`. The existing scale uses `--z-*` names (`--z-dropdown`, `--z-modal`) on a lower band — happy to rename to `--z-max` / `--z-plugin-overlay` or anything that fits your convention; the name is the only bikeshed here, the sync mechanism is independent of it. ## AI assistance disclosure Parts of this change were authored with AI assistance (Anthropic's Claude); the commit carries a `Co-authored-by` trailer accordingly. Everything was human-reviewed before submission, and the test suite and typecheck were run locally against the current `main`. If squash-merging with a rewritten message, please keep the attribution: ``` Co-authored-by: Claude <noreply@anthropic.com> ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added stable dashboard theme tokens for consistent plugin/integration styling. - Introduced a dedicated plugin overlay mount point with click-through defaults and an overlay stacking ceiling. - Overlay z-index now stays in sync with floating window layering automatically. - **Documentation** - Added an explicit stable “theme token contract” and “overlay layering contract,” including interaction and z-index usage rules and deprecation expectations. - **Bug Fixes** - Improved reliability of plugin overlay stacking so overlay content renders above intended dashboard layers. - **Tests** - Added guards validating CSS z-index ceilings and enforcing the documented theme token contract. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ A comprehensive guide to creating Fusion plugins that extend the task board with
|
||||
6. [Registering Routes](#6-registering-routes)
|
||||
7. [Registering UI Slots](#7-registering-ui-slots)
|
||||
8. [Registering Top-Level Dashboard Views](#8-registering-top-level-dashboard-views)
|
||||
- [Theming & Overlay Layering for Dashboard Views](#theming--overlay-layering-for-dashboard-views)
|
||||
9. [Registering Agent Runtimes](#9-registering-agent-runtimes)
|
||||
10. [Plugin Context API Reference](#10-plugin-context-api-reference)
|
||||
11. [Plugin Lifecycle States](#11-plugin-lifecycle-states)
|
||||
@@ -815,6 +816,33 @@ Project-scoped UI state guidance:
|
||||
- For dependency graph layout, the canonical base key is `fusion-plugin-dependency-graph:positions`.
|
||||
- Do not persist plugin UI state in task metadata or server-side task records.
|
||||
|
||||
### Theming & Overlay Layering for Dashboard Views
|
||||
|
||||
Use only the [stable theme token contract](./dashboard-guide.md#stable-theme-token-contract-integrators--plugins) for plugin UI. It provides supported surface, text, spacing, status, motion, and layering variables; internal CSS names may change without notice.
|
||||
|
||||
```css
|
||||
.my-plugin-panel {
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.my-plugin-owned-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: calc(var(--fusion-max-z) + 1);
|
||||
pointer-events: auto;
|
||||
}
|
||||
```
|
||||
|
||||
For overlays that should share Fusion's root stacking context, append the plugin element to the supported `#plugin-overlay-root` mount point:
|
||||
|
||||
```ts
|
||||
const overlayRoot = document.querySelector("#plugin-overlay-root");
|
||||
overlayRoot?.append(pluginOverlayElement);
|
||||
```
|
||||
|
||||
The mount point is fixed and click-through; set `pointer-events: auto` on interactive plugin children. Its layer follows `--fusion-max-z` as Fusion's monotonic floating-window stack rises, so plugins do not need to track dashboard window focus in JavaScript.
|
||||
|
||||
---
|
||||
|
||||
## 9. Registering Agent Runtimes
|
||||
|
||||
@@ -1963,6 +1963,79 @@ Command Center chart surfaces are a stricter token-only zone: `CommandCenter.css
|
||||
|
||||
Non-Command-Center dashboard CSS uses `--text` as the canonical primary text token. The undefined `--text-primary` alias is forbidden outside `components/command-center/**` and guarded by `packages/dashboard/app/__tests__/text-token-canonicalization.test.ts`.
|
||||
|
||||
### Stable theme token contract (integrators & plugins)
|
||||
|
||||
The following curated tokens are the supported dashboard theming contract for integrations and plugin-rendered UI. Each token is defined by `styles.css`; use these names rather than depending on internal or theme-data-only variables.
|
||||
|
||||
<!-- fusion-theme-token-contract:start -->
|
||||
| Token | Stable meaning |
|
||||
|---|---|
|
||||
| `--space-xs` | Extra-small spacing step |
|
||||
| `--space-sm` | Small spacing step |
|
||||
| `--space-md` | Medium spacing step |
|
||||
| `--space-lg` | Large spacing step |
|
||||
| `--space-xl` | Extra-large spacing step |
|
||||
| `--space-2xl` | Largest shared spacing step |
|
||||
| `--radius-sm` | Small corner radius |
|
||||
| `--radius-md` | Medium corner radius |
|
||||
| `--radius-lg` | Large corner radius |
|
||||
| `--radius-xl` | Extra-large corner radius |
|
||||
| `--radius-pill` | Pill-shaped corner radius |
|
||||
| `--font-primary` | Dashboard UI font stack |
|
||||
| `--font-mono` | Dashboard monospace font stack |
|
||||
| `--font-size-xs` | Caption and help text size |
|
||||
| `--font-size-base` | Default body text size |
|
||||
| `--shadow-sm` | Subtle elevation shadow |
|
||||
| `--shadow-md` | Standard elevation shadow |
|
||||
| `--shadow-lg` | High elevation shadow |
|
||||
| `--focus-ring` | Subtle focus indicator shadow |
|
||||
| `--focus-ring-strong` | Emphasized focus indicator shadow |
|
||||
| `--duration-instant` | Instant motion duration |
|
||||
| `--duration-fast` | Fast motion duration |
|
||||
| `--duration-normal` | Standard motion duration |
|
||||
| `--duration-slow` | Slow motion duration |
|
||||
| `--transition-instant` | Instant duration and easing shorthand |
|
||||
| `--transition-fast` | Fast duration and easing shorthand |
|
||||
| `--transition-normal` | Standard duration and easing shorthand |
|
||||
| `--transition-slow` | Slow duration and easing shorthand |
|
||||
| `--bg` | Primary application background |
|
||||
| `--surface` | Primary raised surface |
|
||||
| `--card` | Card surface |
|
||||
| `--card-hover` | Hovered card surface |
|
||||
| `--surface-hover` | Neutral hovered surface |
|
||||
| `--bg-secondary` | Secondary application background |
|
||||
| `--bg-tertiary` | Tertiary application background |
|
||||
| `--border` | Default border color |
|
||||
| `--border-subtle` | Low-contrast border color |
|
||||
| `--border-strong` | High-contrast border color |
|
||||
| `--text` | Primary text color |
|
||||
| `--text-muted` | Secondary text color |
|
||||
| `--text-dim` | De-emphasized text color |
|
||||
| `--triage` | Triage workflow status color |
|
||||
| `--todo` | To-do workflow status color |
|
||||
| `--in-progress` | In-progress workflow status color |
|
||||
| `--in-review` | In-review workflow status color |
|
||||
| `--done` | Done workflow status color |
|
||||
| `--color-success` | Semantic success color |
|
||||
| `--color-error` | Semantic error color |
|
||||
| `--color-warning` | Semantic warning color |
|
||||
| `--color-info` | Semantic informational color |
|
||||
| `--color-muted` | Semantic muted color |
|
||||
| `--fusion-max-z` | Live dashboard floating-layer ceiling |
|
||||
<!-- fusion-theme-token-contract:end -->
|
||||
|
||||
Color tokens resolve to raw color strings (e.g. `#161b22`), not shadcn-style HSL triples, so a token can be used directly as a `color`, `background`, or `border` value without wrapping it in `hsl(...)`.
|
||||
|
||||
#### Overlay layering contract
|
||||
|
||||
`--fusion-max-z` is always at least as high as the dashboard-managed floating layers covered by this contract: the page overlay/popover band at 10000–10001, the session-monotonic floating-utility stack starting at 10100, the reserved toast/feedback ceiling at 10500, and the body-portaled model-combobox dropdown at 11000. Its CSS boot value is 11001, one above the tallest static layer. `floatingWindowStack.ts` raises the inline value on `document.documentElement` whenever the utility stack grows beyond that floor, and CSS `var()` references re-resolve automatically. The separate task-detail popup band starting at 220 is intentionally not a source for updates because it remains below the utility band.
|
||||
|
||||
For the simplest integration, append overlay content to `#plugin-overlay-root`. This fixed, viewport-sized mount point uses `z-index: calc(var(--fusion-max-z) + 1)` and is click-through by default; interactive children must set `pointer-events: auto`. A plugin that owns another root stacking context can apply the same z-index expression directly.
|
||||
|
||||
A static mount-point z-index would eventually be overtaken by the unbounded, session-monotonic utility counter. The live custom property is therefore the layering primitive; the mount point is an inert convenience consumer. When empty, it does not alter layout, scrolling, or pointer behavior.
|
||||
|
||||
Tokens in the table are stable. Renaming or removing one requires a deprecation note and a changeset; `theme-token-contract-docs.test.ts` guards that every documented token still has a CSS definition.
|
||||
|
||||
### Theme system
|
||||
|
||||
<!-- FNXC:DashboardTheming 2026-06-21-00:00: FN-6840 synced the user-facing theme docs to the shipped expanded Shadcn family, the Shadcn Custom color-picker preset, and the sidebar accent behavior that follows each theme's --accent token. -->
|
||||
|
||||
Reference in New Issue
Block a user