Skip to content

defineStack({ themes }) is parsed, ingested and never applied — the last hop from a stored theme item to the theme engine does not exist #10485

Description

@os-elon

Found while documenting the theming surface for #10237 (docs PR #10483). Filed, not fixed — out of that card's scope, and the fix is a decision about direction rather than a mechanical edit.

What is live, measured on origin/main at f094214b3

LayerStateEvidence
Authoring gateliveObjectStackDefinitionSchema.themes (packages/spec/src/stack.zod.ts:339) and defineTheme(); every sub-block is strict (ADR-0078), so an undeclared key is a loud rejection
Artifact ingestliveARTIFACT_FIELD_TO_TYPE maps themes to theme (packages/metadata/src/plugin.ts:76); items are saved and registered like any sibling kind
CSS emission enginelivegenerateThemeVars / mergeThemes / resolveThemeInheritance / resolveMode in objectui packages/core/src/theme/ThemeEngine.ts, with unit tests
Provider that applies themlive but unmountedobjectui packages/react/src/context/ThemeContext.tsx takes a themes array, resolves extends, injects the variables and sets the mode class
A reader that connects the twoabsentsee below

What is missing

  1. No framework package reads the collection or the item. Across packages/core, packages/runtime, packages/rest, packages/services and packages/plugins, a grep for .themes and for a 'theme' metadata item returns zero non-test hits.
  2. theme is not a registered metadata type. The string theme appears zero times in packages/spec/src/kernel/metadata-plugin.zod.ts — it is not in MetadataType, not in DEFAULT_METADATA_TYPE_REGISTRY, and not in BUILTIN_METADATA_TYPE_SCHEMAS. So resolveOverlaySchema('theme', …) returns undefined and the runtime metadata REST door does not validate a stored theme row. theme.zod.ts already says this about itself and correctly claims only the authoring gate; what is new here is that nothing downstream compensates.
  3. No first-party app mounts the spec-aware provider. The only ThemeProvider mounted anywhere in objectui is in packages/app-shell/src/console/ConsoleShell.tsx, and it is app-shell/chrome/ThemeProvider — a light/dark/system class toggle with no relationship to ThemeSchema. The console never fetches theme metadata.
  4. Nothing selects a theme.AppSchema has no theme key; theme there is an alias pointing at branding. There is no stack-level or app-level way to say which theme is active.

Net: an author can write a theme, os build will ship it, every gate is green, and the running console looks exactly the same. This is the shape #10222 found on stack.tools.

What does colour a console today

app.branding.primaryColor / accentColor, read by objectui packages/layout/src/AppShell.tsx, converted hex-to-HSL and written onto --primary, --primary-foreground, --ring, --sidebar-primary, --sidebar-ring, --accent, --accent-foreground, re-derived on the light/dark flip. That path is live and unaffected by this issue.

Why this needs a decision, not a patch

Under ADR-0049 enforce-or-remove there are two honest directions, and they are not equivalent:

  • Enforce — register theme as a metadata type, add a selection key (app-level or stack-level), and have the console read the active theme and mount the spec-aware provider. This is a real feature, not a wiring fix: it needs a decision about scoping (per app? per org? per user?) and about precedence against app.branding, which already writes several of the same variables.
  • Remove — retire the themes carrier key and the schema, leaving app.branding as the one colour surface, with customVars-style escape hatches folded in if needed.

Given the startup focus principle (maintainer, 2026-08-04), "enforce" should not be assumed just because the engine already exists — most of the machinery is built, but the product question of what a theme scopes to is untouched.

Interim mitigation already landed

content/docs/ui/theming.mdx (PR #10483) documents the surface with a warn callout stating exactly this, and points readers at app.branding for the "recolour the shipped console" case, so the docs do not advertise a capability the runtime does not deliver.

Related

  • theme.zod.ts records the same liveness question one level down: typography scales, animation and zIndex were retired at 主题引擎发出的 9 组 CSS 变量零消费方(--font-size-* / --z-* / --duration-* …):ADR-0049 该判去留 #5021 precisely because they were emitted and read by nobody. The keys that survived — colors, borderRadius, shadows, typography.fontFamily.base — have consumers in the engine; this issue is about the engine itself having no caller for authored metadata.
  • objectui also declares a ThemeComponentSchema (type: 'theme', packages/types/src/theme.ts) with no renderer — a separate, smaller dead surface on that repo's side.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions