From b123879b25feadc922f33429d5eef0f92d2ee717 Mon Sep 17 00:00:00 2001 From: rUv Date: Mon, 27 Apr 2026 13:59:34 -0400 Subject: [PATCH] fix(dashboard): settings drawer scrim covers viewport (host transform fix) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(ci): wasm-pack PATH + Dockerfile workspace stub Closes the two post-merge failures from #436: 1. wasm-pack: command not found — cargo install doesn't reliably leave the binary on PATH. Switched to the canonical installer in both the Pages and a11y workflows. 2. nvsim-server Docker build — cargo couldn't resolve workspace.dependencies from a partial copy. Dockerfile now generates a stub workspace Cargo.toml inline that lists just nvsim + nvsim-server. Co-Authored-By: claude-flow * fix(dashboard): settings drawer scrim — escape host transform's containing-block trap The drawer's :host had transform: translateX(...) which makes it the containing block for any fixed-position descendants. The .scrim at 'position: fixed; inset: 0' therefore covered only the drawer's own 420 px panel area, not the viewport. Visible symptoms: - Page behind the drawer didn't dim - Click outside the drawer didn't dismiss it (no scrim to receive) - Felt like the drawer wasn't really 'modal' Fix: keep :host as a fixed full-viewport overlay (no transform), move the drawer body into an inner .panel div, transform only that. Now the scrim covers the viewport correctly and outside-clicks dismiss. Same trap exists nowhere else; nv-modal already follows this pattern. Co-Authored-By: claude-flow --- .../src/components/nv-settings-drawer.ts | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/dashboard/src/components/nv-settings-drawer.ts b/dashboard/src/components/nv-settings-drawer.ts index 3efd907a..92544147 100644 --- a/dashboard/src/components/nv-settings-drawer.ts +++ b/dashboard/src/components/nv-settings-drawer.ts @@ -9,26 +9,35 @@ export class NvSettingsDrawer extends LitElement { @state() private open = false; static styles = css` + /* The host covers the viewport without transforming itself. Only the + * inner .panel is transformed; otherwise the host's transform would + * create a containing block for the fixed-position scrim, clipping + * it to the panel's 420 px width and breaking outside-to-dismiss. */ :host { - position: fixed; top: 0; right: 0; bottom: 0; + position: fixed; inset: 0; + z-index: 51; + pointer-events: none; + opacity: 0; + transition: opacity 0.2s; + } + :host([open]) { pointer-events: auto; opacity: 1; } + .scrim { + position: absolute; inset: 0; + background: rgba(0, 0, 0, 0.5); + backdrop-filter: blur(2px); + } + .panel { + position: absolute; + top: 0; right: 0; bottom: 0; width: 420px; max-width: 100vw; background: var(--bg-1); border-left: 1px solid var(--line); - z-index: 51; transform: translateX(100%); transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1); display: flex; flex-direction: column; - box-shadow: -20px 0 60px -20px rgba(0,0,0,0.5); + box-shadow: -20px 0 60px -20px rgba(0, 0, 0, 0.5); } - :host([open]) { transform: translateX(0); } - .scrim { - position: fixed; inset: 0; - background: rgba(0,0,0,0.5); - z-index: 50; - opacity: 0; pointer-events: none; - transition: opacity 0.2s; - } - :host([open]) .scrim { opacity: 1; pointer-events: auto; } + :host([open]) .panel { transform: translateX(0); } .h { padding: 14px 16px; border-bottom: 1px solid var(--line); @@ -123,6 +132,7 @@ export class NvSettingsDrawer extends LitElement { override render() { return html`
this.close()}>
+ + `; } }