diff --git a/.changeset/banner-single-line-toggle.md b/.changeset/banner-single-line-toggle.md
new file mode 100644
index 0000000000000..e7783b6b1d8e2
--- /dev/null
+++ b/.changeset/banner-single-line-toggle.md
@@ -0,0 +1,7 @@
+---
+'@astryxdesign/core': patch
+---
+
+[fix] Banner: a title-only collapsible banner now centers its header, as a title-only banner with any other control already does. `isSingleLine` counted `endContent` and the dismiss button but not the collapse toggle, so a banner whose only control was the toggle kept `align-items: flex-start` — leaving its icon and title 4px above the 28px toggle they sit beside, while the same banner with a dismiss button centered correctly.
+
+@freddymeta
diff --git a/packages/core/src/Banner/Banner.test.tsx b/packages/core/src/Banner/Banner.test.tsx
index c092c3e5b2ecd..501413bd77192 100644
--- a/packages/core/src/Banner/Banner.test.tsx
+++ b/packages/core/src/Banner/Banner.test.tsx
@@ -650,4 +650,44 @@ describe('Banner', () => {
expect(getComputedStyle(textColumn).flexBasis).not.toBe('8rem');
});
});
+ describe('single-line centering', () => {
+ const headerOf = (ui: React.ReactElement) => {
+ const {container} = render(ui);
+ return container.firstElementChild!.firstElementChild!;
+ };
+
+ it('centers a title-only banner that has a dismiss button', () => {
+ const header = headerOf(
+ ,
+ );
+ expect(getComputedStyle(header).alignItems).toBe('center');
+ });
+
+ it('centers a title-only banner whose only control is the collapse toggle', () => {
+ const header = headerOf(
+
+ Details
+ ,
+ );
+ expect(getComputedStyle(header).alignItems).toBe('center');
+ });
+
+ it('keeps a described banner top-aligned, toggle or not', () => {
+ const header = headerOf(
+
+ Details
+ ,
+ );
+ expect(getComputedStyle(header).alignItems).toBe('flex-start');
+ });
+
+ it('keeps a banner with no controls at all top-aligned', () => {
+ const header = headerOf(
+
+ Details
+ ,
+ );
+ expect(getComputedStyle(header).alignItems).toBe('flex-start');
+ });
+ });
});
diff --git a/packages/core/src/Banner/Banner.tsx b/packages/core/src/Banner/Banner.tsx
index 95a6ea5ca8fd8..d25ceea2fcf01 100644
--- a/packages/core/src/Banner/Banner.tsx
+++ b/packages/core/src/Banner/Banner.tsx
@@ -534,8 +534,11 @@ export function Banner({
// Show the end area if there are actions, dismiss, or a collapsible toggle
const showEndArea = isRenderable(endContent) || isDismissable || hasToggle;
// Center items vertically when there's only a title (no description)
- // and the banner has action buttons
- const hasActions = isRenderable(endContent) || isDismissable;
+ // and the banner has action buttons. The collapse toggle counts: it is the
+ // same 28px control as a dismiss button and shares the row with it, so a
+ // collapsible title-only banner is exactly the case this centers — leaving
+ // it out left the icon and title hanging above the toggle they sit beside.
+ const hasActions = isRenderable(endContent) || isDismissable || hasToggle;
const isSingleLine = !isRenderable(description) && hasActions;
// Non-collapsible children are always shown; collapsible ones follow the