Skip to content

feat(front): make the left sidebar collapsible on desktop - #3055

Open
Pierre-Gilles wants to merge 4 commits into
masterfrom
claude/sidebar-drawer-mode-uljznh
Open

feat(front): make the left sidebar collapsible on desktop#3055
Pierre-Gilles wants to merge 4 commits into
masterfrom
claude/sidebar-drawer-mode-uljznh

Conversation

@Pierre-Gilles

@Pierre-Gilles Pierre-Gilles commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Description

The docked 15rem rail costs every page a column of width, which on a dashboard is width the widgets wanted. This makes the rail collapsible on desktop, as a per-device opt-in.

How it works

  • A chevron button in the rail's brand row (desktop only — below 992px the rail is an on-demand drawer whatever the preference says) collapses it: the rail slides off-canvas, the page takes the width back, and a floating frosted button in the freed 3.25rem gutter expands it back into the same docked rail.
  • Both are resting states — neither is an overlay. Expanded is exactly the rail as it exists today: docked beside the content, nothing dimmed, staying open across navigation. (A first iteration reused the mobile drawer on desktop — open meant "over a backdrop with the page dimmed" — which lost the rail open while working state; field feedback rightly called that a regression, so the overlay grammar — backdrop, Escape, close-on-navigation — stays mobile-only.)
  • The choice is a per-device localStorage preference (sidebar-collapsed), like dark mode and unlike anything account-scoped: the same user wants the rail expanded on a wide screen and out of the way on a wall tablet. No server change, no API change.
  • The expand control is a floating button, not a mobile-style top bar: a full-width bar would have cost every page a row, and would have forced every full-height page and sticky offset written against "no chrome above on desktop" to be re-derived. Nothing moves vertically; the 0.22s rail slide gets a matching padding-left transition on the page so content and rail move as one. The button carries the mobile burger's amber dot when the instance-update notice is waiting in the hidden rail.
  • Includes the review pass's a11y fixes: brand-name ellipsis next to the collapse button, ~44px coarse-pointer targets and :focus-visible rings on both rail controls, the mobile burger's open/close accessible name (it never had one), Escape closing the profile dropup before the mobile drawer via a dedicated close action.

Mobile is untouched; the default stays the expanded rail — nothing changes for anyone who does not press the button.

docs/specs/dashboard-flexible-layout-and-widgets.md § H2 (app navigation) is updated in the same diff, as the spec process requires.

Verified in the running app (Chromium, 1440×900 and 390×780):

  • expanded → padding-left: 240px, rail visible, no backdrop;
  • collapse → padding-left: 52px, rail off-canvas, no backdrop, preference persisted;
  • expand → back to the docked 240px rail, no backdrop, no dimming, the floating button gone;
  • navigation keeps the expanded rail open; the preference survives a reload;
  • no horizontal overflow on dashboard, Discussion, Intégrations, Appareils (incl. dark mode) or the new-dashboard editor; the chat page spans the full viewport in both states;
  • at 390px the desktop controls are hidden and the mobile drawer still opens over its backdrop, closed by backdrop tap or Escape.

Forum

Forum: https://community.gladysassistant.com/t/mettre-la-barre-laterale-gauche-en-mode-tiroir/10740

Checklist

  • Tests pass: cd server && npm run coverage (Codecov requires 100% coverage on changed lines) and Cypress (npm run cypress:run) if the UI changed
  • Linter and prettier pass on both front and server (npm run eslint, npm run prettier)
  • No undocumented breaking change

Notes on the checklist: the change is front-only, so no server test or coverage is involved. npm run prettier-check, npm run eslint, npm run compare-translations and npm run build all pass in front/. Cypress could not be run here — its binary download is blocked in this environment — but no spec touches the header markup (data-cy="sidebar-toggler" and sidebar-nav are preserved), so CI's run is the check that matters.

Summary by CodeRabbit

  • New Features
    • Added a collapsible desktop sidebar rail with a floating expand button.
    • Sidebar preference is remembered per device.
    • Added smooth sidebar transitions and responsive page spacing.
    • Added accessible collapse and expand labels in English, French, and German.
  • Bug Fixes
    • Improved mobile menu closing with Escape and navigation.
    • Updated chat layout to accommodate expanded and collapsed sidebar states.

The docked 15rem rail costs every page a column of width, which on a
dashboard is width the widgets wanted (community request 10740).

A chevron button in the rail's brand row undocks it: the rail then behaves
exactly as it already does on mobile — off-canvas over a backdrop, opened on
demand, closed by backdrop click, Escape or navigation — and the same button
docks it back. The choice is a per-device localStorage preference, like dark
mode: the same user wants the rail docked on a wide screen and out of the
way on a wall tablet.

The desktop drawer is opened by a floating frosted button in the 3.25rem
gutter the page keeps free on the left, not by a mobile-style top bar: a
full-width bar would have cost every page a row (and vertical space is not
what drawer mode is for) and would have forced every full-height page and
sticky offset written against "no chrome above on desktop" to be re-derived.
Nothing moves vertically, so those pages keep working untouched; the chat
page's scene slide-back follows the same 3.25rem.

Also gives the mobile burger the accessible name it never had.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JnwsS9jopHYHX3GQMViT2c
@github-actions github-actions Bot added area:front Preact front-end type:feature New user-facing feature or improvement labels Aug 28, 2026
@coderabbitai

coderabbitai Bot commented Aug 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 44a9ec76-496e-473e-b9ff-ea5055e416af

📥 Commits

Reviewing files that changed from the base of the PR and between fe26302 and 3abf632.

📒 Files selected for processing (12)
  • docs/specs/dashboard-flexible-layout-and-widgets.md
  • front/src/actions/main.js
  • front/src/components/app.jsx
  • front/src/components/header/index.jsx
  • front/src/components/header/style.css
  • front/src/config/i18n/de.json
  • front/src/config/i18n/en.json
  • front/src/config/i18n/fr.json
  • front/src/routes/chat/style.css
  • front/src/style/index.css
  • front/src/utils/getDefaultState.js
  • front/src/utils/sidebarPreference.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • front/src/config/i18n/fr.json
  • front/src/config/i18n/de.json

Included review availability: Your plan provides up to 8 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

The PR replaces desktop drawer mode with a persisted collapsible sidebar rail. Header controls update the collapsed state, responsive styles adjust page and chat spacing, and English, German, and French labels support the controls. Mobile drawer Escape handling remains supported.

Changes

Sidebar collapse mode

Layer / File(s) Summary
Preference, state, and toggle action
front/src/utils/sidebarPreference.js, front/src/utils/getDefaultState.js, front/src/actions/main.js
The collapsed-sidebar preference is read from and written to localStorage. Default state exposes sidebarCollapsed. Actions persist state changes and close the collapsed menu.
Header controls and collapsed rail
front/src/components/app.jsx, front/src/components/header/index.jsx, front/src/components/header/style.css, front/src/config/i18n/*.json
Header receives collapsed-sidebar state and actions. It manages Escape handling, body classes, desktop collapse and expand controls, mobile controls, backdrop behavior, indicators, styling, and translations.
Responsive page and chat layout
front/src/routes/chat/style.css, front/src/style/index.css
Desktop pages and chat use 15rem spacing with the expanded rail and a 3.25rem gutter with the collapsed rail. Page padding transitions with the rail.
Sidebar behavior specification
docs/specs/dashboard-flexible-layout-and-widgets.md
The specification describes the collapsible desktop rail, persisted preference, synchronized transitions, and mobile drawer Escape closing.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 3abf6

The PR makes the desktop sidebar collapsible while preserving the existing expanded default and mobile behavior. No actionable merge-blocking risk remains after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Header
  participant MainStore
  participant LocalStorage
  participant PageLayout
  User->>Header: click collapse or expand control
  Header->>MainStore: toggleSidebarCollapsed()
  MainStore->>LocalStorage: save sidebar-collapsed
  MainStore-->>Header: update sidebarCollapsed
  Header->>PageLayout: set gladys-sidebar-collapsed
  PageLayout-->>User: animate rail and page spacing
Loading

Poem

A rabbit clicks the chevron bright
The rail slides softly out of sight
Local storage keeps the state
Escape closes the mobile gate
New labels guide each menu turn
The page shifts as rails return

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 5 files. (7 skipped: 7 … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: making the left sidebar collapsible on desktop.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 5 files. (7 skipped: 7 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/sidebar-drawer-mode-uljznh

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 28, 2026

Copy link
Copy Markdown

Deploying gladys-plus with  Cloudflare Pages  Cloudflare Pages

Latest commit: 3abf632
Status: ✅  Deploy successful!
Preview URL: https://89cc8385.gladys-plus.pages.dev
Branch Preview URL: https://claude-sidebar-drawer-mode-u.gladys-plus.pages.dev

View logs

@github-actions

Copy link
Copy Markdown
Contributor

🐳 A Docker image has been built for this branch and pushed to the GitHub Container Registry.

You can test this pull request (AMD64 only) by pulling the image below:

ghcr.io/gladysassistant/gladys-preview:claude-sidebar-drawer-mode-uljznh

For example, run it with:

sudo docker run -d \
  --log-driver json-file \
  --log-opt max-size=10m \
  --cgroupns=host \
  --restart=always \
  --privileged \
  --network=host \
  --name gladys-claude-sidebar-drawer-mode-uljznh \
  -e NODE_ENV=production \
  -e SERVER_PORT=80 \
  -e TZ=Europe/Paris \
  -e SQLITE_FILE_PATH=/var/lib/gladysassistant/gladys-production.db \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /var/lib/gladysassistant:/var/lib/gladysassistant \
  -v /dev:/dev \
  -v /run/udev:/run/udev:ro \
  ghcr.io/gladysassistant/gladys-preview:claude-sidebar-drawer-mode-uljznh

This comment and the image are automatically updated on every new commit pushed to this pull request.

Need an ARM64 image (Raspberry Pi, Apple Silicon, …)? Comment /build-arm64 on this pull request.

@codecov

codecov Bot commented Aug 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.55%. Comparing base (c398895) to head (3abf632).
⚠️ Report is 3 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #3055   +/-   ##
=======================================
  Coverage   99.55%   99.55%           
=======================================
  Files        1269     1269           
  Lines       92987    92987           
=======================================
  Hits        92574    92574           
  Misses        413      413           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Pierre-Gilles Pierre-Gilles added the needs:cursor-review Automated review by Cursor is needed label Aug 28, 2026 — with Cursor
cursor[bot]
cursor Bot approved these changes Aug 28, 2026
Comment thread front/src/components/header/style.css
Comment thread front/src/components/header/style.css
Comment thread front/src/components/header/index.jsx
@Pierre-Gilles Pierre-Gilles removed the needs:cursor-review Automated review by Cursor is needed label Aug 28, 2026
cursor[bot]
cursor Bot previously approved these changes Aug 28, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Opt-in desktop drawer for the left rail, matching the existing mobile off-canvas behaviour. Default stays the docked 15rem rail; the preference is per-device localStorage (same idea as dark mode), so a wall tablet can hide the column without affecting a wide desktop session. No server, API, or DEVICE_FEATURE_* change.

The layout split is careful: body classes gladys-sidebar-nav / gladys-sidebar-drawer from the header constructor (so the first frame already has the 3.25rem gutter), page padding and .fixed-bottom in style/index.css, and the chat slide-back as the one page that still copies the 15rem trick. Below 992px the chevron and floating opener are CSS-hidden; Cypress is green and data-cy="sidebar-toggler" / sidebar-nav are unchanged. Spec H2 is updated in the same diff.

Two residuals, neither a merge blocker: Escape currently toggles rather than closes, and the new controls sit under the ~44px coarse-pointer floor this chrome already uses for the mobile burger — worth tightening because wall tablets are the reason the preference exists.

Not risk:high. Not needs:human-review.

Open in Web View Automation 

Sent by Cursor Automation: Automatic PR review

Comment thread front/src/components/header/index.jsx
Comment thread front/src/components/header/style.css Outdated
Five review findings on the drawer-mode rail:

- the brand name shares its row with the dock button and no longer had the
  width for it: nowrap let it paint over that button and grow the rail's
  scroller, so it now ellipsises;
- the two drawer-mode controls (the 2rem chevron and the 2.5rem floating
  opener) missed the ~44px coarse-pointer floor the rest of the rail keeps —
  and wall tablets, the feature's stated audience, are coarse-pointer at
  desktop widths. Both join the `pointer: coarse` block; the opener still
  clears the 3.25rem gutter at that size;
- the opener is the only way back into an undocked rail on a keyboard, so it
  (and the chevron) get a `:focus-visible` ring;
- both menu buttons kept the accessible name "Open the menu" while the drawer
  was open, contradicting their own `aria-expanded`: the label now swaps to a
  new `header.closeMenu`, added in the three languages;
- Escape toggled `showCollapsedMenu`, so another document-level Escape handler
  closing the drawer in the same tick could see it reopened. It now dismisses
  the profile dropup first, then calls a dedicated `closeCollapsedMenu`.

Co-Authored-By: Claude <noreply@anthropic.com>

Autofix-Pass: 1
cursor[bot]
cursor Bot previously approved these changes Aug 29, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Follow-up fe26302 lands the five residuals from the last pass. The brand title now ellipsises in the shared row; both drawer-mode controls grow to 2.75rem under pointer: coarse (gutter still clears at 3.25rem); the opener and chevron get a :focus-visible ring; aria-label swaps to header.closeMenu while the drawer is open; Escape dismisses the profile dropup first, then a dedicated closeCollapsedMenu instead of toggling.

Still an opt-in desktop drawer over the existing mobile off-canvas path. Default stays the docked 15rem rail; preference is per-device localStorage (same idea as dark mode). No server, API, or DEVICE_FEATURE_* change. Spec H2 is already in the same diff. Front test/build, Cypress, Docker and codecov are green on this head.

Not risk:high. Not needs:human-review.

Open in Web View Automation 

Sent by Cursor Automation: Automatic PR review

claude added 2 commits August 29, 2026 15:43
… overlay drawer

Field feedback on the first iteration: reusing the mobile drawer on desktop
(open = over a backdrop, page dimmed) lost the "rail open while working"
state — open was always modal, which is worse than the docked rail.

The desktop preference is now a plain collapse: expanded is the docked rail
with the content beside it, exactly as before this feature; collapsed slides
the rail off-canvas and gives the page the width back, with the floating
button expanding it back into the docked rail. Neither state is an overlay —
no backdrop, no Escape, no close-on-navigation on desktop; that whole
grammar stays mobile-only. A matching padding-left transition on the outer
page makes content and rail move as one.

Renamed accordingly (sidebar-collapsed preference, toggleSidebarCollapsed,
body.gladys-sidebar-collapsed, header.collapseMenu/expandMenu labels).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JnwsS9jopHYHX3GQMViT2c
The review pass landed on the drawer iteration; its five findings are
ported onto the collapsed-rail behaviour: brand-name ellipsis next to the
collapse button, ~44px coarse-pointer targets and focus-visible rings on
the two rail controls, the mobile burger's open/close accessible name, and
the Escape refinement (profile dropup first, dedicated close instead of a
toggle). The open/close label swap on the desktop button no longer applies:
the expand button is only rendered while the rail is collapsed and expands
it in place, so its name is static.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JnwsS9jopHYHX3GQMViT2c
@Pierre-Gilles Pierre-Gilles changed the title feat(front): let the left sidebar be undocked into a drawer on desktop feat(front): make the left sidebar collapsible on desktop Aug 29, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up 3abf632 replaces the desktop overlay drawer with a real collapse: expanded is the same docked 15rem rail as today (content beside it, no backdrop, stays open across navigation); collapsed slides it off-canvas and gives the page the column back, with the floating button expanding it into that same docked rail. That is the right answer to the field regression — open-while-working was lost when open meant “over a dimmed page”. Overlay grammar (backdrop, Escape, close-on-navigation) stays mobile-only.

The five residuals from the drawer iteration are ported onto this behaviour: brand-name ellipsis next to the chevron; both rail controls at 2.75rem under pointer: coarse (gutter still clears at 3.25rem); :focus-visible rings; the mobile burger’s open/close name; Escape dismisses the profile dropup first, then a dedicated closeCollapsedMenu. The expand button’s name is now static (header.expandMenu) because it only exists while collapsed.

Still front-only, opt-in, default expanded. Preference is per-device localStorage (sidebar-collapsed), same idea as dark mode. Spec H2 is in the same diff. Front test/build, Cypress, Docker and codecov are green on this head. data-cy="sidebar-toggler" / sidebar-nav are unchanged.

One non-blocking residual: collapsing/expanding does not move keyboard focus onto the newly relevant control, so a keyboard user who just hid the rail lands on body until they tab to the floating button. Mouse and touch are unaffected.

Not risk:high. Not needs:human-review.

Open in Web View Automation 

Sent by Cursor Automation: Automatic PR review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:front Preact front-end type:feature New user-facing feature or improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants