"use client";
import { createContext, useContext } from "react";
import { Tabs as TabsPrimitive } from "@base-ui/react/tabs";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "./cn";
/*
* The primitive owns arrow-key navigation, the roving tabindex and the
* tab/panel wiring that keeps a screen reader in step.
*
* Note the state attribute: Tabs marks the active tab with `data-active`,
* while Checkbox and Radio use `data-selected`. Base UI is not uniform here, so
* styling one from memory of another silently produces a tab row where nothing
* ever looks selected.
*
* Two variants because the products genuinely differ in kind, not degree:
* `line` is an underlined row that sits on a page rule, `solid` is a filled
* segmented control. Neither is a brand default — a product picks per surface.
*/
const tabsListVariants = cva(
["inline-flex max-w-full items-center overflow-x-auto"],
{
variants: {
variant: {
line: "gap-6 border-b border-(color:--color-border) bg-transparent",
solid:
"gap-1 rounded-(--tabs-radius) bg-(color:--color-action-neutral) p-[3px]",
},
},
defaultVariants: { variant: "line" },
},
);
const tabsTriggerVariants = cva(
[
"group inline-flex items-center justify-center gap-2 whitespace-nowrap",
"text-(length:--font-size-control) font-medium",
"transition-colors duration-(--motion-duration) ease-(--motion-ease)",
"outline-none focus-visible:outline-solid",
"focus-visible:outline-(length:--focus-ring-width)",
"focus-visible:outline-(color:--color-focus-ring)",
"disabled:pointer-events-none disabled:opacity-50",
"hover:text-(color:--color-content)",
"data-active:text-(color:--color-content)",
],
{
variants: {
variant: {
line: [
"-mb-px border-b-2 border-transparent px-0 pt-1 pb-2",
"text-(color:--color-content-muted)",
"focus-visible:outline-offset-2",
"data-active:border-(color:--color-content)",
],
solid: [
"rounded-(--tabs-item-radius) px-2.5 py-1",
"text-(color:--color-action-neutral-content)",
"focus-visible:-outline-offset-1",
"data-active:bg-(color:--color-surface)",
],
},
},
defaultVariants: { variant: "line" },
},
);
function Tabs({ className, ...props }: TabsPrimitive.Root.Props) {
return (
);
}
/*
* The list publishes its variant so triggers inherit it. Without this a
* `solid` list full of default triggers renders an underlined row inside a
* filled container -- each part defaults independently, and the mismatch only
* shows once a product picks the non-default variant. Enterprise never hit it
* because it uses `line`, the default for both. A trigger can still override
* explicitly.
*/
const TabsVariantContext = createContext<
VariantProps["variant"] | undefined
>(undefined);
export interface TabsListProps
extends Omit,
VariantProps {
className?: string;
}
function TabsList({ className, variant, ...props }: TabsListProps) {
return (
);
}
export interface TabsTriggerProps
extends Omit,
VariantProps {
className?: string;
}
function TabsTrigger({ className, variant, ...props }: TabsTriggerProps) {
const inherited = useContext(TabsVariantContext);
return (
);
}
function TabsContent({
className,
keepMounted = false,
...props
}: TabsPrimitive.Panel.Props) {
return (
);
}
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants, tabsTriggerVariants };