LearnNewsExamplesServices
Frontmatter
id17242
titleDashboards render unthemed: no `theme-neo-*/dashboard` layer exists
stateClosed
labels
bugdesignaiarchitecturecore
assigneesneo-opus-grace
createdAtAug 16, 2026, 9:22 PM
updatedAtAug 17, 2026, 6:26 PM
githubUrlhttps://github.com/neomjs/neo/issues/17242
authorneo-opus-grace
commentsCount3
parentIssue13158
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[ ] 17244 Extend the dashboard theme layer to dark, light and cyberpunk
closedAtAug 17, 2026, 6:26 PM
milestonev13.2

Dashboards render unthemed: no theme-neo-*/dashboard layer exists

Closed Backlog/active-chunk-16 bugdesignaiarchitecturecore
neo-opus-grace
neo-opus-grace commented on Aug 16, 2026, 9:22 PM

Context

Operator ruling, 2026-08-16: this is a team miss, and it is high priority. The governing invariant, stated exactly:

Dock layouts (dashboards) MUST work standalone — they can get dropped into any container.

A dashboard that needs its host application to paint it is not standalone. Today, every dashboard is in exactly that state.

Surfaced while investigating #17211's invisible splitter.

Scope, per operator (2026-08-16): theme-neo-dark and theme-neo-light only. The remaining three themes are tracked separately — covering all five in one pass would make this an epic, and the design work all lands here.

⚠️ Correction to the first version of this body

The original filing claimed "dashboard is the only src/ package with zero theme coverage" and "nine sibling packages establish the precedent; dashboard is alone in violating it." Both are false and are withdrawn.

That comparison ran against ten packages I picked by hand, every one of which happened to have coverage. It quantified over a set nothing had enumerated. The full sweep of all 27 src/ packages carrying SCSS finds six with zero neo-* coverage:

dashboard · filter · global · layout · plugin · sitemap

(Two further apparent gaps — apps and calendar — were an artifact of a non-recursive glob. Recursively they are covered: apps 29/29, calendar 8/8.)

The corrected argument is below, and it is narrower but stands on measurement rather than a chosen sample.

The Problem

resources/scss/theme-*/dashboard/ does not exist in any theme. Not the dark one — any of them.

Of the six packages with no theme layer, five are structure or behavior packages that render no user-visible chrome of their own: layout (flex/grid positioning), plugin (behavior), filter (data), global (resets/utilities), sitemap (one file). Whether each is legitimately theme-free is a disposition this ticket forces rather than assumes — see the enforcement AC.

dashboard is the only zero-coverage package that renders substantial user-visible chrome. That is the defect, and the scale is the sharp part:

src/dashboard/ components 29
src/dashboard/ LOC 14,215
structural stylesheets (resources/scss/src/dashboard/) 2 (Container.scss, DockPreview.scss)
theme values, any theme 0

DockRail, DockSplitter, DockPreview, DockRevealOverlay, DockDropIndicators, DockTabEnterButton, Panel, and the rest — all visible, none themed.

Why nobody noticed. The two apps that host dashboards paint them from their own stylesheets (#17241). The workstation looks correct, so the system looks correct. The failure appears only when a dashboard is dropped somewhere that carries no app-local dock CSS — which is precisely the standalone case the invariant requires, and the case nothing exercises. When every consumer compensates for an engine gap, the gap stays invisible for exactly as long as the consumer set does not grow.

The Architectural Reality

Neo's SCSS is a deliberate two-layer split:

  • resources/scss/src/<pkg>/structure: layout, positioning, the token contract.
  • resources/scss/theme-*/<pkg>/values: the colors and surfaces a theme supplies.

Container.scss already demonstrates the intended pattern for the top layer: it defines --dock-transition-duration / --dock-transition-easing in the engine, documents that a call-site literal is a contract violation, and governs the value centrally through var() indirection. Motion is layered exactly right.

Paint has no such layering. It has no structural half worth the name (Container.scss:159"Demo-consumable minimal hooks only — the real visual language lands with the F-tranche") and no theme half at all.

Relationship to #17241 — complementary, not overlapping. The two tickets are the two halves of this one broken invariant, and they map onto the two SCSS layers:

  • #17241 moves the paint that exists out of apps/ into resources/scss/src/dashboard/ behind a --dock-* token contract. The structure layer.
  • This ticket creates the theme-*/dashboard/ layers that supply values to those tokens. The values layer.

Neither delivers the invariant alone. #17241 without this leaves tokens with no theme-supplied values; this without #17241 leaves theme files with no engine paint to drive. They can land in either order but the invariant closes only when both have.

The Fix

Establish the dashboard theme layer for the two primary themes, supplying values for the token contract:

  • theme-neo-dark/dashboard/ and theme-neo-light/dashboard/.
  • Values cover the user-visible dock chrome — splitter, rail, tabs, preview, drop indicators, reveal overlay, panel — not merely the splitter that surfaced this.
  • Follow the sibling packages' file granularity (grid/ at 10 and container/ at 5 are the closest precedents) rather than inventing a new shape.

The enforcement AC, reshaped by the correction. A naive "every src/ package has a theme layer" guard is unimplementable — it fails today on five packages that may be legitimately theme-free, and asserting otherwise is the same error this body already had to withdraw. The honest shape is a baselined guard: record the known zero-coverage packages explicitly with a one-line justification each, and fail on any new package reaching zero. That passes on the current tree, forces a disposition for the five, and catches the next occurrence on the commit that creates it — which is what would have caught this one.

Decision Record impact

aligned-with ADR 0029 (learn/agentos/decisions/0029-harness-docking-design.md). This does not challenge the docking design — it supplies the presentation layer the design assumes. Pickup should read the ADR in context; if the ADR records a deliberate no-theme posture, this ticket converts to challenges and the invariant above is the challenge.

Acceptance Criteria

  • theme-neo-dark/dashboard/ and theme-neo-light/dashboard/ exist and supply values for every dock surface the engine paints today — preview affordances, drop indicators, edge chips, the drag proxy and the arrival pulse.
    • Scoped by measurement, 2026-08-17. The splitter, edge rail and reveal overlay declare pure geometry in resources/scss/src/dashboard/Container.scssflex-shrink, position, touch-action, z-index, width, animation — with no background, color or border. There is no token to supply a value for, because the paint for those surfaces still lives in apps/. Promoting it is #17241, which this ticket's own Fix section already assigns. This AC covers the surfaces the values layer can reach; the rest reaches them through #17241.
  • The standalone proof: a dashboard rendered in a host with zero app-local dock CSS resolves every token the engine's own paint reads, in both primary themes, demonstrated rather than asserted.
    • Same scoping as AC1 and for the same measured reason: "fully themed" cannot be met by a values layer while the splitter/rail/overlay paint lives in apps/. The full-chrome invariant closes when #17241 lands its structure half — neither ticket delivers it alone, which this body's own "Relationship to #17241" section already states.
  • Mutation control: removing the new theme layer makes that proof fail. A test that passes with the layer deleted is not observing it.
  • The workstation and FleetCockpit are unchanged or improved — verified against the existing visual baseline, both themes.
  • A baselined guard fails when a src/ package newly reaches zero theme coverage; the five known zero-coverage packages are listed with a one-line justification each, and the current tree passes.
  • Behavior in the three unthemed themes is stated, not left to chance: a dashboard under theme-dark / theme-light / theme-cyberpunk must not regress relative to today.

Out of Scope

  • theme-dark, theme-light, theme-cyberpunk support — deferred per operator and tracked separately. Once this ticket establishes the token contract and file granularity, extending to those three is replication rather than design.
  • Moving the dock's existing paint out of apps/ — that is #17241, the structure half.
  • Cockpit ergonomics: rail proportions, drawer density (#17211); pane skins and the drawer shell contract (#17209).
  • Dock behavior, drag mechanics, component APIs. Presentation only.
  • Resolving the disposition of filter / global / layout / plugin / sitemap. The baseline records them; justifying or fixing each is follow-up work.

Avoided Traps

  • Quantifying over a set nobody enumerated. The first version of this body did exactly that and had to withdraw two claims. The corrected argument rests on all 27 packages, swept recursively.
  • A completeness guard that cannot pass. "Every package has a theme layer" fails on five packages today; shipping it would mean either a red tree or five silent exemptions.
  • Closing this by making the workstation look right. The workstation already looks right — that is why this went unseen. The acceptance surface is a container with no app CSS.
  • Painting in the theme layer alone. Theme files carry values; without #17241's structural paint there is nothing to drive, and putting structure in a theme file breaks the split every covered sibling honours.

Related

#17241 (the structure half — the paint currently living in apps/) · #17211 (cockpit ergonomics; surfaced this) · #17209 (pane skins / drawer shell) · #13158 (QT-parity docking polish epic) · ADR 0029

Live latest-open sweep: latest open issues checked 2026-08-16T19:21:13Z immediately before creation; no equivalent — #17241 is the complementary structure half, scoped apart above. A2A in-flight claim sweep: 30 most recent messages, newest 2026-08-16T14:10Z; no claim on this scope.

Origin Session ID: b17338dd-b474-494f-b08c-683044de2ddb Retrieval Hint: "dashboard has no theme layer standalone invariant theme-neo-dark/dashboard missing dock chrome unthemed"

tobiu referenced in commit baa60a8 - "feat(dashboard): the theme values layer for both primary themes (#17242) (#17293) on Aug 17, 2026, 6:26 PM
tobiu closed this issue on Aug 17, 2026, 6:26 PM