From 692b14d47b884cdcfecf3ac3c207daaed3f61912 Mon Sep 17 00:00:00 2001 From: Saxon Fletcher Date: Fri, 9 Oct 2026 16:12:47 +1000 Subject: [PATCH] fix(ui): opaque sticky table column hover (#47668) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem On tables with a sticky last column (Database Extensions, OAuth Apps, Custom Auth Providers), hovering a row lets scrolled cell content show through the sticky column. Root cause: row hover uses `bg-surface-200`, which maps to `--muted` (alpha). That works for normal cells (nothing behind them) but sticky cells sit above horizontally scrolled content, so the tint is see-through. ## Solution In `ShadowScrollArea` when `stickyLastColumn` is set: - Keep an opaque `bg-surface-100` base on sticky cells - Composite the muted hover tint via `background-image` so hover matches `TableRow` without becoming transparent Also: - Extensions: use `Table containerProps={{ stickyLastColumn: true }}` instead of nesting a second `ShadowScrollArea` (same pattern as OAuth / Custom Auth) - OAuth Apps and Custom Auth Providers: drop redundant per-cell `bg-surface-100` / `hover:bg-surface-200` so sticky hover comes only from `ShadowScrollArea` | Before | After | | --- | --- | | 27097 | 94456 | ## Review instructions 1. Open Database Extensions. Narrow the viewport or scroll the table horizontally so the Enabled column is sticky. 2. Hover a row while Links or description content sits under the Enabled column. The sticky cell should stay opaque and match the row hover tint (light and dark). 3. Optionally repeat on Auth → OAuth Apps and Auth → Custom Providers (actions column). Co-authored-by: Danny White <3104761+dnywh@users.noreply.github.com> --- .../CustomAuthProvidersList.tsx | 2 +- .../Auth/OAuthApps/OAuthAppsList.tsx | 2 +- .../Database/Extensions/ExtensionRow.tsx | 6 +- .../Database/Extensions/Extensions.tsx | 79 ++++++++----------- .../src/components/ShadowScrollArea/index.tsx | 11 ++- 5 files changed, 48 insertions(+), 52 deletions(-) diff --git a/apps/studio/components/interfaces/Auth/CustomAuthProviders/CustomAuthProvidersList.tsx b/apps/studio/components/interfaces/Auth/CustomAuthProviders/CustomAuthProvidersList.tsx index b0c99c06408..f6a50554d08 100644 --- a/apps/studio/components/interfaces/Auth/CustomAuthProviders/CustomAuthProvidersList.tsx +++ b/apps/studio/components/interfaces/Auth/CustomAuthProviders/CustomAuthProvidersList.tsx @@ -468,7 +468,7 @@ export const CustomAuthProvidersList = () => { {provider.enabled ? 'Enabled' : 'Disabled'} - +
diff --git a/apps/studio/components/interfaces/Auth/OAuthApps/OAuthAppsList.tsx b/apps/studio/components/interfaces/Auth/OAuthApps/OAuthAppsList.tsx index 88699627728..168e6dc199c 100644 --- a/apps/studio/components/interfaces/Auth/OAuthApps/OAuthAppsList.tsx +++ b/apps/studio/components/interfaces/Auth/OAuthApps/OAuthAppsList.tsx @@ -403,7 +403,7 @@ export const OAuthAppsList = () => { labelFormat="D MMM, YYYY" /> - +
diff --git a/apps/studio/components/interfaces/Database/Extensions/ExtensionRow.tsx b/apps/studio/components/interfaces/Database/Extensions/ExtensionRow.tsx index efef982bf33..99d18c684a9 100644 --- a/apps/studio/components/interfaces/Database/Extensions/ExtensionRow.tsx +++ b/apps/studio/components/interfaces/Database/Extensions/ExtensionRow.tsx @@ -149,10 +149,10 @@ export const ExtensionRow = ({ extension }: ExtensionRowProps) => { {/* - [Joshen] The div child here and all these classes is to properly add a left border - to make the sticky column more distinct + Left border on the absolute child makes the sticky column more distinct. + Sticky positioning/background come from ShadowScrollArea's stickyLastColumn. */} - +
{isDisabling ? ( diff --git a/apps/studio/components/interfaces/Database/Extensions/Extensions.tsx b/apps/studio/components/interfaces/Database/Extensions/Extensions.tsx index 6fa4a6c3913..6ebb730030f 100644 --- a/apps/studio/components/interfaces/Database/Extensions/Extensions.tsx +++ b/apps/studio/components/interfaces/Database/Extensions/Extensions.tsx @@ -8,7 +8,6 @@ import { InputGroup, InputGroupAddon, InputGroupInput, - ShadowScrollArea, Table, TableBody, TableCell, @@ -107,51 +106,43 @@ export const Extensions = () => { ) : ( - - - +
+ + + Name + + Version + + Schema + + Description + + Used by + Links + +
+ Enabled +
+
+
+
+ + {[...enabledExtensions, ...disabledExtensions].map((extension) => ( + + ))} + {extensions.length === 0 && ( - Name - - Version - - Schema - - Description - - Used by - Links - {/* - [Joshen] All these classes are just to make the last column sticky - I reckon we can pull these out into the Table component where we can declare - sticky columns via props, but we can do that if we start to have more tables - in the dashboard with sticky columns - */} - -
- Enabled -
-
+ + setFilterString('')} + /> +
- - - {[...enabledExtensions, ...disabledExtensions].map((extension) => ( - - ))} - {extensions.length === 0 && ( - - - setFilterString('')} - /> - - - )} - -
-
+ )} + +
)} diff --git a/packages/ui/src/components/ShadowScrollArea/index.tsx b/packages/ui/src/components/ShadowScrollArea/index.tsx index 46f5e73d611..cfe489176cf 100644 --- a/packages/ui/src/components/ShadowScrollArea/index.tsx +++ b/packages/ui/src/components/ShadowScrollArea/index.tsx @@ -57,10 +57,15 @@ const ShadowScrollArea = React.forwardRef 'w-full overflow-auto', stickyLastColumn && [ '[&_tr>*:last-child]:sticky [&_tr>*:last-child]:z-38 [&_tr>*:last-child]:right-0', - '[&_tr:hover>*:last-child]:bg-transparent', - '[&_th>*:last-child]:bg-surface-100', + // Sticky cells need an opaque base. `bg-surface-200` / `--muted` is alpha-based, + // so using it alone lets scrolled row content show through on hover. Keep the + // opaque surface-100 base and composite muted via background-image so hover + // matches TableRow without becoming transparent. + '[&_td:last-child]:bg-surface-100', + '[&_tr:hover>td:last-child]:!bg-surface-100', + '[&_tr:hover>td:last-child]:![background-image:linear-gradient(var(--background-surface-200),var(--background-surface-200))]', + '[&_th:last-child]:bg-200', stickyColumnShadow, - hasHorizontalScroll && '[&_tr:hover>td:last-child]:!bg-surface-200', ], canScrollRight && '[&_td]:before:opacity-100 [&_tr>*:last-child]:before:opacity-100 [&_th:last-child]:before:opacity-100',