From 1ea8e79dd07f5a35283a5ea2d231f7cc2443639e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tao=20Bojl=C3=A9n?= Date: Sat, 20 Jun 2026 15:59:23 +0100 Subject: [PATCH] fix(website): render command palette dialog outside the backdrop-blur header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The palette's dialog overlay/content are position: fixed, but were DOM-descendants of the sticky nav
, which has backdrop-blur (backdrop-filter). Per the CSS spec, an ancestor with backdrop-filter becomes the containing block for fixed descendants, so the dialog was positioned relative to the 64px header instead of the viewport — top: 50% resolved to ~32px and translate(-50%, -50%) shoved most of the dialog above the viewport, leaving only the bottom half visible. Fix: keep the trigger button inside the nav header (where it belongs visually) but render the dialog LiveComponent as a sibling of
, outside the backdrop-filter containing block. The existing #command-palette-content CSS override then centers correctly relative to the viewport. Adds a regression test asserting the dialog markup appears after
. --- test/website/command_palette_test.exs | 36 ++++++++++++++++++++------- website/command_palette.ex | 21 +++------------- website/nav.ex | 22 +++++++++++++++- 3 files changed, 51 insertions(+), 28 deletions(-) diff --git a/test/website/command_palette_test.exs b/test/website/command_palette_test.exs index 4280412..6e01910 100644 --- a/test/website/command_palette_test.exs +++ b/test/website/command_palette_test.exs @@ -191,20 +191,38 @@ defmodule Shadix.Website.CommandPaletteTest do assert html =~ ~s(data-slot="command-palette-trigger") assert html =~ "⌘K" end + + test "the dialog is rendered outside the backdrop-blur header (containing-block fix)" do + {:ok, lv, html} = live_isolated(build_conn(), NavHost) + + # The trigger stays inside the nav header. + assert has_element?(lv, "header button#command-palette-trigger") + assert html =~ "⌘K" + + # The dialog root must NOT be a descendant of
. The header has + # backdrop-blur (backdrop-filter), which per the CSS spec becomes the + # containing block for position: fixed descendants — so a dialog inside + # the header is positioned relative to the 64px header, not the viewport, + # and ends up shoved above the visible area. The dialog must be a sibling + # of the header, i.e. its markup appears AFTER the header's closing tag. + {header_close, _} = :binary.match(html, "
") + {root_open, _} = :binary.match(html, ~s(id="command-palette-root")) + + assert root_open > header_close, + "command palette dialog must be rendered after
, not inside it" + + assert has_element?(lv, "#command-palette-root") + end end describe "render (live)" do - test "renders the trigger button and a hidden dialog containing the command" do + test "renders the hidden dialog containing the command" do {:ok, lv, html} = live_isolated(build_conn(), Host) - # Trigger: ⌘K hint, opens the dialog via show_dialog. - assert has_element?(lv, "button[data-slot=command-palette-trigger]") - assert html =~ "⌘K" - assert html =~ ~s(phx-hook="ShadixCommandPaletteKb") - assert html =~ ~s(inline-flex) - assert html =~ ~s(rounded-md) - assert html =~ ~s(border-input) - assert html =~ ~s(justify-between) + # The LiveComponent renders only the dialog (the trigger lives in + # Shadix.Website.Nav.top_nav, so it can stay inside the header while the + # dialog escapes the header's backdrop-filter containing block). + refute has_element?(lv, "button#command-palette-trigger") # Dialog scaffold exists (hidden by default) with the command inside. assert has_element?(lv, "#command-palette-root[phx-hook=ShadixDialog]") diff --git a/website/command_palette.ex b/website/command_palette.ex index ee7f03f..b89025c 100644 --- a/website/command_palette.ex +++ b/website/command_palette.ex @@ -15,7 +15,9 @@ defmodule Shadix.Website.CommandPalette do use Phoenix.LiveComponent - import Shadix.Components.Dialog + import Shadix.Components.Dialog, + only: [dialog: 1, hide_dialog: 1, dialog_title: 1, dialog_description: 1] + import Shadix.Components.Command alias Phoenix.LiveView.JS @@ -152,23 +154,6 @@ defmodule Shadix.Website.CommandPalette do def render(assigns) do ~H"""
- - <.dialog id="command-palette" class="p-0"> <:trigger> <.dialog_title id="command-palette" class="sr-only">Search docs diff --git a/website/nav.ex b/website/nav.ex index 8568afe..f4c1afa 100644 --- a/website/nav.ex +++ b/website/nav.ex @@ -10,6 +10,7 @@ defmodule Shadix.Website.Nav do use Phoenix.Component import Shadix.Components.Button + import Shadix.Components.Dialog, only: [show_dialog: 1] alias Shadix.Website.Components.Catalog @@ -50,7 +51,24 @@ defmodule Shadix.Website.Nav do
- <.live_component module={Shadix.Website.CommandPalette} id="command-palette" /> + <.button variant="outline" @@ -80,6 +98,8 @@ defmodule Shadix.Website.Nav do
+ + <.live_component module={Shadix.Website.CommandPalette} id="command-palette" /> """ end -- 2.51.2