From 38bb056aa6cf263569799760d1ab817e14163aa1 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Wed, 29 Apr 2026 15:26:16 +0100 Subject: [PATCH] fix(ui): adjust plugin header spacing Align PluginHeader spacing across tabbed and non-tabbed variants so the component owns the surrounding page spacing more consistently. Signed-off-by: Charles de Dreuille --- .changeset/plugin-header-spacing.md | 7 ++ .storybook/themes/spotify.css | 11 +-- .../src/components/Header/Header.module.css | 4 - .../PluginHeader/PluginHeader.module.css | 13 ++- .../components/PluginHeader/PluginHeader.tsx | 87 +++++++++---------- .../recipes/PluginHeaderAndHeader.stories.tsx | 26 +++++- 6 files changed, 85 insertions(+), 63 deletions(-) create mode 100644 .changeset/plugin-header-spacing.md diff --git a/.changeset/plugin-header-spacing.md b/.changeset/plugin-header-spacing.md new file mode 100644 index 0000000000..bebf7a5608 --- /dev/null +++ b/.changeset/plugin-header-spacing.md @@ -0,0 +1,7 @@ +--- +'@backstage/ui': patch +--- + +Adjusted PluginHeader spacing so headers with and without tabs align more consistently with surrounding page content. + +**Affected components:** PluginHeader, Header diff --git a/.storybook/themes/spotify.css b/.storybook/themes/spotify.css index f91eb62e02..d22b1ef6aa 100644 --- a/.storybook/themes/spotify.css +++ b/.storybook/themes/spotify.css @@ -245,11 +245,12 @@ * this file for easier scanning alongside other component overrides above. */ [data-theme-name='spotify'] { + .bui-PluginHeader { + margin-top: var(--bui-space-4); + } + .bui-PluginHeaderToolbar { height: 32px; - border: none; - background: none; - margin-top: var(--bui-space-4); } .bui-PluginHeaderTabsWrapper { @@ -258,8 +259,4 @@ margin-left: 0; margin-top: var(--bui-space-2); } - - .bui-HeaderTop { - padding-top: var(--bui-space-4); - } } diff --git a/packages/ui/src/components/Header/Header.module.css b/packages/ui/src/components/Header/Header.module.css index 58bcf2699d..60db85cb25 100644 --- a/packages/ui/src/components/Header/Header.module.css +++ b/packages/ui/src/components/Header/Header.module.css @@ -32,10 +32,6 @@ box-sizing: border-box; } - .bui-HeaderTop { - padding-top: var(--bui-space-6); - } - .bui-HeaderStickySentinel { height: 1px; margin-bottom: -1px; diff --git a/packages/ui/src/components/PluginHeader/PluginHeader.module.css b/packages/ui/src/components/PluginHeader/PluginHeader.module.css index 1ab0f9570a..41a31b78e4 100644 --- a/packages/ui/src/components/PluginHeader/PluginHeader.module.css +++ b/packages/ui/src/components/PluginHeader/PluginHeader.module.css @@ -17,15 +17,23 @@ @layer tokens, base, components, utilities; @layer components { + .bui-PluginHeader { + margin-bottom: var(--bui-space-6); + padding-inline: var(--bui-space-5); + } + .bui-PluginHeaderToolbar { display: flex; flex-direction: row; align-items: center; justify-content: space-between; - padding-inline: var(--bui-space-5); color: var(--bui-fg-primary); height: 52px; - border-bottom: 1px solid var(--bui-border-1); + border-bottom: solid 1px var(--bui-border-1); + + &[data-has-tabs] { + border-bottom: none; + } } .bui-PluginHeaderToolbarContent { @@ -69,7 +77,6 @@ } .bui-PluginHeaderTabsWrapper { - padding-inline: var(--bui-space-3); border-bottom: 1px solid var(--bui-border-1); } } diff --git a/packages/ui/src/components/PluginHeader/PluginHeader.tsx b/packages/ui/src/components/PluginHeader/PluginHeader.tsx index e410ce91f9..5d9b176907 100644 --- a/packages/ui/src/components/PluginHeader/PluginHeader.tsx +++ b/packages/ui/src/components/PluginHeader/PluginHeader.tsx @@ -25,7 +25,6 @@ import { Box } from '../Box'; import { Link } from '../Link'; import { RiShapesLine } from '@remixicon/react'; import { Text } from '../Text'; -import { BgReset } from '../../hooks/useBg'; declare module 'react-aria-components' { interface RouterConfig { @@ -53,7 +52,7 @@ export const PluginHeader = (props: PluginHeaderProps) => { } = ownProps; const hasTabs = tabs && tabs.length > 0; - const headerRef = useRef(null); + const rootRef = useRef(null); const animationFrameRef = useRef(undefined); const lastAppliedHeightRef = useRef(undefined); @@ -62,7 +61,7 @@ export const PluginHeader = (props: PluginHeaderProps) => { }, [customActions]); useIsomorphicLayoutEffect(() => { - const el = headerRef.current; + const el = rootRef.current; if (!el) { return undefined; } @@ -125,50 +124,44 @@ export const PluginHeader = (props: PluginHeaderProps) => { const titleText = title || 'Your plugin'; return ( - -
- -
- -

- {titleLink ? ( - - {titleText} - - ) : ( - - {titleText} - - )} -

-
-
{actionChildren}
-
- {tabs && ( - - - - {tabs?.map(tab => ( - - {tab.label} - - ))} - - +
+
+
+ - )} -
-
+

+ {titleLink ? ( + + {titleText} + + ) : ( + + {titleText} + + )} +

+ +
{actionChildren}
+ + {tabs && ( +
+ + + {tabs?.map(tab => ( + + {tab.label} + + ))} + + +
+ )} + ); }; diff --git a/packages/ui/src/recipes/PluginHeaderAndHeader.stories.tsx b/packages/ui/src/recipes/PluginHeaderAndHeader.stories.tsx index 4f74572ef5..b3bad07185 100644 --- a/packages/ui/src/recipes/PluginHeaderAndHeader.stories.tsx +++ b/packages/ui/src/recipes/PluginHeaderAndHeader.stories.tsx @@ -49,7 +49,7 @@ import { // --------------------------------------------------------------------------- const PageContent = () => ( - + @@ -59,7 +59,7 @@ const PageContent = () => ( ); const LongPageContent = () => ( - + @@ -104,6 +104,28 @@ export const NoHeader = meta.story({ ), }); +export const NoHeaderWithTabs = meta.story({ + decorators: [withLayout], + render: () => ( + <> + } + title="APIs" + tabs={[ + { id: 'overview', label: 'Overview', href: '/apis' }, + { + id: 'definitions', + label: 'Definitions', + href: '/apis/definitions', + }, + { id: 'consumers', label: 'Consumers', href: '/apis/consumers' }, + ]} + /> + + + ), +}); + export const SimpleHeader = meta.story({ decorators: [withLayout], render: () => (