fix(ui): align sidebar trigger affordances

Align the Control UI and exported transcript sidebar triggers around a shared accessible hamburger affordance.
This commit is contained in:
Val Alexander
2026-04-29 14:33:39 -05:00
committed by GitHub
parent f55b810412
commit 323985f4ca
8 changed files with 134 additions and 37 deletions

View File

@@ -106,6 +106,42 @@
box-shadow: none;
}
.sidebar-menu-trigger {
align-items: center;
justify-content: center;
flex-shrink: 0;
width: 38px;
height: 38px;
padding: 0;
border: 1px solid color-mix(in srgb, var(--border) 84%, transparent);
border-radius: var(--radius-full);
background: color-mix(in srgb, var(--bg-elevated) 80%, transparent);
color: var(--muted);
cursor: pointer;
transition:
border-color var(--duration-fast) ease,
background var(--duration-fast) ease,
color var(--duration-fast) ease,
transform var(--duration-fast) ease;
box-shadow: inset 0 1px 0 color-mix(in srgb, white 8%, transparent);
}
.sidebar-menu-trigger:hover {
border-color: color-mix(in srgb, var(--border-strong) 88%, transparent);
background: color-mix(in srgb, var(--bg-hover) 84%, transparent);
color: var(--text);
transform: translateY(-1px);
}
.sidebar-menu-trigger:active {
transform: translateY(0);
}
.sidebar-menu-trigger:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
}
.topbar-nav-toggle {
display: none;
}

View File

@@ -126,16 +126,6 @@
/* Show the hamburger toggle at the same breakpoint where the drawer takes over. */
.topbar-nav-toggle {
display: inline-flex;
align-items: center;
justify-content: center;
width: 38px;
height: 38px;
padding: 0;
border: 1px solid color-mix(in srgb, var(--border) 84%, transparent);
border-radius: var(--radius-full);
background: color-mix(in srgb, var(--bg-elevated) 80%, transparent);
color: var(--muted);
box-shadow: inset 0 1px 0 color-mix(in srgb, white 8%, transparent);
}
.sidebar,
@@ -309,17 +299,6 @@
.topbar-nav-toggle {
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
width: 38px;
height: 38px;
padding: 0;
border: 1px solid color-mix(in srgb, var(--border) 84%, transparent);
border-radius: var(--radius-full);
background: color-mix(in srgb, var(--bg-elevated) 80%, transparent);
color: var(--muted);
box-shadow: inset 0 1px 0 color-mix(in srgb, white 8%, transparent);
}
.topbar-status {

View File

@@ -11,6 +11,15 @@ function readMobileCss(): string {
return readFileSync(cssPath!, "utf8");
}
function readLayoutCss(): string {
const cssPath = [
resolve(process.cwd(), "ui/src/styles/layout.css"),
resolve(process.cwd(), "..", "ui/src/styles/layout.css"),
].find((candidate) => existsSync(candidate));
expect(cssPath).toBeTruthy();
return readFileSync(cssPath!, "utf8");
}
describe("chat header responsive mobile styles", () => {
it("keeps the chat header and session controls from clipping on narrow widths", () => {
const css = readMobileCss();
@@ -21,3 +30,19 @@ describe("chat header responsive mobile styles", () => {
expect(css).toContain(".chat-controls__thinking-select");
});
});
describe("sidebar menu trigger styles", () => {
it("keeps the mobile sidebar trigger visibly interactive on hover and keyboard focus", () => {
const css = readLayoutCss();
expect(css).toContain(".sidebar-menu-trigger {");
expect(css).toContain("cursor: pointer;");
expect(css).toContain(".sidebar-menu-trigger:hover {");
expect(css).toContain("background: color-mix(in srgb, var(--bg-hover) 84%, transparent);");
expect(css).toContain("color: var(--text);");
expect(css).toContain(".sidebar-menu-trigger:focus-visible {");
expect(css).toContain("box-shadow: var(--focus-ring);");
expect(css).toContain(".topbar-nav-toggle {");
expect(css).toContain("display: none;");
});
});

View File

@@ -1341,7 +1341,7 @@ export function renderApp(state: AppViewState) {
<div class="topnav-shell">
<button
type="button"
class="topbar-nav-toggle"
class="sidebar-menu-trigger topbar-nav-toggle"
@click=${() => {
state.navDrawerOpen = !navDrawerOpen;
}}

View File

@@ -393,6 +393,7 @@ describe("control UI routing", () => {
}
expect(toggle.classList.contains("topbar-nav-toggle")).toBe(true);
expect(toggle.classList.contains("sidebar-menu-trigger")).toBe(true);
expect(actions.classList.contains("topnav-shell__actions")).toBe(true);
expect(topShell.firstElementChild).toBe(toggle);
expect(topShell.querySelector(".topbar-nav-toggle")).toBe(toggle);