From 0f22e1e9051663a627bee78d2be91e44459fa5b2 Mon Sep 17 00:00:00 2001
From: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Date: Thu, 3 Sep 2026 11:16:40 -0700
Subject: [PATCH] Fix table header transparency with opaque background (#18499)
Replace the translucent bg-muted/25 with an opaque color-mix blend
(40% muted on background) to ensure scrolled rows don't show through
the sticky header. Add test coverage for header styling and layout.
---
.../AutomationListTableHeader.test.tsx | 45 +++++++++++++++++++
src/renderer/src/lib/list-table-layout.ts | 4 +-
2 files changed, 47 insertions(+), 2 deletions(-)
create mode 100644 src/renderer/src/components/automations/AutomationListTableHeader.test.tsx
diff --git a/src/renderer/src/components/automations/AutomationListTableHeader.test.tsx b/src/renderer/src/components/automations/AutomationListTableHeader.test.tsx
new file mode 100644
index 00000000000..5c5bbe8e568
--- /dev/null
+++ b/src/renderer/src/components/automations/AutomationListTableHeader.test.tsx
@@ -0,0 +1,45 @@
+// @vitest-environment happy-dom
+
+import { cleanup, render, screen } from '@testing-library/react'
+import { afterEach, describe, expect, it } from 'vitest'
+import { AutomationListTableHeader } from './AutomationListTableHeader'
+import {
+ LIST_TABLE_HEADER_CLASS,
+ LIST_TABLE_STICKY_HEADER_CELL_CLASS
+} from '@/lib/list-table-layout'
+
+describe('AutomationListTableHeader', () => {
+ afterEach(cleanup)
+
+ it('renders all expected columns', () => {
+ render()
+
+ expect(screen.getByText('Name')).toBeDefined()
+ expect(screen.getByText('Schedule')).toBeDefined()
+ expect(screen.getByText('Project')).toBeDefined()
+ expect(screen.getByText('Host')).toBeDefined()
+ expect(screen.getByText('Next run')).toBeDefined()
+ expect(screen.getByText('Last run')).toBeDefined()
+ expect(screen.getByText('Status')).toBeDefined()
+ expect(screen.getByText('Agent')).toBeDefined()
+ expect(screen.getByText('Actions')).toBeDefined()
+ })
+
+ it('uses opaque background and sticky positioning on the header row', () => {
+ const { container } = render()
+ const header = container.firstElementChild as HTMLElement
+
+ expect(header.className).toContain(LIST_TABLE_HEADER_CLASS)
+ expect(header.className).toContain('sticky')
+ expect(header.className).toContain('top-0')
+ expect(header.className).toContain('bg-[color-mix(in_srgb,var(--muted)_40%,var(--background))]')
+ expect(header.className).not.toContain('bg-muted/25')
+ })
+
+ it('applies sticky cell styling to the first column', () => {
+ render()
+ const nameCell = screen.getByText('Name')
+
+ expect(nameCell.className).toBe(LIST_TABLE_STICKY_HEADER_CELL_CLASS)
+ })
+})
diff --git a/src/renderer/src/lib/list-table-layout.ts b/src/renderer/src/lib/list-table-layout.ts
index c40607ee944..89b85d0aab5 100644
--- a/src/renderer/src/lib/list-table-layout.ts
+++ b/src/renderer/src/lib/list-table-layout.ts
@@ -5,9 +5,9 @@
*/
export const LIST_TABLE_CONTAINER_CLASS = 'rounded-md border border-border/50 bg-muted/20'
-// Why: z-30 must beat the rows' sticky first cells (z-20) so the header still covers them.
+// Why: z-30 and opaque wash ensure scrolled rows cannot show through the sticky header.
export const LIST_TABLE_HEADER_CLASS =
- 'sticky top-0 z-30 h-8 items-center gap-3 border-b border-border/50 bg-muted/25 px-3 text-[11px] font-medium uppercase tracking-[0.08em] text-muted-foreground'
+ 'sticky top-0 z-30 h-8 items-center gap-3 border-b border-border/50 bg-[color-mix(in_srgb,var(--muted)_40%,var(--background))] px-3 text-[11px] font-medium uppercase tracking-[0.08em] text-muted-foreground'
// Why: keep keyboard-selected rows clear of the sticky table header.
export const LIST_TABLE_ROW_CLASS =