[bug]: Base UI buttonVariants outline does not match Button outline unless wrapped with cn()
bug
### 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 条评论