Context
Operator-reported (2026-06-01), live portal: deep-linking the pulls view renders with no styling:
#/news/pulls/12113 → unstyled timeline.
- Navigate to
#/news/tickets/12114, then back to the pull → now styled.
Diagnosis (operator): "pulls have a HARD dependency on a different module for theming without requiring it." Confirmed.
The Problem
The GitHub-style timeline palette (--gh-bg-default, --gh-timeline-border, --gh-text-*, …) is defined under the global :root .neo-theme-neo-{light,dark} selector — but only inside the tickets and discussions leaf theme files (resources/scss/theme-neo-{light,dark}/apps/portal/news/{tickets,discussions}/Component.scss). The pulls theme file defines only its own PR badge colors and never defines (nor loads) the shared palette.
Neo loads theme CSS per component via component/Abstract.afterSetWindowId → Neo.currentWorker.insertThemeFiles(windowId, __proto__), which recurses up the prototype chain (src/worker/App.mjs insertThemeFiles). The pulls component's proto-chain is pulls.Component → Portal.view.content.Component → …; it does not include tickets/discussions. So a standalone pulls load never pulls in the palette. Visiting tickets/discussions first defines the :root vars globally, which is why back-navigation appears styled.
The Fix
Move the shared --gh-* palette to the theme of the shared timeline base Portal.view.content.Component (which tickets, pulls, and discussions all extend). It then loads for all three via the existing proto-chain insertThemeFiles recursion — making the dependency explicit and correct (the base every timeline view requires), instead of an implicit reliance on a sibling view having loaded first.
- NEW
resources/scss/theme-neo-{light,dark}/apps/portal/content/Component.scss — the shared --gh-* palette at :root .neo-theme-neo-{light,dark}.
- Remove the now-redundant shared-palette block from the tickets + discussions leaf themes (keep their leaf-specific vars, e.g. discussions'
--gh-discussion-category-*).
- pulls leaf theme unchanged (its PR badge colors stay).
Bundled cleanup (operator-requested)
examples/calendar/weekview/MainContainer.mjs demonstrates the "one component loads an unrelated module's theme" pattern but is broken: it calls Neo.worker.App.insertThemeFiles(this.appName, null, 'Neo.calendar.view.MainContainer') in construct — passing appName where the first param is windowId. Fix to the reactive, correct shape:
afterSetWindowId(value, oldValue) {
super.afterSetWindowId?.(value, oldValue);
value && Neo.worker.App.insertThemeFiles(value, null, 'Neo.calendar.view.MainContainer')
}Acceptance Criteria
Out of Scope
- Chunk-folder sort order (#12309 / GPT's #12311).
- Discussions offscreen-canvas timeline bug, avatar full-size fetch, news tab order, discussions open/closed tree state — separate tickets.
Related
- Portal news chunked-nav track (epic #12204); pulls nav PR #12307.
Context
Operator-reported (2026-06-01), live portal: deep-linking the pulls view renders with no styling:
#/news/pulls/12113→ unstyled timeline.#/news/tickets/12114, then back to the pull → now styled.Diagnosis (operator): "pulls have a HARD dependency on a different module for theming without requiring it." Confirmed.
The Problem
The GitHub-style timeline palette (
--gh-bg-default,--gh-timeline-border,--gh-text-*, …) is defined under the global:root .neo-theme-neo-{light,dark}selector — but only inside the tickets and discussions leaf theme files (resources/scss/theme-neo-{light,dark}/apps/portal/news/{tickets,discussions}/Component.scss). The pulls theme file defines only its own PR badge colors and never defines (nor loads) the shared palette.Neo loads theme CSS per component via
component/Abstract.afterSetWindowId → Neo.currentWorker.insertThemeFiles(windowId, __proto__), which recurses up the prototype chain (src/worker/App.mjsinsertThemeFiles). The pulls component's proto-chain ispulls.Component → Portal.view.content.Component → …; it does not include tickets/discussions. So a standalone pulls load never pulls in the palette. Visiting tickets/discussions first defines the:rootvars globally, which is why back-navigation appears styled.The Fix
Move the shared
--gh-*palette to the theme of the shared timeline basePortal.view.content.Component(which tickets, pulls, and discussions all extend). It then loads for all three via the existing proto-chaininsertThemeFilesrecursion — making the dependency explicit and correct (the base every timeline view requires), instead of an implicit reliance on a sibling view having loaded first.resources/scss/theme-neo-{light,dark}/apps/portal/content/Component.scss— the shared--gh-*palette at:root .neo-theme-neo-{light,dark}.--gh-discussion-category-*).Bundled cleanup (operator-requested)
examples/calendar/weekview/MainContainer.mjsdemonstrates the "one component loads an unrelated module's theme" pattern but is broken: it callsNeo.worker.App.insertThemeFiles(this.appName, null, 'Neo.calendar.view.MainContainer')inconstruct— passingappNamewhere the first param iswindowId. Fix to the reactive, correct shape:afterSetWindowId(value, oldValue) { super.afterSetWindowId?.(value, oldValue); value && Neo.worker.App.insertThemeFiles(value, null, 'Neo.calendar.view.MainContainer') }Acceptance Criteria
#/news/pulls/<n>directly (no prior tickets/discussions visit) renders the fully-styled PR timeline (borders, backgrounds, text colors) in both light and dark themes.--gh-*palette is defined once, inPortal.view.content.Component's theme; tickets/pulls/discussions all resolve it via the proto-chain. No leaf re-defines the shared set.npm run build-themes -- -n -e dev -t all) regenerates the content-base theme CSS containing the palette, and the generated tickets/pulls/discussions CSS still resolve every--gh-*var.examples/calendar/weekview/MainContainer.mjsusesafterSetWindowIdwith the correctwindowIdarg (noappName-as-windowId).Out of Scope
Related