From 617a779ca7c5e83f584b2cd0e1b2a4228cff8dde Mon Sep 17 00:00:00 2001 From: testvalue Date: Fri, 3 Apr 2026 09:43:59 -0400 Subject: [PATCH 1/7] fix(ui): prevents navbar jump when notification drawer opens --- src/app/components/shared/NotificationDrawer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/shared/NotificationDrawer.tsx b/src/app/components/shared/NotificationDrawer.tsx index 01ee0b48..aa8ff430 100644 --- a/src/app/components/shared/NotificationDrawer.tsx +++ b/src/app/components/shared/NotificationDrawer.tsx @@ -25,7 +25,7 @@ export default function NotificationDrawer(props: NotificationDrawerProps) { } return ( - !open && props.onClose()} modal> + !open && props.onClose()} modal preventScroll={false}> From 9847225e0852b97b54c7ed243d3da123091ed969 Mon Sep 17 00:00:00 2001 From: testvalue Date: Fri, 3 Apr 2026 10:00:57 -0400 Subject: [PATCH 2/7] fix(ui): uses CSS scrollbar-width var for navbar compensation --- src/app/components/shared/NotificationDrawer.tsx | 2 +- src/app/index.css | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/app/components/shared/NotificationDrawer.tsx b/src/app/components/shared/NotificationDrawer.tsx index aa8ff430..01ee0b48 100644 --- a/src/app/components/shared/NotificationDrawer.tsx +++ b/src/app/components/shared/NotificationDrawer.tsx @@ -25,7 +25,7 @@ export default function NotificationDrawer(props: NotificationDrawerProps) { } return ( - !open && props.onClose()} modal preventScroll={false}> + !open && props.onClose()} modal> diff --git a/src/app/index.css b/src/app/index.css index fd89cab7..f71caf65 100644 --- a/src/app/index.css +++ b/src/app/index.css @@ -107,6 +107,13 @@ animation: overlay-fade-in 0.3s ease-out forwards; } +/* ── Fixed-element scrollbar compensation ────────────────────────────────── */ +/* solid-prevent-scroll sets --scrollbar-width on body when scroll is locked; + fixed elements don't inherit body padding-right, so compensate explicitly */ +.navbar { + padding-right: var(--scrollbar-width, 0px); +} + /* ── Kobalte → daisyUI bridges ───────────────────────────────────────────── */ /* Kobalte Tabs: data-selected → daisyUI tab-active */ From 73a34102731a09fbd07ced837d7f190956e3c3f8 Mon Sep 17 00:00:00 2001 From: testvalue Date: Fri, 3 Apr 2026 10:06:25 -0400 Subject: [PATCH 3/7] fix(ui): preserves navbar padding and compensates footer --- src/app/index.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/app/index.css b/src/app/index.css index f71caf65..b93f3836 100644 --- a/src/app/index.css +++ b/src/app/index.css @@ -111,6 +111,9 @@ /* solid-prevent-scroll sets --scrollbar-width on body when scroll is locked; fixed elements don't inherit body padding-right, so compensate explicitly */ .navbar { + padding-right: calc(0.5rem + var(--scrollbar-width, 0px)); +} +footer { padding-right: var(--scrollbar-width, 0px); } From 0c9a2820a2ef57287eaf553ca0d471f4cc42956e Mon Sep 17 00:00:00 2001 From: testvalue Date: Fri, 3 Apr 2026 10:19:17 -0400 Subject: [PATCH 4/7] docs(css): notes daisyUI .navbar padding dependency in comment --- src/app/index.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/index.css b/src/app/index.css index b93f3836..b64e0f7d 100644 --- a/src/app/index.css +++ b/src/app/index.css @@ -109,7 +109,8 @@ /* ── Fixed-element scrollbar compensation ────────────────────────────────── */ /* solid-prevent-scroll sets --scrollbar-width on body when scroll is locked; - fixed elements don't inherit body padding-right, so compensate explicitly */ + fixed elements don't inherit body padding-right, so compensate explicitly. + 0.5rem matches daisyUI .navbar padding (padding: .5rem) */ .navbar { padding-right: calc(0.5rem + var(--scrollbar-width, 0px)); } From f484da9db475f556e6fb65732792f5ee5f2b8fc7 Mon Sep 17 00:00:00 2001 From: testvalue Date: Fri, 3 Apr 2026 11:03:48 -0400 Subject: [PATCH 5/7] test(e2e): adds scrollbar compensation test and scopes footer selector --- e2e/smoke.spec.ts | 30 +++++++++++++++++++ .../components/dashboard/DashboardPage.tsx | 2 +- src/app/index.css | 2 +- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/e2e/smoke.spec.ts b/e2e/smoke.spec.ts index 92aaaa75..c12585b9 100644 --- a/e2e/smoke.spec.ts +++ b/e2e/smoke.spec.ts @@ -165,6 +165,36 @@ test("switching tabs changes active tab indicator", async ({ page }) => { await expect(actionsTab).toHaveAttribute("aria-selected", "true"); }); +test("fixed elements compensate for scrollbar width when scroll is locked", async ({ page }) => { + await setupAuth(page); + await page.goto("/dashboard"); + + const navbar = page.locator(".navbar"); + const footer = page.locator(".app-footer"); + await expect(navbar).toBeVisible(); + await expect(footer).toBeVisible(); + + // Baseline: navbar 0.5rem (8px) from daisyUI, footer 0px (no base padding) + expect(parseFloat(await navbar.evaluate((el) => getComputedStyle(el).paddingRight))).toBeCloseTo(8, 0); + expect(parseFloat(await footer.evaluate((el) => getComputedStyle(el).paddingRight))).toBeCloseTo(0, 0); + + // Simulate solid-prevent-scroll setting --scrollbar-width on body + await page.evaluate(() => { + document.body.style.setProperty("--scrollbar-width", "15px"); + }); + + // Navbar: 8px + 15px = 23px, footer: 0px + 15px = 15px + expect(parseFloat(await navbar.evaluate((el) => getComputedStyle(el).paddingRight))).toBeCloseTo(23, 0); + expect(parseFloat(await footer.evaluate((el) => getComputedStyle(el).paddingRight))).toBeCloseTo(15, 0); + + // Clear — both return to baseline + await page.evaluate(() => { + document.body.style.removeProperty("--scrollbar-width"); + }); + expect(parseFloat(await navbar.evaluate((el) => getComputedStyle(el).paddingRight))).toBeCloseTo(8, 0); + expect(parseFloat(await footer.evaluate((el) => getComputedStyle(el).paddingRight))).toBeCloseTo(0, 0); +}); + test("dashboard shows empty state with no data", async ({ page }) => { await setupAuth(page); await page.goto("/dashboard"); diff --git a/src/app/components/dashboard/DashboardPage.tsx b/src/app/components/dashboard/DashboardPage.tsx index fe932933..9a70498e 100644 --- a/src/app/components/dashboard/DashboardPage.tsx +++ b/src/app/components/dashboard/DashboardPage.tsx @@ -428,7 +428,7 @@ export default function DashboardPage() { -