ITADN

[bug]: Base UI buttonVariants outline does not match Button outline unless wrapped with cn()

#10874OpenMeenic 创建于 2026-06-05
bug
M
Meeniccommented
### Describe the bug When using the Base UI `Button` component, `variant="outline"` renders correctly. However, when using the exported `buttonVariants()` helper directly on a `Link`, the `outline` variant can render visually like `ghost` because the generated class string contains conflicting border utilities. The generated Base UI button includes `border-transparent` in the base classes: ```ts const buttonVariants = cva( "group/button inline-flex shrink-0 items-center justify-center rounded-4xl border border-transparent ...", { variants: { variant: { outline: "border-border bg-background hover:bg-muted hover:text-foreground ...", }, }, }, ) ``` Inside the `Button` component, the classes are wrapped with `cn(...)`: ```tsx className={cn(buttonVariants({ variant, size, className }))} ``` So `tailwind-merge` resolves the conflict between `border-transparent` and `border-border`. But when using `buttonVariants()` directly, the classes are not merged: ```tsx <Link href="/" className={buttonVariants({ variant: "outline" })} > Back </Link> ``` This can cause `border-transparent` to win over `border-border`, making the outline link-button appear like a ghost button. Wrapping the result in `cn(...)` fixes it: ```tsx <Link href="/" className={cn(buttonVariants({ variant: "outline" }))} > Back </Link> ``` I would be happy to submit a PR. I think either of these fixes could work: 1. Update the Base UI Button docs/examples to wrap direct `buttonVariants()` usage with `cn(...)`. 2. Move `border-transparent` out of the base button classes and into the variants that need it, so `outline` does not rely on `tailwind-merge` to remove a conflicting class. ### Affected component/components Button ### How to reproduce 1. Create/init a shadcn project: ```bash bunx --bun shadcn@latest init ``` 2. Use the Base UI Luma preset. 3. Add the Base UI Button component. 4. Use the normal `Button` component: ```tsx <Button variant="outline"> Back </Button> ``` 5. Use `buttonVariants()` directly on a `Link`: ```tsx <Link href="/" className={buttonVariants({ variant: "outline" })} > Back </Link> ``` 6. Observe that the `Button` renders with an outline, but the `Link` can render visually like the `ghost` variant. 7. Wrap the `buttonVariants()` call with `cn(...)`: ```tsx <Link href="/" className={cn(buttonVariants({ variant: "outline" }))} > Back </Link> ``` 8. Observe that the `Link` now visually matches the `Button`. ### Codesandbox/StackBlitz link TODO: Add minimal reproduction. ### Logs ```bash No runtime error. This is a visual/class-merging issue. ``` ### System Info ```bash OS: Windows 11 bun: 1.3.3 shadcn: 4.10.0 Next.js: 16.2.6 Tailwind CSS: 4 Base: Base UI Style: Luma Browsers: Microsoft Edge, Chrome ``` ### Before submitting - [x] I've made research efforts and searched the documentation - [x] I've searched for existing issues
2 条评论