Unify sidebar chrome across themes (#2380)

This commit is contained in:
klopez4212
2026-07-22 10:58:29 -06:00
committed by GitHub
parent bd37a4d584
commit f8fd055b9f
2 changed files with 79 additions and 160 deletions
@@ -1,57 +1,4 @@
@layer components {
[data-testid="app-sidebar"] [data-testid="sidebar-pinned-header"],
[data-testid="app-sidebar"] [data-sidebar="footer"] {
--buzz-sidebar-edge-fade-size: 0.5rem;
--buzz-sidebar-edge-fade-surface: hsl(var(--sidebar-background));
--buzz-sidebar-edge-fade-transparent: hsl(var(--sidebar-background) / 0);
background: var(--buzz-sidebar-edge-fade-surface);
box-shadow: none;
isolation: isolate;
position: relative;
z-index: 30;
}
[data-testid="app-sidebar"] [data-testid="sidebar-pinned-header"]::before,
[data-testid="app-sidebar"] [data-sidebar="footer"]::before {
box-shadow: none;
content: "";
filter: none;
pointer-events: none;
position: absolute;
z-index: 5;
}
[data-testid="app-sidebar"] [data-testid="sidebar-pinned-header"] > *,
[data-testid="app-sidebar"] [data-sidebar="footer"] > * {
position: relative;
z-index: 10;
}
[data-testid="app-sidebar"] [data-testid="sidebar-pinned-header"]::before {
background: linear-gradient(
to bottom,
var(--buzz-sidebar-edge-fade-surface) 0%,
var(--buzz-sidebar-edge-fade-transparent) 100%
);
bottom: calc(var(--buzz-sidebar-edge-fade-size) * -1);
height: var(--buzz-sidebar-edge-fade-size);
left: 0;
right: 0;
}
[data-testid="app-sidebar"] [data-sidebar="footer"]::before {
background: linear-gradient(
to top,
var(--buzz-sidebar-edge-fade-surface) 0%,
var(--buzz-sidebar-edge-fade-transparent) 100%
);
height: var(--buzz-sidebar-edge-fade-size);
left: 0;
right: 0;
top: calc(var(--buzz-sidebar-edge-fade-size) * -1);
}
.buzz-side-panel-enter {
animation: buzz-side-panel-enter 260ms cubic-bezier(0.32, 0.72, 0, 1) both;
transform-origin: right center;
+79 -107
View File
@@ -22,6 +22,14 @@ async function storedSidebarWidth(page: Page) {
);
}
async function loadTheme(page: Page, theme: string) {
await page.addInitScript((selectedTheme) => {
window.localStorage.setItem("buzz-theme", selectedTheme);
}, theme);
await installMockBridge(page);
await page.goto("/");
}
// Regression guard for the "Leave channel" lockup: with two bundled copies of
// @radix-ui/react-dismissable-layer, opening a modal AlertDialog from a modal
// Radix ContextMenu left `pointer-events: none` stuck on <body> after the
@@ -215,124 +223,88 @@ test("channel owner can delete from the context menu", async ({ page }) => {
await expect(page.getByTestId("stream-list")).not.toContainText("general");
});
test("fades the pinned sidebar chrome edges outside the Buzz theme", async ({
page,
}) => {
// The Buzz default theme repaints the pinned header/footer with the
// sidebar gradient and drops the edge-fade pseudo-elements, so the fade
// treatment under test only exists on non-Buzz themes.
await page.addInitScript(() => {
window.localStorage.setItem("buzz-theme", "github-light");
});
await page.goto("/");
for (const theme of ["buzz", "github-light", "catppuccin-mocha"]) {
test(`uses the continuous sidebar surface in ${theme}`, async ({ page }) => {
await loadTheme(page, theme);
const pinnedHeader = page.getByTestId("sidebar-pinned-header");
const footer = page.locator(
'[data-testid="app-sidebar"] [data-sidebar="footer"]',
);
const channelContent = page.getByTestId("sidebar-channel-content");
await expect(pinnedHeader).toBeVisible();
await expect(footer).toBeVisible();
await expect(channelContent).toBeVisible();
const fadeStyles = await page.evaluate(() => {
const header = document.querySelector<HTMLElement>(
'[data-testid="app-sidebar"] [data-testid="sidebar-pinned-header"]',
);
const footerElement = document.querySelector<HTMLElement>(
const pinnedHeader = page.getByTestId("sidebar-pinned-header");
const footer = page.locator(
'[data-testid="app-sidebar"] [data-sidebar="footer"]',
);
const channelElement = document.querySelector<HTMLElement>(
'[data-testid="sidebar-channel-content"]',
);
const channelContent = page.getByTestId("sidebar-channel-content");
await expect(pinnedHeader).toBeVisible();
await expect(footer).toBeVisible();
await expect(channelContent).toBeVisible();
if (!header || !footerElement || !channelElement) {
throw new Error("Expected sidebar chrome elements to be rendered");
}
const chromeStyles = await page.evaluate(() => {
const header = document.querySelector<HTMLElement>(
'[data-testid="app-sidebar"] [data-testid="sidebar-pinned-header"]',
);
const footerElement = document.querySelector<HTMLElement>(
'[data-testid="app-sidebar"] [data-sidebar="footer"]',
);
const channelElement = document.querySelector<HTMLElement>(
'[data-testid="sidebar-channel-content"]',
);
const headerBefore = getComputedStyle(header, "::before");
const headerStyle = getComputedStyle(header);
const sidebarElement = header.closest<HTMLElement>(
'[data-sidebar="sidebar"]',
);
const sidebarStyle = sidebarElement
? getComputedStyle(sidebarElement)
: null;
const footerStyle = getComputedStyle(footerElement);
const footerBefore = getComputedStyle(footerElement, "::before");
const channelBefore = getComputedStyle(channelElement, "::before");
const channelAfter = getComputedStyle(channelElement, "::after");
const headerRect = header.getBoundingClientRect();
const footerRect = footerElement.getBoundingClientRect();
if (!header || !footerElement || !channelElement) {
throw new Error("Expected sidebar chrome elements to be rendered");
}
return {
channelAfterBackground: channelAfter.backgroundImage,
channelBeforeBackground: channelBefore.backgroundImage,
footerBackgroundColor: footerStyle.backgroundColor,
footerBackdropFilter: footerBefore.backdropFilter,
footerBackground: footerBefore.backgroundImage,
footerBoxShadow: footerStyle.boxShadow,
footerFadeBoxShadow: footerBefore.boxShadow,
footerFadeHeight: Number.parseFloat(footerBefore.height),
footerHeight: footerRect.height,
footerPointerEvents: footerBefore.pointerEvents,
footerPosition: footerBefore.position,
footerTopPx: Number.parseFloat(footerBefore.top),
footerZIndex: footerBefore.zIndex,
headerBackground: headerBefore.backgroundImage,
headerBackdropFilter: headerBefore.backdropFilter,
headerBackgroundColor: headerStyle.backgroundColor,
headerBottomPx: Number.parseFloat(headerBefore.bottom),
headerBoxShadow: headerStyle.boxShadow,
headerFadeBoxShadow: headerBefore.boxShadow,
headerFadeHeight: Number.parseFloat(headerBefore.height),
headerHeight: headerRect.height,
headerPointerEvents: headerBefore.pointerEvents,
headerPosition: headerBefore.position,
headerZIndex: headerBefore.zIndex,
sidebarBackgroundColor: sidebarStyle?.backgroundColor ?? null,
};
const headerBefore = getComputedStyle(header, "::before");
const headerStyle = getComputedStyle(header);
const footerStyle = getComputedStyle(footerElement);
const footerBefore = getComputedStyle(footerElement, "::before");
const channelBefore = getComputedStyle(channelElement, "::before");
const channelAfter = getComputedStyle(channelElement, "::after");
return {
channelAfterBackground: channelAfter.backgroundImage,
channelBeforeBackground: channelBefore.backgroundImage,
footerBackground: footerStyle.backgroundImage,
footerBackgroundColor: footerStyle.backgroundColor,
footerBeforeBackground: footerBefore.backgroundImage,
footerBeforeContent: footerBefore.content,
footerBoxShadow: footerStyle.boxShadow,
footerIsolation: footerStyle.isolation,
footerMarginTop: Number.parseFloat(footerStyle.marginTop),
footerZIndex: footerStyle.zIndex,
headerBackground: headerStyle.backgroundImage,
headerBackgroundColor: headerStyle.backgroundColor,
headerBeforeBackground: headerBefore.backgroundImage,
headerBeforeContent: headerBefore.content,
headerBoxShadow: headerStyle.boxShadow,
headerIsolation: headerStyle.isolation,
headerMarginBottom: Number.parseFloat(headerStyle.marginBottom),
headerZIndex: headerStyle.zIndex,
};
});
expect(chromeStyles.headerBackground).toBe("none");
expect(chromeStyles.headerBackgroundColor).toBe("rgba(0, 0, 0, 0)");
expect(chromeStyles.headerBeforeBackground).toBe("none");
expect(chromeStyles.headerBeforeContent).toBe("none");
expect(chromeStyles.headerBoxShadow).toBe("none");
expect(chromeStyles.headerIsolation).toBe("auto");
expect(chromeStyles.headerMarginBottom).toBe(0);
expect(chromeStyles.headerZIndex).toBe("auto");
expect(chromeStyles.footerBackground).toBe("none");
expect(chromeStyles.footerBackgroundColor).toBe("rgba(0, 0, 0, 0)");
expect(chromeStyles.footerBeforeBackground).toBe("none");
expect(chromeStyles.footerBeforeContent).toBe("none");
expect(chromeStyles.footerBoxShadow).toBe("none");
expect(chromeStyles.footerIsolation).toBe("auto");
expect(chromeStyles.footerMarginTop).toBe(0);
expect(chromeStyles.footerZIndex).toBe("auto");
expect(chromeStyles.channelBeforeBackground).toBe("none");
expect(chromeStyles.channelAfterBackground).toBe("none");
});
expect(fadeStyles.headerBackground).toContain("gradient");
expect(fadeStyles.headerBackgroundColor).toBe(
fadeStyles.sidebarBackgroundColor,
);
expect(fadeStyles.headerBackground).toContain("rgba");
expect(fadeStyles.headerBackground).toContain("0) 100%");
expect(fadeStyles.headerBackdropFilter).toBe("none");
expect(fadeStyles.headerBottomPx).toBeLessThan(0);
expect(fadeStyles.headerBoxShadow).toBe("none");
expect(fadeStyles.headerFadeBoxShadow).toBe("none");
expect(fadeStyles.headerFadeHeight).toBeLessThanOrEqual(10);
expect(fadeStyles.headerPointerEvents).toBe("none");
expect(fadeStyles.headerPosition).toBe("absolute");
expect(fadeStyles.headerZIndex).toBe("5");
expect(fadeStyles.footerBackground).toContain("gradient");
expect(fadeStyles.footerBackgroundColor).toBe(
fadeStyles.sidebarBackgroundColor,
);
expect(fadeStyles.footerBackground).toContain("rgba");
expect(fadeStyles.footerBackground).toContain("0) 100%");
expect(fadeStyles.footerBackdropFilter).toBe("none");
expect(fadeStyles.footerBoxShadow).toBe("none");
expect(fadeStyles.footerFadeBoxShadow).toBe("none");
expect(fadeStyles.footerFadeHeight).toBeLessThanOrEqual(10);
expect(fadeStyles.footerPointerEvents).toBe("none");
expect(fadeStyles.footerPosition).toBe("absolute");
expect(fadeStyles.footerTopPx).toBeLessThan(0);
expect(fadeStyles.footerZIndex).toBe("5");
expect(fadeStyles.channelBeforeBackground).toBe("none");
expect(fadeStyles.channelAfterBackground).toBe("none");
});
}
test("aligns the sidebar search with the channel title outside the Buzz theme", async ({
page,
}) => {
await page.addInitScript(() => {
window.localStorage.setItem("buzz-theme", "github-light");
});
await page.goto("/");
await loadTheme(page, "github-light");
await page.getByTestId("channel-general").click();
const root = page.locator("html");