fix(admin): remove spurious scrollbar from feedback tab strip

Marc reported a stray "mini scroll-button" on the right side of the
admin feedback inbox tab strip (Open / Acknowledged / Resolved /
Archived). The four short tabs do not horizontally overflow the
viewport, so the painted bar surprised him every time he opened the
inbox.

Root cause is a CSS overflow coupling: setting only `overflow-x: auto`
silently forces `overflow-y` to `auto` too, because a single axis
cannot be `visible` while the other clips. The strip is locked at
`h-9` (36 CSS px) but its badge children (`py-0.5` + text-xs digits)
ride 1-2 px taller than the strip on some glyph stacks, so the
implicit `overflow-y: auto` paints a tiny vertical scrollbar on the
right edge.

Fix is a single class on `tabsListVariants`: `overflow-y-hidden`
(plus `overflow-y-visible` in the vertical-orientation override so
vertical tab columns are not affected). Horizontal swipe and
keyboard-arrow scrolling still work — only the implicit y-axis bar
is suppressed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marc-André BombeckandClaude Opus 4.7 committed 2026-05-10 13:21:36 +02:00
1 parent ef67da5570
commit 088832aa39
2 files changed
+44 -1

No files matched your search

+33
View File
@@ -0,0 +1,33 @@
import { describe, expect, it } from "vitest";
import { renderToStaticMarkup } from "react-dom/server";
import { Tabs, TabsList, TabsTrigger } from "../tabs";
describe("<TabsList>", () => {
it("clamps the y-axis so a fixed-height strip never paints a vertical scrollbar", () => {
// Marc reported a stray "mini scroll-button" on the right side of
// the feedback inbox tab strip (Open / Acknowledged / Resolved /
// Archived). Root cause: `overflow-x-auto` couples to `overflow-y`
// — once one axis is `auto`, the other can no longer be `visible`,
// so the browser silently flips it to `auto` too. Combined with
// the fixed `h-9` strip and the badges (`py-0.5` + text-xs) that
// ride 1-2 px taller than the strip on some glyph stacks, a
// tiny vertical scrollbar paints on the right edge.
//
// Locking `overflow-y-hidden` on the strip kills the painted bar
// without touching the horizontal swipe behaviour.
const html = renderToStaticMarkup(
<Tabs defaultValue="a">
<TabsList>
<TabsTrigger value="a">A</TabsTrigger>
<TabsTrigger value="b">B</TabsTrigger>
</TabsList>
</Tabs>,
);
const list = html.match(/<div\b[^>]*data-slot="tabs-list"[^>]*>/);
expect(list).not.toBeNull();
expect(list![0]).toMatch(/\boverflow-y-hidden\b/);
// Horizontal scroll behaviour is still intact for long strips.
expect(list![0]).toMatch(/\boverflow-x-auto\b/);
});
});
+11 -1
View File
@@ -34,8 +34,18 @@ function Tabs({
// `whitespace-nowrap` so labels never word-wrap mid-tab. Desktop
// looks identical because the inline-flex shrink-wraps inside the
// wider container.
//
// v1.4.19 A7 — `overflow-y-hidden` clamps the y-axis. Browsers couple
// the two overflow axes once one is `auto`: setting only
// `overflow-x: auto` silently flips `overflow-y` to `auto` as well.
// Combined with the fixed `h-9` strip and badge children that ride
// ~1 px taller than the strip on some glyph stacks, a tiny painted
// vertical scrollbar appeared on the right edge of short strips
// (e.g. the admin feedback `Open / Acknowledged / Resolved /
// Archived` tabs Marc reported in v1.4.19). Locking the y-axis hides
// that bar without affecting horizontal swipe behaviour.
const tabsListVariants = cva(
"rounded-lg p-[3px] group-data-[orientation=horizontal]/tabs:h-9 data-[variant=line]:rounded-none group/tabs-list text-muted-foreground inline-flex w-fit max-w-full items-center justify-center overflow-x-auto touch-pan-x scrollbar-thin group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col group-data-[orientation=vertical]/tabs:overflow-x-visible",
"rounded-lg p-[3px] group-data-[orientation=horizontal]/tabs:h-9 data-[variant=line]:rounded-none group/tabs-list text-muted-foreground inline-flex w-fit max-w-full items-center justify-center overflow-x-auto overflow-y-hidden touch-pan-x scrollbar-thin group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col group-data-[orientation=vertical]/tabs:overflow-x-visible group-data-[orientation=vertical]/tabs:overflow-y-visible",
{
variants: {
variant: {