From 71585e1bbc5753a88441209e82b996120947d9d7 Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Tue, 12 May 2026 13:40:57 +0200 Subject: [PATCH] fix(appshell): wrap PrimeVue responsive elements to bypass specificity conflict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tailwind's lg:hidden loses to PrimeVue's .p-button { display: inline-flex } due to equal specificity but later cascade order. Resulted in the mobile hamburger remaining visible on desktop, allowing the Drawer to open over the already-visible permanent sidebar. Fix: wrap mobile-only cluster (hamburger + title) in a plain
so the wrapper owns the visibility toggle. The wrapper is not a PrimeVue component, so no specificity competition. The Drawer itself had the same anti-pattern (class="lg:hidden") and is worse, because PrimeVue Drawer teleports to body — a wrapping div on the parent does not isolate the teleported overlay, and a class on the Drawer root loses to .p-drawer { display: flex } when visible. Converted to v-if="!isLg" driven by useMediaQuery('(min-width: 1024px)'). Vue simply does not render the component on lg+, so no display rule competes. Audited all 5 layouts for the same anti-pattern: - AppShell.vue — fixed (Button + Drawer described above) - default.vue / OrganizerLayout.vue / PortalLayout.vue — delegate to AppShell; no PrimeVue elements with responsive classes - blank.vue — plain
, no PrimeVue - PublicLayout.vue — plain
, no PrimeVue useMediaQuery is auto-imported via unplugin-auto-import's @vueuse/core entry in vite.config.ts; explicit imports get stripped by the post-edit ESLint --fix hook as redundant. F3-introduced bug (commit 43915501); surfaced during F3.5 testing. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/app/src/layouts/components/AppShell.vue | 51 ++++++++++++++------ 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/apps/app/src/layouts/components/AppShell.vue b/apps/app/src/layouts/components/AppShell.vue index 71316717..d1224e95 100644 --- a/apps/app/src/layouts/components/AppShell.vue +++ b/apps/app/src/layouts/components/AppShell.vue @@ -52,6 +52,13 @@ const authStore = useAuthStore() const mobileNavOpen = ref(false) +// Tailwind's lg breakpoint, mirrored in script so Vue can own the +// visibility of PrimeVue elements that would otherwise lose a CSS +// specificity duel to .p-button / .p-drawer / etc. See the wrapper +// `
` around the topbar mobile cluster and the +// `v-if="!isLg"` on the Drawer. +const isLg = useMediaQuery('(min-width: 1024px)') + function isHeading(item: NavItem): item is NavHeading { return 'heading' in item } @@ -143,11 +150,16 @@ function onHelpClick() { - +
- - {{ title }} + +
+ + {{ title }} +