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