From 7888349739e39eac058eba5c18a607796797b221 Mon Sep 17 00:00:00 2001 From: Lucalopezz Date: Sat, 24 Oct 2026 08:49:22 -0300 Subject: [PATCH] fix(web): make mobile navigation opaque Fixes #18 --- apps/web/e2e/mocked/browser-behavior.spec.ts | 38 ++++++++++++++++++ apps/web/src/index.css | 14 +++++++ .../issue-18-mobile-navigation-dark.png | Bin 0 -> 44376 bytes .../issue-18-mobile-navigation-light.png | Bin 0 -> 44358 bytes 4 files changed, 52 insertions(+) create mode 100644 docs/images/issue-18-mobile-navigation-dark.png create mode 100644 docs/images/issue-18-mobile-navigation-light.png diff --git a/apps/web/e2e/mocked/browser-behavior.spec.ts b/apps/web/e2e/mocked/browser-behavior.spec.ts index 154cf48..0a6dd14 100644 --- a/apps/web/e2e/mocked/browser-behavior.spec.ts +++ b/apps/web/e2e/mocked/browser-behavior.spec.ts @@ -1,6 +1,44 @@ import { expect, test } from '@playwright/test' import { authenticatedApi, entry, entryId, guestApi, mockApi, project, projectId } from './mock-api' +function backgroundPixels(element: Element) { + const canvas = document.createElement('canvas') + const context = canvas.getContext('2d')! + context.fillStyle = getComputedStyle(element).backgroundColor + context.fillRect(0, 0, 1, 1) + return Array.from(context.getImageData(0, 0, 1, 1).data) +} + +for (const theme of ['dark', 'light']) { + test(`mobile navigation has an opaque ${theme} surface and a dark backdrop without blur`, async ({ page }, testInfo) => { + await page.setViewportSize({ width: 390, height: 844 }) + const assertNoUnexpected = await mockApi(page, authenticatedApi) + await page.goto('/dashboard') + await page.evaluate((dark) => document.documentElement.classList.toggle('dark', dark), theme === 'dark') + await page.getByRole('button', { name: 'Open navigation menu' }).click() + const drawer = page.getByRole('dialog', { name: 'Sidebar' }) + await expect(drawer).toBeVisible() + + // Browser-computed colors catch transparent surfaces even when the theme's + // CSS variables or generated utility classes change. + const surface = await drawer.evaluate(backgroundPixels) + expect(surface[3], 'Navigation must hide the page behind it').toBe(255) + expect(surface[0]).toBe(theme === 'dark' ? 10 : 255) + await expect(drawer).toHaveCSS('backdrop-filter', 'none') + + const overlay = page.locator('[data-slot="sheet-overlay"]') + await expect(overlay).toHaveCSS('backdrop-filter', 'none') + const backdrop = await overlay.evaluate(backgroundPixels) + expect(backdrop.slice(0, 3)).toEqual([0, 0, 0]) + expect(backdrop[3]).toBeGreaterThanOrEqual(178) + expect(backdrop[3]).toBeLessThanOrEqual(179) + await page.screenshot({ path: testInfo.outputPath(`mobile-navigation-${theme}.png`) }) + await page.keyboard.press('Escape') + await expect(drawer).toBeHidden() + assertNoUnexpected() + }) +} + test('mobile drawer exposes working links and closes after navigation', async ({ page }) => { await page.setViewportSize({ width: 390, height: 844 }) const assertNoUnexpected = await mockApi(page, authenticatedApi) diff --git a/apps/web/src/index.css b/apps/web/src/index.css index 2614ed3..e57e472 100644 --- a/apps/web/src/index.css +++ b/apps/web/src/index.css @@ -88,3 +88,17 @@ html.dark { min-width: 20rem; } } + +/* The mobile sidebar is portaled outside the app layout. Give the drawer its + own opaque theme surface so page content cannot compete with navigation. */ +[data-slot='sidebar'][data-mobile='true'] { + @apply bg-background text-foreground; + backdrop-filter: none; +} + +/* Limit the darker scrim to navigation; other sheets keep their own styling. + Radix renders the overlay immediately before its portaled drawer. */ +[data-slot='sheet-overlay']:has(+ [data-slot='sidebar'][data-mobile='true']) { + @apply bg-black/70; + backdrop-filter: none; +} diff --git a/docs/images/issue-18-mobile-navigation-dark.png b/docs/images/issue-18-mobile-navigation-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..08adc86b380c4e5d6a9f22a68cb13d071045d620 GIT binary patch literal 44376 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