LearnNewsExamplesServices
Frontmatter
id17950
titletab.Container ui variants: inline and standalone header chrome
stateClosed
labels
enhancementdesignaitestingcore
assigneesneo-gpt-emmy
createdAtAug 31, 2026, 3:51 PM
updatedAtAug 31, 2026, 7:16 PM
githubUrlhttps://github.com/neomjs/neo/issues/17950
authorneo-opus-vega
commentsCount1
parentIssue13158
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 31, 2026, 7:16 PM

tab.Container ui variants: inline and standalone header chrome

Closed v13.2.0/archive-v13-2-0-chunk-17 enhancementdesignaitestingcore
neo-opus-vega
neo-opus-vega commented on Aug 31, 2026, 3:51 PM

Context

Chrome half of the pane-header parity family under epic #13158 (the action half lives in the #17928 sibling leaves): dense multi-pane compositions need tab chrome designed for embedding, and the current themes only offer one look each. Motivating consumer in-repo: apps/workstation, whose dock workspace renders the neo themes' free-standing tab chrome inside pane headers where it reads oversized and disconnected. Anchors verified at Engine dev@34499f737b.

The Problem

The classic themes (theme-light / theme-dark) style tab containers for inline placement — flush strips that sit inside surrounding chrome. The two neo themes (theme-neo-light / theme-neo-dark) style free-standing tab containers — card-like, elevated, generous. Both looks are correct for their original context and wrong for the other: inside a dock workspace every tabs node is embedded in a pane header band, and the free-standing design fights the composition (workstation shows it today). There is no per-instance selector; a consumer gets whatever its theme ships.

The Architectural Reality

  • The variant mechanism already exists and costs nothing: component.Base#afterSetUi (src/component/Base.mjs:894) renders ui: 'x' as a neo-<ntype>-x class — for tab.Container, neo-tab-container-inline. No new config machinery, no projection change beyond a value.
  • Theme substrate: base sheets at resources/scss/src/tab/{Container,Strip,header/}; per-theme overrides exist (verified: theme-neo-light/tab/). Four themes declared: theme-light, theme-dark, theme-neo-light, theme-neo-dark.
  • Dock seam: the adapter projects tabs-node config at src/dashboard/dock/projection/LayoutAdapter.mjs:1004-1013 (cls: ['neo-dashboard-dock-tabs']) — one added ui value flows to every dock consumer, apps/workstation included, with zero app changes.

The Fix

Define two documented ui variants for Neo.tab.Container:

  • standalone — the free-standing look: exactly the neo themes' current design, now addressable by name.
  • inline — the embedded look: flush compact strip, reduced radius/elevation, and a header background-gradient hook, frozen here as a public surface: the structural sheet consumes background-image: var(--tab-header-inline-background-image, none) on .neo-tab-container-inline > .neo-tab-header-toolbar; theme value layers (or a consumer root) may set the property. The none default keeps today's flat paint, and ui: null containers never match the owning selector, so a globally-set hook cannot leak outside the inline variant. Dense pane headers are where a subtle band gradient earns its keep.

Naming decision (recorded): inline / standalone — over "free", which reads ambiguously (free-standing vs unconstrained).

Defaults preserve every current consumer: ui: null keeps each theme's existing look, byte-for-byte. The neo themes gain real inline styling; the classic themes map inline to (approximately) their current look and standalone to a coherent card variant, so both values mean something in all four themes. The dock adapter projects ui: 'inline' on the tabs nodes it builds — the one behavior change, scoped to dock projections, which is precisely the context whose chrome is wrong today.

Contract Ledger (consumed cells)

Cell Producer Behavior Fallback Docs Evidence
variant selector component.Base#afterSetUi (src/component/Base.mjs:894) — exists ui: 'inline' ⇒ neo-tab-container-inline on the container root; standalone likewise; null ⇒ no variant class Current look tab.Container config jsdoc documents both values Mechanism shipping engine-wide
theme styling NEW SCSS blocks under the tab sheets for all four themes Distinct, documented chrome per variant per theme; gradient hook = --tab-header-inline-background-image, consumed as background-image: var(--tab-header-inline-background-image, none) on .neo-tab-container-inline > .neo-tab-header-toolbar (the --tab-* token family) Unset hook = none = flat header, today's look; ui: null never matches the owning selector SCSS block comments name the two variants' intent + the hook Theme override structure exists (theme-neo-light/tab/)
dock projection projection.LayoutAdapter tabs-node config (LayoutAdapter.mjs:1004-1013) Projects ui: 'inline' on every projected tabs node n/a (dock chrome is the motivating context) Inline comment at the projection site Projection seam shipping; #17926/#17939 precedent for threading one option through it
non-dock consumers untouched ui: null default keeps every existing tab container byte-identical n/a Config jsdoc AC pins it

Decision Record impact

none — theming plus one projected config value; no model, operation, or ADR surface. (ADR 0029 §2.6's embedded-product-surface consumers are downstream beneficiaries, not contract parties here.)

Acceptance Criteria

  • ui: 'inline' and ui: 'standalone' render distinct, intentional chrome in theme-neo-light and theme-neo-dark; both values are also styled (not no-ops) in the classic themes. Visual evidence beat per the epic's closure-gate discipline (recorded comparison or screenshot pair per theme).
  • Default (ui: null) tab containers are byte-identical in DOM and cls to today in all four themes (regression spec).
  • The dock adapter projects ui: 'inline'; a projected dock tabs node carries neo-tab-container-inline (headless spec), and apps/workstation inherits the inline chrome with zero app-side changes.
  • The gradient hook: a runtime getComputedStyle read of --tab-header-inline-background-image proves unset ⇒ background-image: none (flat, today's paint) and set ⇒ the gradient renders on the inline header toolbar only — never on a ui: null container (both witnessed).
  • Component spec pins the cls contract for both values plus the null default.

Out of Scope

  • Redesigning either theme's standalone look — it is today's neo design, renamed.
  • Tab body styling, icon work, or the header actions (the sibling leaves).
  • Per-app theme work in consumers.

Related

Parent: #13158 (the closure gate's experience-parity matrix is where dense-composition chrome gets judged). Siblings: #17928 and the header-action leaves filed alongside. Public consumer: apps/workstation.

Origin Session ID: 914dffcd-0057-46a5-a0c6-9ca807fd5c91

Retrieval Hint: "tab container ui inline standalone variant dock header chrome gradient"

— Vega (Fable 5, Claude Code) 🌿

tobiu cross-referenced by PR #17956 on Aug 31, 2026, 5:39 PM