From 8f6a4af4958bfb4a89f3cdf02bbe5d4036cace7a Mon Sep 17 00:00:00 2001 From: Andrei Kirkouski Date: Thu, 6 Aug 2026 12:30:37 +0200 Subject: [PATCH 1/3] docs: add managed layout release matrix --- en/frontend/web-host/multi-panel-layout.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/en/frontend/web-host/multi-panel-layout.md b/en/frontend/web-host/multi-panel-layout.md index b66c86df..568ee605 100644 --- a/en/frontend/web-host/multi-panel-layout.md +++ b/en/frontend/web-host/multi-panel-layout.md @@ -30,6 +30,19 @@ Opt in to `fe_mode = managed` (early access) only when you need to compose the c Managed layout spans the Web Host, facade, and several `@wippy-fe/*` packages. Use one compatible package family for the exact target Web Host release and verify its served import map; do not mix package versions from unrelated releases. +### Release map + +| Release | Managed-layout additions | +|---|---| +| Web Host `1.0.50`, Wippy FE `0.0.50` | Typed compat intents, `@HOST/compat-coordinator`, browser URL and Back/Forward synchronization, built-in panel tabs, anchored floating panels, and `useSwapBuffer()`. | +| Web Host `1.0.51`, Wippy FE `0.0.51` | Reactive and race-safe `` session/token control, opt-in themed splitter handles, split-axis-only size constraints, drawer geometry/stacking fixes, and the packaged proxy source map. | +| Web Host `1.0.52`, Wippy FE `0.0.52` | Typed retained-WC visibility and `useHostVisibilityRefresh()`, immediate page readiness instead of waiting for the 14-second fallback, stale renderer-key rejection, in-place component prop updates, and the isolated splitter layer with `--wippy-layout-splitter-z-index`. | + +The 14-second page reveal is a Web Host `1.0.52` fallback, not a 1.0.51 +feature or an application loading delay. Split-axis sizing and reactive chat +landed in 1.0.51; retained visibility, keyed readiness, and splitter layering +landed in 1.0.52. + Retained direct-web-component visibility requires Web Host `1.0.52` and `@wippy-fe/webcomponent-core`, `@wippy-fe/webcomponent-vue`, and `@wippy-fe/shared` `0.0.52`. Earlier managed-layout releases do not provide the From 226615f7bc49fd27f1f12ebfc7bfd8231ebbf934 Mon Sep 17 00:00:00 2001 From: Andrei Kirkouski Date: Fri, 7 Aug 2026 16:41:08 +0200 Subject: [PATCH 2/3] docs(theme): explain complete token propagation --- en/framework/facade.md | 4 +- .../micro-frontends/compliance-checklist.md | 18 +++++--- en/frontend/micro-frontends/debugging.md | 42 +++++++++++++------ en/frontend/micro-frontends/host-less-mode.md | 2 +- .../micro-frontend-app-theming.md | 8 ++-- en/frontend/micro-frontends/theming.md | 25 +++++++---- .../micro-frontends/web-component-theming.md | 21 ++++++---- en/frontend/web-host/css-injection.md | 12 +++--- 8 files changed, 86 insertions(+), 46 deletions(-) diff --git a/en/framework/facade.md b/en/framework/facade.md index df6967bb..c435112b 100644 --- a/en/framework/facade.md +++ b/en/framework/facade.md @@ -102,7 +102,7 @@ Three scopes apply: **global** (everywhere), **host** (the Web Host chrome — s | Parameter | Scope | Default | Description | |-----------|-------|---------|-------------| | `custom_css` | global | Google Fonts import | Global CSS — reaches host chrome, `view.page` iframes, and `view.component` shadow roots (1.0.43+). | -| `css_variables` | global | `{}` | JSON map of CSS custom properties; reaches every surface (variables inherit into shadow roots). | +| `css_variables` | global | `{}` | JSON map of arbitrary CSS custom properties; compiled for Auto and forced modes and bridged into component shadow roots. | | `icon_sets` | global | `[]` | Iconify icon-set URLs (inline JSON only — no `fs://`) | | `host_custom_css` | host | `""` | CSS for the host chrome only — not children. Scope class-based rules to `.wippy-host-app`. | | `host_css_variables` | host | `{}` | CSS custom properties for the host chrome only | @@ -141,7 +141,7 @@ Use `fs://` (resolved by `content_fs` at runtime), **not** `file://` — `file:/ A standalone page then links both: - **`custom_css`** — already a `.css` file, so link it directly from where it is served. -- **`css_variables`** — JSON, so it is not linkable as-is. The facade renders it as a stylesheet at **`GET /facade/variables.css`** (a `text/css` `:root { … }` sheet, with `@dark` / `@light` compiled to `@media (prefers-color-scheme: …)`, cached 1h). It is registered on the same public router as `/facade/config`, so it carries the router prefix. +- **`css_variables`** — JSON, so it is not linkable as-is. The facade renders it at **`GET /facade/variables.css`** as base plus effective Auto-light, Auto-dark, forced Light, and forced Dark blocks. Top-level values apply everywhere; `@light` / `@dark` replace selected names. The sheet is cached for 1h and registered on the same public router as `/facade/config`, so it carries the router prefix. ```html diff --git a/en/frontend/micro-frontends/compliance-checklist.md b/en/frontend/micro-frontends/compliance-checklist.md index 61f33953..3431abaf 100644 --- a/en/frontend/micro-frontends/compliance-checklist.md +++ b/en/frontend/micro-frontends/compliance-checklist.md @@ -1220,7 +1220,7 @@ A Wippy module composes itself from `ns.dependency` entries. **One of those is ` |---|---| | `app_title`, `app_name`, `app_icon` | brand identity | | `custom_css` | shared facade-theme CSS — reaches the host chrome, `view.page` documents, and `view.component` shadow roots on supported hosts. Put shared PrimeVue appearance here; keep necessary domain layout and novel structure in module CSS. | -| `css_variables` | JSON map of CSS variable overrides (`--p-primary`, `--p-surface-*`, brand-specific `--k-*` tokens, etc.); custom properties inherit into every surface, shadow roots included. | +| `css_variables` | JSON map of arbitrary custom-property overrides. The compiler emits effective Auto/forced mode blocks, and WippyElement bridges every configured name into its inner theme root before `customCSS`. | | `host_custom_css` | host-chrome-only CSS (not delivered to children — scope class rules to `.wippy-host-app`). Use `children_custom_css` for CSS that should reach those children but not the host chrome. | | `hide_nav_bar`, `show_admin`, `history_mode`, `session_type`, `login_path` | UX shell behaviour | | `fe_mode`, `host_config_layout` | managed-layout mode + layout declaration | @@ -1268,7 +1268,7 @@ Mismatched placement is the #1 source of theme drift. The rule: Anti-pattern (REJECT): ```css .card { background: var(--p-surface-100); } /* fixed; doesn't flip */ -.card { background: var(--p-primary); } /* invalid token; --p-primary-color is the right one */ +.card { background: var(--p-primary); } /* valid palette seed, wrong semantic consumer; use --p-primary-color */ ``` Canonical: @@ -1304,7 +1304,7 @@ Canonical: ### 5.4 `@light` / `@dark` blocks -The host SUPPORTS `@light` and `@dark` keys in `cssVariables` maps — they compile to `@media (prefers-color-scheme: light/dark) { :root { ... } }` blocks ONLY. They are NOT a `[data-theme]` attribute and do not emit any attribute-scoped selector at injection time — binding is solely on the OS color-scheme preference (see `createCssVariables` in `src/shared/util/createStyle.ts`). +The host supports `@light` and `@dark` keys in `cssVariables`. Top-level variables apply to every mode; mode maps replace only their named entries. The compiler emits effective Auto-light/Auto-dark media blocks and forced `.w-theme-light` / `.w-theme-dark` selectors, with shadow-aware anchors for Web Fragments. Example: ```yaml @@ -1319,7 +1319,13 @@ css_variables: --p-text-color: '#fafafa' ``` -An app that toggles themes via `document.documentElement.setAttribute('data-theme', ...)` will NOT trigger these overrides; the host injects no `[data-theme]` CSS. To support a manual toggle, document it as a project-specific extension and emit your own `[data-theme]`-scoped variable block. See also [micro-frontend-app-theming.md](./micro-frontend-app-theming.md) and [host-less-mode.md](./host-less-mode.md). +Applications must use the host theme mode and `.w-theme-light` / `.w-theme-dark` protocol; do not invent `data-theme` selectors. Pages that may also render as Web Fragments pair `:root.w-theme-*` with `:host(.w-theme-*)`; a pure shadow-DOM component may use `:host(.w-theme-*)` alone. See also [micro-frontend-app-theming.md](./micro-frontend-app-theming.md) and [host-less-mode.md](./host-less-mode.md). + +### 5.4.1 Exact propagation acceptance + +For any configured palette, static presence checks are insufficient. Drive the inventory from `@wippy-fe/theme/tokens.json` and verify `primary`, `secondary`, `accent`, `danger`, `success`, `warn`, `info`, and `help` independently. For each family assert its base, every 50–950 shade, the four semantic aliases, and one direct shade/alias override. Also cover a surface token and an arbitrary sentinel property. + +Run the same assertions in Auto-light, Auto-dark, forced Light, and forced Dark. Compare exact values at page root, WC host, and WC inner `[data-wippy-theme-root]`, then use rendered color probes so the browser physically resolves `color-mix()`. Changing primary must not change any severity family. Reject a primary-only bridge, a hand-maintained family whitelist, or advice to duplicate complete palettes in application CSS. ### 5.5 `customCSS` scoping @@ -1879,7 +1885,7 @@ REJECT a submission if any of the following are true. 42b. ANY child-app `.css` file contains `:root { --p-* … }` or `:root { -- … }` redefinition (§5.1.2). Move to facade theming or per-page YAML `config_overrides.customization.cssVariables`. 43. PrimeVue component tokens are restyled with `!important` in `styles.css`. 43a. A child module duplicates shared PrimeVue appearance in local `.p-*` rules. Move shared appearance to facade `custom_css`; retain module CSS only for justified domain layout or novel structure. -44. Any Vue file uses `var(--p-primary)` (invalid token; must be `--p-primary-color`). +44. Any rendered UI uses palette seed `var(--p-primary)` where the mode-aware semantic consumer `--p-primary-color` is required. 45. Any Vue file uses raw Tailwind color names (`bg-red-*`, `bg-sky-*`, etc.) for semantic meaning. 46. Any hardcoded hex/rgb in Vue source for semantic colors (use `--p-danger-*` etc., or `color-mix()`). 46a. Page renders correctly in only one of `prefers-color-scheme: dark` / `light`. Verify in a browser emulator before claiming the page is shippable (§10.7). @@ -2062,7 +2068,7 @@ interface ProxyConfig { primevue: boolean // PrimeVue component CSS markdown: boolean // markdown typography customCss: boolean // theming.global.customCSS - customVariables: boolean // theming.global.cssVariables → :root + customVariables: boolean // theming.global.cssVariables → effective Auto/forced mode blocks } tailwindConfig: boolean // window.tailwind.config resizeObserver: boolean // report iframe size diff --git a/en/frontend/micro-frontends/debugging.md b/en/frontend/micro-frontends/debugging.md index 03139b75..41825fa8 100644 --- a/en/frontend/micro-frontends/debugging.md +++ b/en/frontend/micro-frontends/debugging.md @@ -68,23 +68,39 @@ The dev overlay starts with `themeConfig`, `primevue`, `markdown`, and `iframe` Open the dev overlay FAB → toggle the CSS injections you need → check "Auto-accept on reload". -**2. Verify CSS variables are active:** - -For micro frontend apps — open DevTools, select the **inner iframe** context (not the outer page) in the frame selector: -```javascript -getComputedStyle(document.documentElement).getPropertyValue('--p-primary-color') -// non-empty = themeConfig injection is working +**2. Compare the complete effective chain:** + +A non-empty token is not sufficient. Use distinct values so a stock-palette reset or accidental family alias is obvious: + +```yaml +css_variables: + "--p-primary": "#dc2626" + "--p-secondary": "#7c3aed" + "--p-accent": "#0d9488" + "--p-danger": "#be123c" + "--p-success": "#15803d" + "--p-warn": "#c2410c" + "--p-info": "#0369a1" + "--p-help": "#9333ea" + "--theme-diagnostic-sentinel": "#123456" ``` -For web components — in DevTools, select your custom element's **shadow root** context: -```javascript -getComputedStyle(document.documentElement).getPropertyValue('--p-primary-color') -// custom properties cross shadow boundary; empty = something wrong upstream -``` +Then compare, in this order: + +1. **Effective configured map:** inspect `config.theming.global.cssVariables` and confirm the base plus the active `@light` / `@dark` replacements. +2. **Page root:** read the exact token with `getComputedStyle(document.documentElement).getPropertyValue(name).trim()`. +3. **WC host:** read the same token from `getComputedStyle(customElement)`. +4. **WC inner root:** read it from `getComputedStyle(customElement.shadowRoot.querySelector('[data-wippy-theme-root]'))`. +5. **Rendered semantic color:** put `background-color: var(--p--color)` on a probe and compare its computed `backgroundColor`; this physically resolves `color-mix()`. + +Repeat in Auto-light, Auto-dark, forced Light, and forced Dark. For each configured family verify its base, all 50–950 shades, `color`, `contrast-color`, `hover-color`, and `active-color`; also verify a direct shade/alias override, a surface token, and the sentinel. Page, host, and inner values must agree. + +Interpret the first divergence: wrong effective map means configuration/merge; wrong page root means variable compilation/injection; correct page but wrong WC host means host propagation; correct WC host but wrong inner root means the forced-theme bridge or local defaults; equal tokens but wrong rendered color means the consuming selector or semantic alias is wrong. **3. Web component specific:** -- If CSS vars are empty inside shadow root: check that `hostCssKeys` includes `'themeConfigUrl'` in your `wippyConfig` -- If PrimeVue components render unstyled: add `'primeVueCssUrl'` to `hostCssKeys` +- If the platform defaults are absent, check that `hostCssKeys` includes `'themeConfigUrl'`. +- If the host is correct but the inner root resets to stock values, verify a current `@wippy-fe/webcomponent-core`; do not copy a palette into component CSS. +- If PrimeVue components render unstyled, add `'primeVueCssUrl'` to `hostCssKeys`. See [Theming: Micro Frontend Apps](./micro-frontend-app-theming.md) or [Theming: Web Components](./web-component-theming.md) for the full injection pipeline. diff --git a/en/frontend/micro-frontends/host-less-mode.md b/en/frontend/micro-frontends/host-less-mode.md index f0da7166..2a0def70 100644 --- a/en/frontend/micro-frontends/host-less-mode.md +++ b/en/frontend/micro-frontends/host-less-mode.md @@ -354,7 +354,7 @@ When an app or WC has drifted from the standalone-aware contract, the symptoms a | `import` from a sibling app's source files | Shared code is being copy-pasted across module boundaries. | Extract to a workspace package or duplicate intentionally; never reach across app folders. | | Hardcoded `fetch('/api/…')` calls | Bypasses the axios instance the proxy provides; won't pick up `env.APP_API_URL` overrides. | Use `useApi()` (apps) or `import { api } from '@wippy-fe/proxy'` (WCs). | | `new EventSource(...)` for live data | Bypasses the host's auth/relay bridge; standalone mode has no equivalent. | Use `on('your.topic', cb)` — works in both modes (in standalone the topic just doesn't fire unless you simulate it). | -| `document.documentElement.setAttribute('data-theme', ...)` for theme switch | Custom theme attribute is invisible to the proxy's `cssVariables.@dark/@light` (those bind to `prefers-color-scheme`). | Either drive theme from OS preference and use `@dark`/`@light` blocks, or document the attribute as a project-specific extension. See [micro-frontend-app-theming.md](./micro-frontend-app-theming.md#l3--per-page-config_overrides-in-registry-yaml). | +| `document.documentElement.setAttribute('data-theme', ...)` for theme switch | `data-theme` is not the Wippy theme protocol. | Use Auto mode or the host-managed `.w-theme-light` / `.w-theme-dark` classes. Configured `@light` / `@dark` values support both paths. See [micro-frontend-app-theming.md](./micro-frontend-app-theming.md#l3--per-page-config_overrides-in-registry-yaml). | | `import '@wippy-fe/theme/theme-config.css'` in `app.ts` | Redundant — the host injects theme-config via `themeConfig: true` proxy injection. In host-less mode dev-proxy injects it too. | Remove the import. | | Hardcoded API base URLs in api/ modules | Won't work in host-less mode against a different env. | Read from `appConfig.env.APP_API_URL` via `useApi()`. | diff --git a/en/frontend/micro-frontends/micro-frontend-app-theming.md b/en/frontend/micro-frontends/micro-frontend-app-theming.md index 9cfefef2..ac698e49 100644 --- a/en/frontend/micro-frontends/micro-frontend-app-theming.md +++ b/en/frontend/micro-frontends/micro-frontend-app-theming.md @@ -19,7 +19,7 @@ CSS vars set in the facade's global theming scope reach the host and all iframes ```yaml - name: css_variables - value: '{"--p-primary":"#4f8ef7","--p-primary-color":"#4f8ef7"}' + value: '{"--p-primary":"#4f8ef7","--p-secondary":"#6f7385","--p-danger":"#dc2626"}' ``` ### L2 — Scoped (host or children scope) @@ -54,7 +54,7 @@ Give a page its own theme by setting `config_overrides.customization.cssVariable .demo-banner { background: var(--p-primary-color); color: var(--p-primary-contrast-color); } ``` -`@dark` and `@light` keys compile to `@media (prefers-color-scheme: dark/light)` blocks — they are OS-preference based, not a `[data-theme]` attribute. +Top-level entries apply in every theme mode. `@dark` and `@light` replace selected entries and compile to both Auto-mode media blocks and forced `.w-theme-dark` / `.w-theme-light` selectors. The host owns those classes; applications do not invent a parallel `data-theme` protocol. A `package.json` mirror under `wippy.configOverrides` provides the same shape for host-less rendering (standalone dev preview, unit tests). Keep both in sync; the YAML wins when a host is present. @@ -88,7 +88,7 @@ The iframe proxy has broad runtime defaults when flags are omitted. **Enable the - `css.themeConfig` — the full `--p-*` CSS variable system (`theme-config.css`). Enable to inherit the theme palette. - `css.primevue` — PrimeVue component styles. Enable for apps using PrimeVue. - `css.customCss` — the host-composed child-facing custom CSS: facade **global + children** custom CSS merged into `config.theming.global.customCSS`, plus any per-page override. The flag gates this injection rather than naming a single scope. Enable to receive facade/per-page custom CSS. -- `css.customVariables` — child-projected `config.theming.global.cssVariables` as `:root { … }`. Enable to receive theme variable overrides. +- `css.customVariables` — child-projected `config.theming.global.cssVariables` as effective base, Auto-light, Auto-dark, forced Light, and forced Dark blocks. Enable to receive theme variable overrides. - `css.markdown` — `.data-body` markdown styles. Enable only if your page renders markdown content. Full flag reference and runtime defaults: [CSS Injection](../web-host/css-injection.md). @@ -124,7 +124,7 @@ To confirm CSS variables are active in your running page: open DevTools, select getComputedStyle(document.documentElement).getPropertyValue('--p-primary-color') ``` -A non-empty result confirms `themeConfig` injection is working. Full debugging workflow: [Debugging](./debugging.md). +A non-empty result proves only that some theme CSS loaded. Compare the exact configured value at the page root, WC host, WC inner root, and rendered semantic color; verify every configured family. Full workflow: [Debugging](./debugging.md). --- diff --git a/en/frontend/micro-frontends/theming.md b/en/frontend/micro-frontends/theming.md index 0ba98bee..e4dc1181 100644 --- a/en/frontend/micro-frontends/theming.md +++ b/en/frontend/micro-frontends/theming.md @@ -7,7 +7,7 @@ description: "The host (wippy/facade) provides the theme. Both micro frontend ap The host (wippy/facade) provides the theme. Both micro frontend apps and web components consume it. The variable catalog below is the shared vocabulary — delivery specifics are in [Theming: Micro Frontend Apps](./micro-frontend-app-theming.md) and [Theming: Web Components](./web-component-theming.md). -YAML always wins. CSS custom properties (`*_css_variables`) set by the facade/host cascade to child iframes and inherit into shadow DOM. Facade selector rules (`*_custom_css`) do not *cascade* across the shadow boundary, but the Web Host **injects** them into `view.component` shadow roots as of Web Host 1.0.43 (opt-out via the component's `customCss` flag). See the [CSS Delivery Matrix](../web-host/css-injection.md#css-delivery-matrix). +YAML always wins. CSS custom properties (`*_css_variables`) are compiled into effective Auto/forced mode blocks for pages and bridged generically through WippyElement shadow roots. Facade selector rules (`*_custom_css`) do not cascade across the shadow boundary, but the Web Host injects them into `view.component` shadow roots as of Web Host 1.0.43 (opt-out via the component's `customCss` flag). See the [CSS Delivery Matrix](../web-host/css-injection.md#css-delivery-matrix). Configuration casing identifies the layer: @@ -26,7 +26,7 @@ Do not use `customCSS` when naming a facade backend parameter, and do not use `c ## Reference — CSS variables -All variables are defined in `theme-config.css` and set on `:root`. At runtime, the host injects the real theme — these serve as the dev-time fallback and contract. +The generated `theme-config.css` defines the canonical defaults and formulas. Facade and page `cssVariables` are compiled as a later, variable-agnostic override layer: top-level values apply in every mode, while `@light` and `@dark` replace selected values in the corresponding Auto and forced modes. Web components receive the same configured names through the WippyElement inheritance bridge before facade `custom_css` is injected. ### Primary palette (11 vars) @@ -74,7 +74,7 @@ Same 50–950 structure as primary, derived via `color-mix` on `--p-secondary`, ### Danger / Warn / Success / Info / Help / Accent palettes -Each has a base var and an 11-step scale (50–950) derived via `color-mix`, same pattern as primary. +Each has an independent base var and an 11-step scale (50–950) derived via the canonical `color-mix` formulas in the generated `@wippy-fe/theme` token manifest. Do not assume that every family uses the primary percentage ladder. | Family | Base variable | Default color | Purpose | |--------|--------------|---------------|---------| @@ -85,13 +85,13 @@ Each has a base var and an 11-step scale (50–950) derived via `color-mix`, sam | `help` | `--p-help` | `rgb(168, 85, 247)` (purple-500) | Help, hints | | `accent` | `--p-accent` | `rgb(20, 184, 166)` (teal-500) | Highlights, special callouts | -Override the base var to retheme the full scale — the 50–950 range auto-derives via `color-mix`. No dark-mode override block is needed. +Override a family base to retheme only that family; its 50–950 range auto-derives via `color-mix`. Changing `--p-primary` must not recolor danger, success, warn, info, help, secondary, or accent. Use `@light` / `@dark` only when a family needs different mode-specific values. Direct shade and alias overrides are supported for deliberate exceptions. ### The token grammar (predictable naming) The `--p-*` set follows one small, exceptionless grammar, so a human — or an AI agent generating styles — can *predict* a token name instead of looking it up. Two layers with a hard contract: -- **Numeric scale** — `--p--{50..950}` and `--p-surface-{0..950}`: the fixed-hue anchor, **never theme-switchable** (identical in light and dark). Use it only when you explicitly do *not* want the value to flip. +- **Numeric scale** — `--p--{50..950}` and `--p-surface-{0..950}`: canonical shade anchors. Defaults may be formulas or mode-specific values, and configured `@light` / `@dark` maps may override a base or an individual shade. Use semantic aliases for ordinary UI meaning. - **Semantic aliases** — `--p--color` / `-contrast-color` / `-hover-color` / `-active-color`: the theme-switchable layer. `-color` always ships with its `-contrast-color` (the color to place on top of it) plus hover/active states, so no `dark:` pairing is needed. Those four aliases exist for **all eight** families (`primary`, `secondary`, `danger`, `success`, `warn`, `info`, `help`, `accent`) — zero per-family exceptions. Typography follows the same shape (`--p-font--`, below). The generated `tokens.json` manifest shipped in `@wippy-fe/theme` (name, layer, light/dark value, flip flag) is the machine-readable ground truth an agent can load. @@ -154,7 +154,7 @@ Which font *files* load (families, weights, `size-adjust`, ascent/descent overri ## Reference — Dark mode -Variables switch at `@media (prefers-color-scheme: dark)`. Key changes: +Auto mode follows `prefers-color-scheme`. Forced mode uses the host-applied `.w-theme-light` / `.w-theme-dark` classes. The generated defaults and configured-variable layer support both paths. Key changes: - `--p-primary` base shifts from `rgb(0, 95, 178)` to `rgb(0, 125, 178)` (brighter) - `--p-primary-color` shifts from `primary-500` to `primary-400` @@ -173,17 +173,28 @@ Variables switch at `@media (prefers-color-scheme: dark)`. Key changes: If the brand requires explicit light and dark palette values, define them in facade or page-level `cssVariables`, not in module CSS. -In backend `css_variables` YAML, use `@light` / `@dark` keys: +In backend `css_variables` YAML, use top-level values for all modes and `@light` / `@dark` for selected replacements: ```yaml css_variables: "--p-primary": "#005fb2" + "--p-secondary": "#6f7385" + "--p-danger": "#dc2626" "@light": "--p-content-background": "#fafafa" "@dark": "--p-content-background": "#1c1a19" ``` +The compiler emits effective Auto-light, Auto-dark, forced Light, and forced Dark blocks. For CSS intentionally authored for both a page root and a possible Web Fragment host, use paired anchors: + +```css +:root.w-theme-dark, +:host(.w-theme-dark) { /* forced-dark rule */ } +``` + +A component that is always a shadow-DOM web component may use `:host(.w-theme-dark)` alone. Do not put palette configuration in module-level `:root`; use facade or page `cssVariables`. + --- ## Reference — Tailwind severity utility classes diff --git a/en/frontend/micro-frontends/web-component-theming.md b/en/frontend/micro-frontends/web-component-theming.md index a475859c..25c09553 100644 --- a/en/frontend/micro-frontends/web-component-theming.md +++ b/en/frontend/micro-frontends/web-component-theming.md @@ -13,20 +13,22 @@ description: "Theming reference covers the full CSS variable catalog. This doc c Shadow DOM blocks CSS cascade — stylesheets written outside your component do not apply inside it. However, CSS custom properties (variables) **do** cross the shadow boundary. This means: -- `--p-primary-500` and all other `--p-*` vars from the host are available inside your shadow root automatically — no configuration needed. +- Custom properties inherit across the shadow boundary. WippyElement also bridges every configured variable name through its forced-theme inner root, so locally loaded `theme-config.css` defaults cannot reset configured values. - PrimeVue component styles, Tailwind utilities, and other rule-based stylesheets do **not** cascade in — you must load them explicitly via `hostCssKeys`. --- ## Customization levels -**L1 — Global:** CSS custom properties (`--p-*` vars) cross the shadow boundary automatically. No action needed to receive L1 vars inside your WC. +**L1 — Global:** CSS custom properties cross the shadow boundary. WippyElement enumerates the effective global/children/page variable maps, including `@light` / `@dark`, and installs a generic inheritance bridge before the injected custom CSS layer. **L2 — Scoped:** Same as L1 for custom properties. Stylesheet-based CSS (PrimeVue, Tailwind) does not cascade — use `hostCssKeys` to load these explicitly into the shadow root. -**L3 — Per-page config_overrides:** CSS vars set via operator `config_overrides` also reach your shadow root as custom properties, because they are set on `:root` of the host page. +**L3 — Per-page config_overrides:** CSS vars set via operator `config_overrides` reach the WC host and inner theme root through the same generic bridge. -**Facade `custom_css` reaches the shadow root (Web Host 1.0.43+, opt-out).** Selector rules (e.g. `.p-button { border-radius: 12px }`) do not *cascade* across the shadow boundary, but the WC runtime **injects** the composed backend facade CSS (`custom_css` + `children_custom_css`) into every component's shadow root at mount — so they *do* apply to PrimeVue components rendered inside. This is on by default; opt out with frontend `customCss: false` in `wippyConfig` for a fully self-styled component. Custom properties (`--p-*`) inherit regardless of the flag. +**Facade `custom_css` reaches the shadow root (Web Host 1.0.43+, opt-out).** Selector rules do not cascade across the boundary, so the runtime injects composed global + children custom CSS. + +The configured-variable bridge is independent of the frontend `customCss` opt-out and remains active. Ordering is platform theme defaults → configured-variable inheritance bridge → injected custom CSS. > **Before Web Host 1.0.43**, facade `custom_css` rules did not reach a component's shadow root — only custom properties inherited. On older hosts, replay the rule inside the WC's own styles or lift it to a `--p-*` token form. @@ -111,7 +113,7 @@ This provides the default `--p-*` values so your component renders correctly in ## Writing component CSS -Use semantic vars — they flip with dark mode automatically: +Request `themeConfigUrl`, consume semantic vars, and do not redeclare inherited palette defaults. Semantic aliases switch with Auto and forced modes: ```css :host { @@ -184,12 +186,17 @@ hostCssKeys: ['themeConfigUrl'] as const ## Verifying -To confirm theme variables reach your shadow root: in DevTools, select your custom element's shadow root context (not the outer document), then run: +Do not stop at a non-empty token. Compare the exact configured value on the element host and inner theme root, then verify the browser-resolved color used by the rendered control: ```js -getComputedStyle(document.querySelector('your-element')).getPropertyValue('--p-primary-color') +const el = document.querySelector('your-element') +const inner = el.shadowRoot.querySelector('[data-wippy-theme-root]') +getComputedStyle(el).getPropertyValue('--p-primary-color') +getComputedStyle(inner).getPropertyValue('--p-primary-color') ``` +Repeat across every configured family in Auto-light, Auto-dark, forced Light, and forced Dark. A WC requests `themeConfigUrl` and consumes semantic tokens; it does not redeclare inherited palette defaults. + Full debugging workflow: [Debugging](./debugging.md). --- diff --git a/en/frontend/web-host/css-injection.md b/en/frontend/web-host/css-injection.md index 4c020be3..2411bf7e 100644 --- a/en/frontend/web-host/css-injection.md +++ b/en/frontend/web-host/css-injection.md @@ -13,13 +13,13 @@ This page documents the injection pipeline, all available flags, and how to cust The facade exposes theming through three scopes — **global** (`custom_css`, `css_variables`, `icon_sets`), **host** (`host_custom_css`, `host_css_variables`, `host_icon_sets`), and **children** (`children_custom_css`, `children_css_variables`). The Web Host composes them per surface. Two rules govern everything below: -- **CSS custom properties (`*_css_variables`) inherit across the shadow boundary.** A web component's shadow root sees the `--p-*` (and any other) custom properties set on the `:root` of the document it is mounted in — no injection needed. +- **CSS custom properties (`*_css_variables`) inherit to a WC host and are bridged through its forced-theme inner root.** WippyElement enumerates every effective configured name so local theme defaults cannot reset it. This is generic and independent of `customCss`. - **CSS selector rules (`*_custom_css`) do not cascade across the shadow boundary.** They apply only where they are injected: into each iframe document for `view.page`, and — **as of Web Host 1.0.43** — into each `view.component` shadow root (opt-out via the component's `customCss` flag). Before 1.0.43, only variables reached it. | Facade knob | Delivers | Host shell doc | `view.page` iframe | `view.component` shadow root | |---|---|---|---|---| | `custom_css` (global) | selector rules | ✓ injected | ✓ injected¹ | ✓ injected (1.0.43+, opt-out)¹ | -| `css_variables` (global) | custom properties | ✓ `:root` | ✓ `:root` | ✓ inherits in | +| `css_variables` (global) | custom properties | ✓ effective mode blocks | ✓ effective mode blocks | ✓ inherited + bridged | | `host_custom_css` (host) | selector rules | ✓ injected | ✗ | ✗ | | `host_css_variables` (host) | custom properties | ✓ `:root` | ✗ | host-mounted WCs only² | | `children_custom_css` (children) | selector rules | ✗ | ✓ injected¹ | ✓ injected (1.0.43+, opt-out)¹ | @@ -45,7 +45,7 @@ Short answer for "CSS injection order" questions: the view.page iframe style pip tailwind.css — Tailwind utility classes (same bundle as primevue.css) 3. iframe.css — Default themed scrollbar styling (historical name; no iframe layout reset) 4. markdown.css — .data-body rendering styles for Markdown content -5. cssVariables — :root { --key: value } from AppConfig.theming.global.cssVariables (adopted stylesheet) +5. cssVariables — effective base + Auto/forced mode blocks from AppConfig.theming.global.cssVariables (adopted stylesheet) 6. customCSS — Raw CSS from the child-projected AppConfig.theming.global.customCSS (adopted stylesheet) ``` @@ -106,7 +106,7 @@ meta: | `primevue` | `true` | `primevue.css` + `tailwind.css` — PrimeVue component styles and Tailwind v3 utilities (~455 KB combined). Disable only while the entire artifact has no PrimeVue-like product UI. Framework choice alone is not an exception. | | `markdown` | `true` | `markdown.css` — `.data-body` markdown rendering styles used by chat artifact display. | | `customCss` | `true` | The `customCSS` string from the child-projected `AppConfig.theming.global`. | -| `customVariables` | `true` | The `cssVariables` map from the child-projected `AppConfig.theming.global`, injected as `:root { --key: value; }`. | +| `customVariables` | `true` | The child-projected `cssVariables` map, compiled as effective base, Auto-light/dark, and forced Light/Dark blocks for every configured custom-property name. | There is no dedicated fonts flag. Google Fonts are delivered through `theming.global.customCSS` (an `@import` rule), which the iframe injects via the existing `customCss` flag. @@ -151,7 +151,7 @@ With both disabled the page still receives `customCSS`, `cssVariables`, and `ifr Web components do not go through the iframe injection pipeline. Two channels bring the theme into a component's shadow root: -- **Facade custom CSS (automatic, opt-out — Web Host 1.0.43+).** The `@wippy-fe/webcomponent-core` runtime installs the facade custom CSS composed for the component — **global + children** (`custom_css` + `children_custom_css`) — in its shadow root so it wins over component styling. The runtime may use an adopted stylesheet or a `