From ea80fe495b2fc50dc664b96f706eeb8ddc447943 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Sun, 19 Jul 2026 21:03:40 +0200 Subject: [PATCH 1/3] Fix draft banner stack overlap Co-authored-by: codex --- .../chat/ComposerBannerStack.test.tsx | 34 ++++++++++ .../components/chat/ComposerBannerStack.tsx | 65 +++++++++++-------- 2 files changed, 71 insertions(+), 28 deletions(-) create mode 100644 apps/web/src/components/chat/ComposerBannerStack.test.tsx diff --git a/apps/web/src/components/chat/ComposerBannerStack.test.tsx b/apps/web/src/components/chat/ComposerBannerStack.test.tsx new file mode 100644 index 00000000000..db2ba4bbe3a --- /dev/null +++ b/apps/web/src/components/chat/ComposerBannerStack.test.tsx @@ -0,0 +1,34 @@ +import { renderToStaticMarkup } from "react-dom/server"; +import { describe, expect, it } from "vite-plus/test"; + +import { ComposerBannerStack, type ComposerBannerStackItem } from "./ComposerBannerStack"; + +const banner = (id: string): ComposerBannerStackItem => ({ + id, + variant: "warning", + icon: , + title: `${id} warning`, +}); + +describe("ComposerBannerStack", () => { + it("keeps expanded banners in layout flow so surrounding content moves out of their way", () => { + const markup = renderToStaticMarkup( + , + ); + + const expandedItems = markup.match( + /
/, + ); + + expect(expandedItems?.[1]).toContain("grid-rows-[0fr]"); + expect(expandedItems?.[1]).toContain("group-hover/banner-stack:grid-rows-[1fr]"); + expect(expandedItems?.[1]).not.toContain("absolute"); + expect(markup.indexOf("stacked warning")).toBeLessThan(markup.indexOf("front warning")); + }); + + it("does not render an expandable region for a single banner", () => { + const markup = renderToStaticMarkup(); + + expect(markup).not.toContain("data-composer-banner-stack-expanded-items"); + }); +}); diff --git a/apps/web/src/components/chat/ComposerBannerStack.tsx b/apps/web/src/components/chat/ComposerBannerStack.tsx index 1f82c452b05..f74182792f9 100644 --- a/apps/web/src/components/chat/ComposerBannerStack.tsx +++ b/apps/web/src/components/chat/ComposerBannerStack.tsx @@ -101,6 +101,43 @@ export function ComposerBannerStack({ className, items }: ComposerBannerStackPro aria-hidden="true" /> ) : null} + {hasStack ? ( +
+
+
+ {stackedItems.map((item) => ( +
+ requestDismiss(item)} + /> +
+ ))} +
+
+
+ ) : null}
requestDismiss(frontItem)} />
- {hasStack ? ( -
- {stackedItems.map((item) => ( -
- requestDismiss(item)} - /> -
- ))} -
- ) : null}
); From 01aca05eef8798b38a03d389bf2194a92db4c1e3 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Mon, 20 Jul 2026 00:59:50 +0200 Subject: [PATCH 2/3] Reduce draft headline size to prevent overlap --- apps/web/src/components/chat/DraftHeroHeadline.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/components/chat/DraftHeroHeadline.tsx b/apps/web/src/components/chat/DraftHeroHeadline.tsx index 32d195d7b2e..6a88fb8da19 100644 --- a/apps/web/src/components/chat/DraftHeroHeadline.tsx +++ b/apps/web/src/components/chat/DraftHeroHeadline.tsx @@ -101,7 +101,7 @@ export function DraftHeroHeadline({ ); return ( -

+

{hasResolvedProject ? ( <>What should we build in {projectSelector}? ) : canChooseProject ? ( From 90ee1f2ed7410fbf4638b757901b44d840a18e5a Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Mon, 20 Jul 2026 01:06:35 +0200 Subject: [PATCH 3/3] Fix composer banner overlap with draft input - Reorder banner layers so the front banner stays above stacked warnings - Hide collapsed stacked banners from pointer and focus interactions --- .../chat/ComposerBannerStack.test.tsx | 5 ++- .../components/chat/ComposerBannerStack.tsx | 42 +++++++++---------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/apps/web/src/components/chat/ComposerBannerStack.test.tsx b/apps/web/src/components/chat/ComposerBannerStack.test.tsx index db2ba4bbe3a..16fdff64425 100644 --- a/apps/web/src/components/chat/ComposerBannerStack.test.tsx +++ b/apps/web/src/components/chat/ComposerBannerStack.test.tsx @@ -22,8 +22,11 @@ describe("ComposerBannerStack", () => { expect(expandedItems?.[1]).toContain("grid-rows-[0fr]"); expect(expandedItems?.[1]).toContain("group-hover/banner-stack:grid-rows-[1fr]"); + expect(expandedItems?.[1]).toContain("z-20"); expect(expandedItems?.[1]).not.toContain("absolute"); - expect(markup.indexOf("stacked warning")).toBeLessThan(markup.indexOf("front warning")); + expect(markup.indexOf("front warning")).toBeLessThan(markup.indexOf("stacked warning")); + expect(markup).toContain("invisible pointer-events-none"); + expect(markup).toContain("group-focus-within/banner-stack:visible"); }); it("does not render an expandable region for a single banner", () => { diff --git a/apps/web/src/components/chat/ComposerBannerStack.tsx b/apps/web/src/components/chat/ComposerBannerStack.tsx index f74182792f9..d51c23a2f27 100644 --- a/apps/web/src/components/chat/ComposerBannerStack.tsx +++ b/apps/web/src/components/chat/ComposerBannerStack.tsx @@ -85,7 +85,7 @@ export function ComposerBannerStack({ className, items }: ComposerBannerStackPro
@@ -101,21 +101,37 @@ export function ComposerBannerStack({ className, items }: ComposerBannerStackPro aria-hidden="true" /> ) : null} +
+ requestDismiss(frontItem)} + /> +
{hasStack ? (
{stackedItems.map((item) => ( @@ -138,22 +154,6 @@ export function ComposerBannerStack({ className, items }: ComposerBannerStackPro
) : null} -
- requestDismiss(frontItem)} - /> -
);