Problem
After #2501 / #2509 (which migrated app-render.ts to upstream's .sidebar / .nav-section class vocabulary), the sidebar nav is clipped to a single visible group header on deployed builds. Reproducer screenshot shows only CHAT group header + the start of the Chat tab; all subsequent groups (Control / Agent / Settings / Resources) are hidden by overflow: hidden. The topbar-right (Health/Version pills, theme toggle, chat controls) is also missing, and the chat panel content area is empty.
Root cause
The migration adopted upstream's CSS class names but did not adopt upstream's markup structure. Upstream's .sidebar rule is designed to be wrapped in .shell-nav (which carries grid-area: nav) and to internally use .sidebar-shell (the flex container that gives .sidebar's flex: 1 something to fill). Without these wrappers the fork's <aside class="sidebar"> is auto-placed by CSS Grid into the wrong cell and overflow: hidden clips the nav-sections.
Upstream structure (openclaw/main:ui/src/ui/app-render.ts:1270-1322):
<div class="shell-nav"> <!-- grid-area: nav (rule at layout.css:311) -->
<aside class="sidebar ...">
<div class="sidebar-shell"> <!-- flex column container -->
<div class="sidebar-shell__header">
<div class="sidebar-brand">...</div>
<button class="nav-collapse-toggle">...</button>
</div>
<div class="sidebar-shell__body">
<nav class="sidebar-nav"> <!-- semantic nav element -->
<section class="nav-section">...</section>
...
</nav>
</div>
</div>
</aside>
</div>
Fork's current structure (ui/src/ui/app-render.ts:251-300):
<aside class="sidebar ..."> <!-- direct grid child; no grid-area set -->
<div class="nav-section">...</div> <!-- no .sidebar-shell wrapper -->
...
</aside>
CSS rules at stake (ui/src/styles/layout.css:311, 324):
.shell-nav {
grid-area: nav;
display: flex;
min-height: 100%;
overflow: hidden;
border-right: 1px solid ...;
}
.sidebar {
display: flex;
flex-direction: column;
flex: 1; /* meaningful only inside a flex parent (.shell-nav) */
min-height: 0;
min-width: 0;
overflow: hidden; /* clips when sidebar collapses to natural content height */
background: ...;
}
Why this is a NEW regression class
We've now seen three instances of the same family of regression caused by the v2026.3.13-1 sync:
| # |
Issue |
Variant |
| 1 |
#2493 |
Type field added in upstream → fork class missing initializer |
| 2 |
#2501 |
CSS class renamed in upstream → fork render code uses old name |
| 3 |
This issue |
CSS class renamed AND markup hierarchy restructured → fork render code adopts new names but keeps old hierarchy |
Instance #3 is the "incomplete migration" sub-pattern: a fix for instance #2 that adopted upstream's class names but missed that the same upstream commit also restructured the surrounding markup. The class-name-existence check in #2502 / #2503 does NOT catch this because the class IS defined; the failure is structural-context mismatch, not symbol drift.
Fix
Update ui/src/ui/app-render.ts:251-300 to wrap the sidebar in the missing containers. Minimum viable fix:
<div class="shell-nav">
<aside class="sidebar ${state.settings.navCollapsed ? "sidebar--collapsed" : ""}">
<div class="sidebar-shell">
<div class="sidebar-shell__body">
<nav class="sidebar-nav">
${TAB_GROUPS.map((group) => { /* existing nav-section rendering */ })}
<div class="nav-section nav-section--links">
${/* existing resources links */}
</div>
</nav>
</div>
</div>
</aside>
</div>
Optional improvements (nice-to-have, not required for fix):
- Add
.sidebar-shell__header containing brand + collapse-toggle (currently in topbar-left; upstream moved them into sidebar header)
- Change inner
<div class="nav-section"> to <section class="nav-section"> for semantic correctness
- Consider
.sidebar-shell__footer if future content warrants it
Acceptance criteria
Pattern note for retrospective
Fix for #2501 used "option 2: migrate to upstream class names" without explicitly mandating "match upstream's markup hierarchy completely". The implementer (rightly) focused on visible symptoms (class names, chevron icon, label style) without realizing the parent-container hierarchy was also part of upstream's design.
This suggests two process-level improvements (separately tracked):
- HQ companion: Migration completeness checklist — when adopting upstream class vocabulary, mandate verification of markup hierarchy not just class names
- Code-repo companion: Computed-style or visual-regression smoke test that would catch "rendered but clipped" symptoms (the existing
#2495 smoke test instantiates the class but doesn't measure layout)
Commit message
fix(ui): restore shell-nav + sidebar-shell wrappers on RemoteClawApp sidebar — sidebar markup mismatch with upstream CSS after #2501 migration
References
- Regression introduced by:
#2501 (nav-section migration) + #2509 (theme-orb migration)
- Same regression class:
#2493 (type field), #2501 (CSS rename), this issue (markup structure)
- Pattern post-mortem:
remoteclaw/hq#57 — third instance to add to evidence
- Risk model:
remoteclaw/hq#59
- Class-existence audit (didn't catch this):
#2502, #2503
- Personal Claude
fork-sync skill: section to be extended with "structural-mismatch" sub-pattern
Problem
After #2501 / #2509 (which migrated
app-render.tsto upstream's.sidebar/.nav-sectionclass vocabulary), the sidebar nav is clipped to a single visible group header on deployed builds. Reproducer screenshot shows onlyCHATgroup header + the start of theChattab; all subsequent groups (Control / Agent / Settings / Resources) are hidden byoverflow: hidden. The topbar-right (Health/Version pills, theme toggle, chat controls) is also missing, and the chat panel content area is empty.Root cause
The migration adopted upstream's CSS class names but did not adopt upstream's markup structure. Upstream's
.sidebarrule is designed to be wrapped in.shell-nav(which carriesgrid-area: nav) and to internally use.sidebar-shell(the flex container that gives.sidebar'sflex: 1something to fill). Without these wrappers the fork's<aside class="sidebar">is auto-placed by CSS Grid into the wrong cell andoverflow: hiddenclips the nav-sections.Upstream structure (
openclaw/main:ui/src/ui/app-render.ts:1270-1322):Fork's current structure (
ui/src/ui/app-render.ts:251-300):CSS rules at stake (
ui/src/styles/layout.css:311, 324):Why this is a NEW regression class
We've now seen three instances of the same family of regression caused by the v2026.3.13-1 sync:
#2493#2501Instance #3 is the "incomplete migration" sub-pattern: a fix for instance #2 that adopted upstream's class names but missed that the same upstream commit also restructured the surrounding markup. The class-name-existence check in
#2502/#2503does NOT catch this because the class IS defined; the failure is structural-context mismatch, not symbol drift.Fix
Update
ui/src/ui/app-render.ts:251-300to wrap the sidebar in the missing containers. Minimum viable fix:Optional improvements (nice-to-have, not required for fix):
.sidebar-shell__headercontaining brand + collapse-toggle (currently in topbar-left; upstream moved them into sidebar header)<div class="nav-section">to<section class="nav-section">for semantic correctness.sidebar-shell__footerif future content warrants itAcceptance criteria
pnpm tsgo,pnpm check,pnpm testall greenpnpm lint:css-classes(from feat(ci): CSS class consistency build-time gate — fail build on undefined class references #2503) still passes — verify the new wrapper classes exist in CSS/chat,/overview,/agentsin light + dark themes — full sidebar visible in allPattern note for retrospective
Fix for #2501 used "option 2: migrate to upstream class names" without explicitly mandating "match upstream's markup hierarchy completely". The implementer (rightly) focused on visible symptoms (class names, chevron icon, label style) without realizing the parent-container hierarchy was also part of upstream's design.
This suggests two process-level improvements (separately tracked):
#2495smoke test instantiates the class but doesn't measure layout)Commit message
References
#2501(nav-section migration) +#2509(theme-orb migration)#2493(type field),#2501(CSS rename), this issue (markup structure)remoteclaw/hq#57— third instance to add to evidenceremoteclaw/hq#59#2502,#2503fork-syncskill: section to be extended with "structural-mismatch" sub-pattern