From 6c4606a71cfc19fdd00b60773680ec8a7df4b8fe Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Sun, 26 Apr 2026 15:05:45 +0100 Subject: [PATCH] Polish sticky Header implementation Signed-off-by: Charles de Dreuille --- .changeset/header-sticky-prop.md | 2 ++ packages/ui/src/components/Header/Header.module.css | 12 +++++++----- packages/ui/src/components/Header/Header.tsx | 8 ++++++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.changeset/header-sticky-prop.md b/.changeset/header-sticky-prop.md index 283c235d43..0e2722ed04 100644 --- a/.changeset/header-sticky-prop.md +++ b/.changeset/header-sticky-prop.md @@ -3,3 +3,5 @@ --- Added a `sticky` prop to the `Header` component. When `true`, the title-and-actions bar stays fixed to the top of its scroll container while the rest of the header (tags, description, metadata) scrolls away. The sticky bar background color automatically matches the container surface using the bg-consumer system. + +**Affected components:** Header diff --git a/packages/ui/src/components/Header/Header.module.css b/packages/ui/src/components/Header/Header.module.css index 0834767d2c..0d6e9f7612 100644 --- a/packages/ui/src/components/Header/Header.module.css +++ b/packages/ui/src/components/Header/Header.module.css @@ -116,8 +116,10 @@ .bui-HeaderTitleStack { position: relative; flex: 1 1 auto; - height: calc(var(--bui-font-size-6) * 1.4); + height: 1lh; min-width: 0; + font-size: var(--bui-font-size-6); + line-height: 140%; overflow: hidden; } @@ -151,13 +153,13 @@ overflow: hidden; font-family: var(--bui-font-regular); font-weight: var(--bui-font-weight-bold); - line-height: 140%; + line-height: inherit; text-overflow: ellipsis; white-space: nowrap; } .bui-HeaderBreadcrumbs .bui-HeaderBreadcrumbLink { - font-size: var(--bui-font-size-6); + font-size: inherit; } .bui-HeaderBreadcrumbsSmall .bui-HeaderBreadcrumbLinkSmall { @@ -176,14 +178,14 @@ min-width: 0; font-family: var(--bui-font-regular); font-weight: var(--bui-font-weight-bold); - line-height: 140%; + line-height: inherit; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .bui-HeaderTitle { - font-size: var(--bui-font-size-6); + font-size: inherit; } .bui-HeaderTitleSmall { diff --git a/packages/ui/src/components/Header/Header.tsx b/packages/ui/src/components/Header/Header.tsx index d46468799e..ad70508250 100644 --- a/packages/ui/src/components/Header/Header.tsx +++ b/packages/ui/src/components/Header/Header.tsx @@ -29,9 +29,11 @@ const getScrollParent = (element: HTMLElement | null): Element | null => { let parent = element?.parentElement; while (parent) { - const { overflowY } = window.getComputedStyle(parent); + const { overflow, overflowX, overflowY } = window.getComputedStyle(parent); - if (/(auto|scroll|overlay)/.test(overflowY)) { + if ( + /(auto|scroll|overlay|hidden)/.test(`${overflow}${overflowX}${overflowY}`) + ) { return parent; } @@ -85,6 +87,8 @@ export const Header = (props: HeaderProps) => { () => (description ? renderInlineMarkdown(description) : null), [description], ); + // The sentinel sits directly before the sticky content and leaves the + // viewport when the content becomes stuck, letting us toggle stuck styling. const stickySentinelRef = useRef(null); const [isStuck, setIsStuck] = useState(false);