Skip to content

fix(shell): the dashboard switcher opens above the app navigation, not behind it - #555

Merged
rubenvdlinde merged 1 commit into
developmentfrom
fix/switcher-sits-above-the-app-navigation
Sep 5, 2026
Merged

fix(shell): the dashboard switcher opens above the app navigation, not behind it#555
rubenvdlinde merged 1 commit into
developmentfrom
fix/switcher-sits-above-the-app-navigation

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

What

The dashboard switcher has been unreachable since #551 adopted the shared shell. 58 e2e tests across eleven specs fail as locator.click timing out, and all 240 interception lines in that run name the same element: [data-testid="cn-nav"] .app-navigation. The panel slides out, it is visible, and every click on it lands on the navigation instead.

Why the z-index was not the fix

This is a stacking context. The sidebar is position: fixed, which escapes the scroll flow but not the stacking context.

Until #551 this view was the page, so there was no context to escape and nothing else claimed the left 280px. Now it renders inside #app-content, a sibling of .app-navigation, and NcAppNavigation carries z-index: 1400 on itself. A sibling with a z-index paints above the whole of a sibling subtree whose own z-index is auto, so the sidebar's 1500 could never win: it was competing inside a box that had already lost.

Raising the number again would have changed nothing. That is worth saying, because it is the obvious first move.

What changes

  • The sidebar and its backdrop are teleported to body, making them siblings of the navigation rather than descendants of the content. That is the only place their z-index means what it says.
  • The backdrop goes from 999 to 1450. At 999 the navigation painted over it even once teleported, so a click meant to dismiss the sidebar navigated instead. 1450 is above the navigation and below the sidebar it sits behind.
  • Both numbers carry a comment saying what they are above and why the Teleport is what makes them work.

Verification

Local, because the E2E leg only runs on the development push:

  • 682 unit tests pass (63 files)
  • webpack production build clean
  • eslint and prettier clean

The proof is the next development push run.

🤖 Generated with Claude Code

…t behind it

#551 adopted the shared shell, and the dashboard switcher has been unreachable
ever since. 58 e2e tests across eleven specs fail as `locator.click` timing out,
and all 240 interception lines in that run name the same element:
`[data-testid="cn-nav"] .app-navigation`. The panel slides out, it is visible,
and every click on it lands on the navigation instead.

This is a stacking context, not a z-index. The sidebar is `position: fixed`,
which escapes the scroll flow but not the stacking context. Until #551 this view
WAS the page, so there was no context to escape and nothing else claimed the
left 280px. Now it renders inside `#app-content`, a sibling of
`.app-navigation`, and NcAppNavigation carries `z-index: 1400` on itself. A
sibling with a z-index paints above the whole of a sibling subtree whose own
z-index is auto, so the sidebar's 1500 could never win: it was competing inside
a box that had already lost. Raising the number again would have changed
nothing, which is worth saying because that is the obvious first move.

The sidebar and its backdrop are therefore teleported to `body`, making them
siblings of the navigation rather than descendants of the content, which is the
only place their z-index means what it says.

The backdrop goes from 999 to 1450 while it is there. At 999 the navigation
painted over it even once teleported, so a click meant to dismiss the sidebar
navigated instead. 1450 puts it above the navigation and below the sidebar it
sits behind.

Verified locally: 682 unit tests pass, webpack build clean, eslint and prettier
clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/launchpad @ a713bd4

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
format
check-schema-l10n
composer ✅ 104/104
npm ✅ 527/527
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright ⏭️ deferred — runs on the promotion into beta/main, not on a pull request into development
Hydra gates

Quality workflow — 2026-09-05 18:12 UTC

Download the full PDF report from the workflow artifacts.

@rubenvdlinde
rubenvdlinde merged commit 1610d27 into development Sep 5, 2026
43 checks passed
rubenvdlinde added a commit that referenced this pull request Sep 5, 2026
…ve (#557)

* fix(shell): the dashboard switcher opens above the app navigation, not behind it

#551 adopted the shared shell, and the dashboard switcher has been unreachable
ever since. 58 e2e tests across eleven specs fail as `locator.click` timing out,
and all 240 interception lines in that run name the same element:
`[data-testid="cn-nav"] .app-navigation`. The panel slides out, it is visible,
and every click on it lands on the navigation instead.

This is a stacking context, not a z-index. The sidebar is `position: fixed`,
which escapes the scroll flow but not the stacking context. Until #551 this view
WAS the page, so there was no context to escape and nothing else claimed the
left 280px. Now it renders inside `#app-content`, a sibling of
`.app-navigation`, and NcAppNavigation carries `z-index: 1400` on itself. A
sibling with a z-index paints above the whole of a sibling subtree whose own
z-index is auto, so the sidebar's 1500 could never win: it was competing inside
a box that had already lost. Raising the number again would have changed
nothing, which is worth saying because that is the obvious first move.

The sidebar and its backdrop are therefore teleported to `body`, making them
siblings of the navigation rather than descendants of the content, which is the
only place their z-index means what it says.

The backdrop goes from 999 to 1450 while it is there. At 999 the navigation
painted over it even once teleported, so a click meant to dismiss the sidebar
navigated instead. 1450 puts it above the navigation and below the sidebar it
sits behind.

Verified locally: 682 unit tests pass, webpack build clean, eslint and prettier
clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(shell): the floating controls move with the backdrop they sit above

#555 teleported the sidebar and its backdrop to `body` and raised the backdrop
to 1450. It should have moved the floating controls at the same time, and the
e2e said so within one run.

Those controls are `position: fixed` with `z-index: 1000`, and that number was
not arbitrary: it sat just above the backdrop's original 999 so that "click the
hamburger again to close the sidebar" works — the sidebar is open, the backdrop
is up, and the toggle has to stay reachable through it. Teleporting one half of
that pairing broke it. The backdrop became a body-level sibling while the
controls stayed inside `#app-content`, whose own z-index is auto, so the
backdrop painted over them whatever number they carried.

`runtime-shell-canEdit` names it exactly: the backdrop intercepting pointer
events over a button the same log calls visible, enabled and stable.

So both halves move, and the controls go to 1460 — above the backdrop, below the
sidebar they never overlap. The comment says which number it is above and why
the Teleport is what makes it mean anything, because the next person to see 1460
will otherwise try raising it.

Verified locally: 682 unit tests pass, webpack build clean, eslint and prettier
clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant