Skip to content

fix(ui): restore shell-nav + sidebar-shell wrappers — sidebar layout broken after #2501 migration #2517

@alexey-pelykh

Description

@alexey-pelykh

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

  • Sidebar nav is fully visible: all 4 groups (Chat / Control / Agent / Settings) render with their child tabs and the Resources section at the bottom
  • No clipping; sidebar scrolls if content exceeds viewport height
  • Topbar-right (Health/Version pills, theme toggle, chat controls) re-appears (this should be a side-effect of fixing the grid layout)
  • Chat panel content area renders (input field at bottom, messages area above)
  • pnpm tsgo, pnpm check, pnpm test all green
  • pnpm 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
  • Visual smoke check on deployed remote: load /chat, /overview, /agents in light + dark themes — full sidebar visible in all

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions