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:
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(...):
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:
<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:
<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:
- Update the Base UI Button docs/examples to wrap direct
buttonVariants() usage with cn(...).
- 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
- Create/init a shadcn project:
bunx --bun shadcn@latest init
- Use the Base UI Luma preset.
- Add the Base UI Button component.
- Use the normal
Button component:
<Button variant="outline">
Back
</Button>
- Use
buttonVariants() directly on a Link:
<Link
href="/"
className={buttonVariants({ variant: "outline" })}
>
Back
</Link>
- Observe that the
Button renders with an outline, but the Link can render visually like the ghost variant.
- Wrap the
buttonVariants() call with cn(...):
<Link
href="/"
className={cn(buttonVariants({ variant: "outline" }))}
>
Back
</Link>
- Observe that the
Link now visually matches the Button.
Codesandbox/StackBlitz link
Minimal repro repo: https://github.com/Meenic/repro-shadcn-buttonvariants-outline.git
Live repro: https://repro-shadcn-buttonvariants-outline.vercel.app/
Logs
No runtime error. This is a visual/class-merging issue.
System Info
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
Describe the bug
When using the Base UI
Buttoncomponent,variant="outline"renders correctly.However, when using the exported
buttonVariants()helper directly on aLink, theoutlinevariant can render visually likeghostbecause the generated class string contains conflicting border utilities.The generated Base UI button includes
border-transparentin the base classes:Inside the
Buttoncomponent, the classes are wrapped withcn(...):So
tailwind-mergeresolves the conflict betweenborder-transparentandborder-border.But when using
buttonVariants()directly, the classes are not merged:This can cause
border-transparentto win overborder-border, making the outline link-button appear like a ghost button.Wrapping the result in
cn(...)fixes it:I would be happy to submit a PR. I think either of these fixes could work:
buttonVariants()usage withcn(...).border-transparentout of the base button classes and into the variants that need it, sooutlinedoes not rely ontailwind-mergeto remove a conflicting class.Affected component/components
Button
How to reproduce
Buttoncomponent:buttonVariants()directly on aLink:Buttonrenders with an outline, but theLinkcan render visually like theghostvariant.buttonVariants()call withcn(...):Linknow visually matches theButton.Codesandbox/StackBlitz link
Minimal repro repo: https://github.com/Meenic/repro-shadcn-buttonvariants-outline.git
Live repro: https://repro-shadcn-buttonvariants-outline.vercel.app/
Logs
System Info
Before submitting