initium
blockbutton-showcasesource: ok · refreshed
Button Showcase
Button section with background contrast section.
npx shadcn add "https://app.initium.sh/r/styles/{style}/button-showcase.json"free
No selection tags yet — empty means no evidence, not lack of support.
Dependencies
Bundle weight · lighticonslucide-react
Files9 · source cached 10/8/2026, 1:38:23 AM
components/pro-blocks/buttons/button-showcase.tsx
import type { VariantProps } from "class-variance-authority"
import { PlusIcon } from "lucide-react"
import { Button as Button3D } from "@/components/ui/buttons/button-3d"
import { Button as ButtonBold } from "@/components/ui/buttons/button-bold"
import { Button as ButtonBoldSkewed } from "@/components/ui/buttons/button-bold-skewed"
import { Button as ButtonClipped } from "@/components/ui/buttons/button-clipped"
import {
Button as ButtonDefault,
buttonVariants,
} from "@/components/ui/buttons/button-default"
import { Button as ButtonGradient } from "@/components/ui/buttons/button-gradient"
import { Button as ButtonShadow } from "@/components/ui/buttons/button-shadow"
import { Button as ButtonStrongOutline } from "@/components/ui/buttons/button-strong-outline"
type ButtonVariant = NonNullable<VariantProps<typeof buttonVariants>["variant"]>
type ButtonSize = NonNullable<VariantProps<typeof buttonVariants>["size"]>
const BUTTON_STYLES = [
{ label: "Default", Button: ButtonDefault, source: "button-default.tsx" },
{ label: "Bold", Button: ButtonBold, source: "button-bold.tsx" },
{ label: "3D", Button: Button3D, source: "button-3d.tsx" },
{ label: "Gradient", Button: ButtonGradient, source: "button-gradient.tsx" },
{ label: "Shadow", Button: ButtonShadow, source: "button-shadow.tsx" },
{ label: "Clipped", Button: ButtonClipped, source: "button-clipped.tsx" },
{
label: "Strong Outline",
Button: ButtonStrongOutline,
source: "button-strong-outline.tsx",
},
{
label: "Bold Skewed",
Button: ButtonBoldSkewed,
source: "button-bold-skewed.tsx",
},
] as const
const VARIANTS = [
"default",
"outline",
"secondary",
"destructive",
"ghost",
"link",
] as const satisfies readonly ButtonVariant[]
const SIZES = [
{ label: "default", size: "default" },
{ label: "xs", size: "xs" },
{ label: "sm", size: "sm" },
{ label: "lg", size: "lg" },
{ label: "icon", size: "icon", iconOnly: true },
{ label: "icon-xs", size: "icon-xs", iconOnly: true },
{ label: "icon-sm", size: "icon-sm", iconOnly: true },
{ label: "icon-lg", size: "icon-lg", iconOnly: true },
] as const satisfies readonly {
label: string
size: ButtonSize
iconOnly?: boolean
}[]
export function ButtonShowcase() {
return (
<section
className="bg-background"
aria-labelledby="button-showcase-heading"
>
<div className="wrapper py-16 sm:py-24">
<div className="tagline">Buttons</div>
<h1 id="button-showcase-heading" className="heading-lg mb-3">
Button styles
</h1>
<p className="paragraph mb-14 max-w-2xl">
Standalone button files paired with each color and size variant. Ghost
and link variants stay flat across elevated styles.
</p>
<div className="flex flex-col gap-16">
{BUTTON_STYLES.map(({ label: styleLabel, Button, source }) => (
<div key={source}>
<h2 className="heading-sm mb-2">{styleLabel}</h2>
<p className="paragraph-sm mb-6 font-mono text-xs">{source}</p>
<div className="flex flex-col gap-8">
<div>
<h3 className="font-mono text-xs text-muted-foreground uppercase">
Variants
</h3>
<div className="mt-3 flex flex-wrap gap-8">
{VARIANTS.map((variant) => (
<div
key={variant}
className="flex min-w-[7.5rem] flex-col items-center gap-3"
>
<Button variant={variant}>Button</Button>
<span className="font-mono text-xs text-muted-foreground">
{variant}
</span>
</div>
))}
</div>
</div>
<div>
<h3 className="font-mono text-xs text-muted-foreground uppercase">
Sizes
</h3>
<div className="mt-3 flex flex-wrap items-end gap-8">
{SIZES.map((sizeConfig) => {
const { label, size } = sizeConfig
const iconOnly =
"iconOnly" in sizeConfig && sizeConfig.iconOnly
return (
<div
key={size}
className="flex min-w-[7.5rem] flex-col items-center gap-3"
>
<Button
size={size}
aria-label={
iconOnly
? `${styleLabel} ${label} button size`
: undefined
}
>
{iconOnly ? <PlusIcon aria-hidden /> : "Button"}
</Button>
<span className="font-mono text-xs text-muted-foreground">
{label}
</span>
</div>
)
})}
</div>
</div>
</div>
</div>
))}
</div>
</div>
</section>
)
}
components/ui/buttons/button-default.tsx
import { cva, type VariantProps } from "class-variance-authority"
import { Button as ButtonPrimitive } from "@base-ui/react/button"
import { isValidElement } from "react"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"group/button inline-flex shrink-0 items-center justify-center rounded-4xl border bg-clip-padding font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:ring-3 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0",
{
variants: {
variant: {
default:
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80 focus-visible:border-ring focus-visible:ring-ring/30",
outline:
"border-border text-foreground hover:bg-muted/30 hover:text-foreground focus-visible:border-ring focus-visible:ring-ring/30 aria-expanded:bg-muted aria-expanded:text-foreground dark:bg-transparent dark:hover:bg-input/30",
secondary:
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/60 focus-visible:border-ring focus-visible:ring-ring/30 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
ghost:
"border-transparent text-foreground hover:bg-muted/30 hover:text-foreground focus-visible:border-ring focus-visible:ring-ring/30 aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
destructive:
"border-transparent bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
link: "border-transparent text-primary underline-offset-4 hover:underline focus-visible:border-ring focus-visible:ring-ring/30",
},
size: {
default:
"h-10 gap-1.5 px-3.5 text-sm has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5 [&_svg:not([class*='size-'])]:size-4",
xs: "h-6 gap-1 px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
sm: "h-8 gap-1 px-3 text-sm has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-4",
lg: "h-12 gap-2 px-5 text-base has-data-[icon=inline-end]:pr-4 has-data-[icon=inline-start]:pl-4 [&_svg:not([class*='size-'])]:size-4",
icon: "size-10 text-sm [&_svg:not([class*='size-'])]:size-4",
"icon-xs": "size-6 text-sm [&_svg:not([class*='size-'])]:size-3",
"icon-sm": "size-8 text-sm [&_svg:not([class*='size-'])]:size-4",
"icon-lg": "size-12 text-sm [&_svg:not([class*='size-'])]:size-4",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
function getNativeButton(props: ButtonPrimitive.Props) {
if (props.nativeButton !== undefined || !isValidElement(props.render)) {
return props.nativeButton
}
return props.render.type === "button"
}
function Button({
className,
variant = "default",
size = "default",
...props
}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
return (
<ButtonPrimitive
data-slot="button"
data-variant={variant}
data-size={size}
className={cn(buttonVariants({ variant, size, className }))}
{...props}
nativeButton={getNativeButton(props)}
/>
)
}
export { Button, buttonVariants }
components/ui/buttons/button-bold.tsx
import { cva, type VariantProps } from "class-variance-authority"
import { Button as ButtonPrimitive } from "@base-ui/react/button"
import { isValidElement } from "react"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"group/button inline-flex shrink-0 items-center justify-center rounded-4xl border bg-clip-padding font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:ring-3 active:not-aria-[haspopup]:!translate-y-1 active:not-aria-[haspopup]:translate-y-1 active:not-aria-[haspopup]:shadow-none disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0",
{
variants: {
variant: {
default:
"border-primary bg-primary text-primary-foreground shadow-[0_4px_0_0_color-mix(in_oklch,var(--primary)_70%,#000)] hover:opacity-80 focus-visible:border-ring focus-visible:ring-ring/30",
outline:
"border-border bg-card text-foreground shadow-[0_4px_0_0_var(--border)] hover:bg-accent/30 hover:text-foreground hover:shadow-[0_4px_0_0_color-mix(in_oklch,var(--border)_90%,#000)] focus-visible:border-ring focus-visible:ring-ring/30 aria-expanded:bg-muted aria-expanded:text-foreground dark:bg-transparent dark:hover:bg-input/30",
secondary:
"border-secondary bg-secondary text-secondary-foreground shadow-[0_4px_0_0_color-mix(in_oklch,var(--secondary-foreground)_25%,transparent)] hover:bg-secondary/70 hover:shadow-[0_4px_0_0_color-mix(in_oklch,var(--secondary-foreground)_35%,transparent)] focus-visible:border-ring focus-visible:ring-ring/30 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground dark:shadow-[0_4px_0_0_color-mix(in_oklch,var(--secondary-foreground)_20%,transparent)]",
ghost:
"!border-transparent text-foreground !shadow-none hover:bg-accent/30 hover:text-foreground focus-visible:border-ring focus-visible:ring-ring/30 active:!translate-y-px aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
destructive:
"border-destructive/10 bg-destructive/10 text-destructive shadow-[0_4px_0_0_color-mix(in_oklch,var(--destructive)_25%,transparent)] hover:border-destructive/15 hover:bg-destructive/15 hover:shadow-[0_4px_0_0_color-mix(in_oklch,var(--destructive)_50%,#fff)] focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:border-destructive/60 dark:bg-destructive/60 dark:text-white dark:shadow-[0_4px_0_0_color-mix(in_oklch,var(--destructive)_50%,#000)] dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
link: "!border-transparent text-primary underline-offset-4 !shadow-none hover:underline focus-visible:border-ring focus-visible:ring-ring/30 active:!translate-y-px",
},
size: {
default:
"h-10 gap-1.5 px-3.5 text-sm has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5 [&_svg:not([class*='size-'])]:size-4",
xs: "h-6 gap-1 px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
sm: "h-8 gap-1 px-3 text-sm has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-4",
lg: "h-12 gap-2 px-5 text-base has-data-[icon=inline-end]:pr-4 has-data-[icon=inline-start]:pl-4 [&_svg:not([class*='size-'])]:size-4",
icon: "size-10 text-sm [&_svg:not([class*='size-'])]:size-4",
"icon-xs": "size-6 text-sm [&_svg:not([class*='size-'])]:size-3",
"icon-sm": "size-8 text-sm [&_svg:not([class*='size-'])]:size-4",
"icon-lg": "size-12 text-sm [&_svg:not([class*='size-'])]:size-4",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
function getNativeButton(props: ButtonPrimitive.Props) {
if (props.nativeButton !== undefined || !isValidElement(props.render)) {
return props.nativeButton
}
return props.render.type === "button"
}
function Button({
className,
variant = "default",
size = "default",
...props
}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
return (
<ButtonPrimitive
data-slot="button"
data-variant={variant}
data-size={size}
className={cn(buttonVariants({ variant, size, className }))}
{...props}
nativeButton={getNativeButton(props)}
/>
)
}
export { Button, buttonVariants }
components/ui/buttons/button-3d.tsx
import { cva, type VariantProps } from "class-variance-authority"
import { Button as ButtonPrimitive } from "@base-ui/react/button"
import { isValidElement } from "react"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"group/button inline-flex shrink-0 items-center justify-center rounded-4xl border bg-clip-padding font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:ring-3 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0",
{
variants: {
variant: {
default:
"border-[color-mix(in_oklch,var(--primary)_75%,#000)] bg-primary text-primary-foreground shadow-[inset_0_1.5px_0px_0_color-mix(in_oklch,var(--primary)_65%,#fff),inset_0_-1.5px_0px_0_color-mix(in_oklch,var(--primary)_75%,#000)] hover:bg-primary/80 focus-visible:border-ring focus-visible:ring-ring/30",
outline:
"border-border bg-card text-foreground hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-ring/30 aria-expanded:bg-muted aria-expanded:text-foreground dark:bg-transparent dark:shadow-[inset_0_1.5px_0px_0_color-mix(in_oklch,white_25%,transparent)] dark:hover:bg-input/30",
secondary:
"border-secondary-foreground/15 bg-secondary text-secondary-foreground shadow-[inset_0_1.5px_0px_0_white,inset_0_-1.5px_0px_0_color-mix(in_oklch,var(--secondary-foreground)_15%,transparent)] hover:opacity-80 focus-visible:border-ring focus-visible:ring-ring/30 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground dark:border-[color-mix(in_oklch,var(--secondary)_90%,transparent)] dark:shadow-[inset_0_1.5px_0px_0_color-mix(in_oklch,white_25%,transparent),inset_0_-1.5px_0px_0_color-mix(in_oklch,black_25%,transparent)]",
ghost:
"border-transparent text-foreground hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-ring/30 aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
destructive:
"border-destructive/20 bg-destructive/10 text-destructive shadow-[inset_0_1.5px_0px_0_color-mix(in_oklch,var(--destructive)_5%,#fff),inset_0_-1.5px_0px_0_color-mix(in_oklch,var(--destructive)_25%,#fff)] hover:opacity-80 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:border-[color-mix(in_oklch,var(--destructive)_38%,#000)] dark:bg-destructive/25 dark:shadow-[inset_0_1.5px_0px_0_color-mix(in_oklch,white_15%,transparent),inset_0_-1.5px_0px_0_color-mix(in_oklch,black_28%,transparent)] dark:focus-visible:ring-destructive/40",
link: "border-transparent text-primary underline-offset-4 hover:underline focus-visible:border-ring focus-visible:ring-ring/30",
},
size: {
default:
"h-10 gap-1.5 px-3.5 text-sm has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5 [&_svg:not([class*='size-'])]:size-4",
xs: "h-6 gap-1 px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
sm: "h-8 gap-1 px-3 text-sm has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-4",
lg: "h-12 gap-2 px-5 text-base has-data-[icon=inline-end]:pr-4 has-data-[icon=inline-start]:pl-4 [&_svg:not([class*='size-'])]:size-4",
icon: "size-10 text-sm [&_svg:not([class*='size-'])]:size-4",
"icon-xs": "size-6 text-sm [&_svg:not([class*='size-'])]:size-3",
"icon-sm": "size-8 text-sm [&_svg:not([class*='size-'])]:size-4",
"icon-lg": "size-12 text-sm [&_svg:not([class*='size-'])]:size-4",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
function getNativeButton(props: ButtonPrimitive.Props) {
if (props.nativeButton !== undefined || !isValidElement(props.render)) {
return props.nativeButton
}
return props.render.type === "button"
}
function Button({
className,
variant = "default",
size = "default",
...props
}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
return (
<ButtonPrimitive
data-slot="button"
data-variant={variant}
data-size={size}
className={cn(buttonVariants({ variant, size, className }))}
{...props}
nativeButton={getNativeButton(props)}
/>
)
}
export { Button, buttonVariants }
components/ui/buttons/button-gradient.tsx
import { cva, type VariantProps } from "class-variance-authority"
import { Button as ButtonPrimitive } from "@base-ui/react/button"
import { isValidElement } from "react"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"group/button inline-flex shrink-0 items-center justify-center rounded-4xl border bg-clip-padding font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:ring-3 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0",
{
variants: {
variant: {
default:
"border-[color-mix(in_oklch,var(--primary)_95%,#000)] bg-gradient-to-b from-[color-mix(in_oklch,var(--primary)_50%,#fff)] to-primary text-primary-foreground hover:from-[color-mix(in_oklch,var(--primary)_20%,#fff)] hover:to-primary focus-visible:border-ring focus-visible:ring-ring/30",
outline:
"border-border bg-card text-foreground hover:bg-accent/30 hover:text-foreground focus-visible:border-ring focus-visible:ring-ring/30 aria-expanded:bg-muted aria-expanded:text-foreground dark:bg-transparent dark:hover:bg-input/30",
secondary:
"dark:to:secondary-10 border-[color-mix(in_oklch,var(--secondary)_95%,#000)] bg-gradient-to-b from-secondary/10 to-secondary text-secondary-foreground hover:from-secondary/10 hover:to-secondary-foreground/10 focus-visible:border-ring focus-visible:ring-ring/30 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground dark:border-[color-mix(in_oklch,var(--secondary)_90%,#fff)] dark:from-secondary dark:to-secondary/20 dark:hover:from-secondary-foreground/18",
ghost:
"border-transparent text-foreground hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-ring/30 aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
destructive:
"border-transparent bg-gradient-to-b from-destructive/20 to-destructive text-white hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
link: "border-transparent text-primary underline-offset-4 hover:underline focus-visible:border-ring focus-visible:ring-ring/30",
},
size: {
default:
"h-10 gap-1.5 px-3.5 text-sm has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5 [&_svg:not([class*='size-'])]:size-4",
xs: "h-6 gap-1 px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
sm: "h-8 gap-1 px-3 text-sm has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-4",
lg: "h-12 gap-2 px-5 text-base has-data-[icon=inline-end]:pr-4 has-data-[icon=inline-start]:pl-4 [&_svg:not([class*='size-'])]:size-4",
icon: "size-10 text-sm [&_svg:not([class*='size-'])]:size-4",
"icon-xs": "size-6 text-sm [&_svg:not([class*='size-'])]:size-3",
"icon-sm": "size-8 text-sm [&_svg:not([class*='size-'])]:size-4",
"icon-lg": "size-12 text-sm [&_svg:not([class*='size-'])]:size-4",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
function getNativeButton(props: ButtonPrimitive.Props) {
if (props.nativeButton !== undefined || !isValidElement(props.render)) {
return props.nativeButton
}
return props.render.type === "button"
}
function Button({
className,
variant = "default",
size = "default",
...props
}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
return (
<ButtonPrimitive
data-slot="button"
data-variant={variant}
data-size={size}
className={cn(buttonVariants({ variant, size, className }))}
{...props}
nativeButton={getNativeButton(props)}
/>
)
}
export { Button, buttonVariants }
components/ui/buttons/button-shadow.tsx
import { cva, type VariantProps } from "class-variance-authority"
import { Button as ButtonPrimitive } from "@base-ui/react/button"
import { isValidElement } from "react"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"group/button inline-flex shrink-0 items-center justify-center rounded-4xl bg-clip-padding whitespace-nowrap transition-all outline-none select-none focus-visible:ring-3 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0",
{
variants: {
variant: {
default:
"bg-primary font-medium text-primary-foreground shadow-xs hover:bg-primary/80 focus-visible:border-ring focus-visible:ring-ring/30",
outline:
"border border-border bg-background font-medium text-foreground shadow-xs hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-ring/30 aria-expanded:bg-muted aria-expanded:text-foreground dark:bg-transparent dark:hover:bg-input/30",
secondary:
"border border-border bg-secondary font-medium text-secondary-foreground shadow-xs hover:bg-secondary/80 focus-visible:border-ring focus-visible:ring-ring/30 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
ghost:
"font-normal text-foreground hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-ring/30 aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
destructive:
"border border-destructive/20 bg-destructive/10 font-medium text-destructive shadow-xs hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
link: "font-medium text-primary underline-offset-4 hover:underline focus-visible:border-ring focus-visible:ring-ring/30",
},
size: {
default:
"h-10 gap-1.5 px-3.5 text-sm has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5 [&_svg:not([class*='size-'])]:size-4",
xs: "h-6 gap-1 px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
sm: "h-8 gap-1 px-3 text-sm has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-4",
lg: "h-12 gap-2 px-5 text-base has-data-[icon=inline-end]:pr-4 has-data-[icon=inline-start]:pl-4 [&_svg:not([class*='size-'])]:size-4",
icon: "size-10 text-sm [&_svg:not([class*='size-'])]:size-4",
"icon-xs": "size-6 text-sm [&_svg:not([class*='size-'])]:size-3",
"icon-sm": "size-8 text-sm [&_svg:not([class*='size-'])]:size-4",
"icon-lg": "size-12 text-sm [&_svg:not([class*='size-'])]:size-4",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
function getNativeButton(props: ButtonPrimitive.Props) {
if (props.nativeButton !== undefined || !isValidElement(props.render)) {
return props.nativeButton
}
return props.render.type === "button"
}
function Button({
className,
variant = "default",
size = "default",
...props
}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
return (
<ButtonPrimitive
data-slot="button"
data-variant={variant}
data-size={size}
className={cn(buttonVariants({ variant, size, className }))}
{...props}
nativeButton={getNativeButton(props)}
/>
)
}
export { Button, buttonVariants }
components/ui/buttons/button-clipped.tsx
import { cva, type VariantProps } from "class-variance-authority"
import { Button as ButtonPrimitive } from "@base-ui/react/button"
import { isValidElement } from "react"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"group/button inline-flex shrink-0 items-center justify-center border bg-clip-padding whitespace-nowrap transition-all outline-none select-none [clip-path:polygon(var(--button-clip)_0,100%_0,100%_calc(100%_-_var(--button-clip)),calc(100%_-_var(--button-clip))_100%,0_100%,0_var(--button-clip))] focus-visible:ring-3 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0",
{
variants: {
variant: {
default:
"border-transparent bg-primary font-medium text-primary-foreground hover:bg-primary/80 focus-visible:border-ring focus-visible:ring-ring/30",
outline:
"border-border bg-background font-medium text-foreground hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-ring/30 aria-expanded:bg-muted aria-expanded:text-foreground dark:bg-transparent dark:hover:bg-input/30",
secondary:
"border-transparent bg-secondary font-medium text-secondary-foreground hover:bg-secondary/50 focus-visible:border-ring focus-visible:ring-ring/30 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
ghost:
"border-transparent font-normal text-foreground hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-ring/30 aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
destructive:
"border-transparent bg-destructive/10 font-medium text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
link: "border-transparent font-medium text-primary underline-offset-4 hover:underline focus-visible:border-ring focus-visible:ring-ring/30",
},
size: {
default:
"h-10 gap-1.5 px-4 text-sm [--button-clip:12px] has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3 [&_svg:not([class*='size-'])]:size-4",
xs: "h-6 gap-1 px-3 text-xs [--button-clip:6px] has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5 [&_svg:not([class*='size-'])]:size-3",
sm: "h-8 gap-1 px-3.5 text-sm [--button-clip:8px] has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5 [&_svg:not([class*='size-'])]:size-4",
lg: "h-12 gap-2 px-5 text-base [--button-clip:12px] has-data-[icon=inline-end]:pr-4 has-data-[icon=inline-start]:pl-4 [&_svg:not([class*='size-'])]:size-4",
icon: "size-10 text-sm [--button-clip:10px] [&_svg:not([class*='size-'])]:size-4",
"icon-xs":
"size-6 text-sm [--button-clip:6px] [&_svg:not([class*='size-'])]:size-3",
"icon-sm":
"size-8 text-sm [--button-clip:8px] [&_svg:not([class*='size-'])]:size-4",
"icon-lg":
"size-12 text-sm [--button-clip:12px] [&_svg:not([class*='size-'])]:size-4",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
const outlineFrameVariants = cva(
"inline-flex shrink-0 bg-border p-px [--button-clip:12px] [clip-path:polygon(var(--button-clip)_0,100%_0,100%_calc(100%_-_var(--button-clip)),calc(100%_-_var(--button-clip))_100%,0_100%,0_var(--button-clip))] active:not-aria-[haspopup]:translate-y-px has-disabled:pointer-events-none has-disabled:opacity-50",
{
variants: {
size: {
default: "h-10",
xs: "h-6 [--button-clip:6px]",
sm: "h-8 [--button-clip:8px]",
lg: "h-12",
icon: "size-9 [--button-clip:9px]",
"icon-xs": "size-6 [--button-clip:6px]",
"icon-sm": "size-8 [--button-clip:8px]",
"icon-lg": "size-12",
},
},
defaultVariants: {
size: "default",
},
}
)
function getNativeButton(props: ButtonPrimitive.Props) {
if (props.nativeButton !== undefined || !isValidElement(props.render)) {
return props.nativeButton
}
return props.render.type === "button"
}
function Button({
className,
variant = "default",
size = "default",
...props
}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
if (variant === "outline") {
return (
<span
data-slot="button-outline-frame"
data-size={size}
className={cn(outlineFrameVariants({ size, className }))}
>
<ButtonPrimitive
data-slot="button"
data-variant={variant}
data-size={size}
className={cn(
buttonVariants({ variant, size }),
"h-full w-full border-0 active:not-aria-[haspopup]:translate-y-0 dark:bg-background"
)}
{...props}
nativeButton={getNativeButton(props)}
/>
</span>
)
}
return (
<ButtonPrimitive
data-slot="button"
data-variant={variant}
data-size={size}
className={cn(buttonVariants({ variant, size, className }))}
{...props}
nativeButton={getNativeButton(props)}
/>
)
}
export { Button, buttonVariants }
components/ui/buttons/button-strong-outline.tsx
import { cva, type VariantProps } from "class-variance-authority"
import { Button as ButtonPrimitive } from "@base-ui/react/button"
import { isValidElement } from "react"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"group/button inline-flex shrink-0 items-center justify-center rounded-4xl border-2 bg-clip-padding whitespace-nowrap transition-all outline-none select-none focus-visible:ring-3 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0",
{
variants: {
variant: {
default:
"border-foreground bg-primary font-medium text-primary-foreground shadow-xs hover:bg-primary/80 focus-visible:border-foreground focus-visible:ring-foreground/30",
outline:
"border-foreground bg-background font-medium text-foreground shadow-xs hover:bg-muted focus-visible:border-foreground focus-visible:ring-foreground/30 aria-expanded:bg-muted dark:bg-transparent dark:hover:bg-input/30",
secondary:
"border-foreground bg-secondary font-medium text-secondary-foreground shadow-xs hover:bg-secondary/80 focus-visible:border-foreground focus-visible:ring-foreground/30 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
ghost:
"border-transparent font-normal text-foreground hover:bg-muted hover:text-foreground focus-visible:border-foreground focus-visible:ring-foreground/30 aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
destructive:
"border-destructive bg-destructive/10 font-medium text-destructive shadow-xs hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
link: "border-transparent font-medium text-primary underline-offset-4 hover:underline focus-visible:border-foreground focus-visible:ring-foreground/30",
},
size: {
default:
"h-10 gap-1.5 px-3.5 text-sm has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5 [&_svg:not([class*='size-'])]:size-4",
xs: "h-6 gap-1 px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
sm: "h-8 gap-1 px-3 text-sm has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-4",
lg: "h-12 gap-2 px-5 text-base has-data-[icon=inline-end]:pr-4 has-data-[icon=inline-start]:pl-4 [&_svg:not([class*='size-'])]:size-4",
icon: "size-10 text-sm [&_svg:not([class*='size-'])]:size-4",
"icon-xs": "size-6 text-sm [&_svg:not([class*='size-'])]:size-3",
"icon-sm": "size-8 text-sm [&_svg:not([class*='size-'])]:size-4",
"icon-lg": "size-12 text-sm [&_svg:not([class*='size-'])]:size-4",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
function getNativeButton(props: ButtonPrimitive.Props) {
if (props.nativeButton !== undefined || !isValidElement(props.render)) {
return props.nativeButton
}
return props.render.type === "button"
}
function Button({
className,
variant = "default",
size = "default",
...props
}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
return (
<ButtonPrimitive
data-slot="button"
data-variant={variant}
data-size={size}
className={cn(buttonVariants({ variant, size, className }))}
{...props}
nativeButton={getNativeButton(props)}
/>
)
}
export { Button, buttonVariants }
components/ui/buttons/button-bold-skewed.tsx
import { cva, type VariantProps } from "class-variance-authority"
import { Button as ButtonPrimitive } from "@base-ui/react/button"
import { isValidElement } from "react"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"group/button inline-flex shrink-0 items-center justify-center rounded-xl border-1 bg-clip-padding font-medium whitespace-nowrap transition-all duration-100 outline-none select-none focus-visible:ring-3 active:not-aria-[haspopup]:!translate-y-1 active:not-aria-[haspopup]:translate-y-1 active:not-aria-[haspopup]:shadow-none disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0",
{
variants: {
variant: {
default:
"skew-x-[-5deg] border-[color-mix(in_oklch,var(--primary)_75%,black)] bg-primary text-primary-foreground shadow-[0_4px_0_0_color-mix(in_oklch,var(--primary)_75%,#000)] hover:bg-primary/80 focus-visible:border-ring focus-visible:ring-ring/30",
outline:
"skew-x-[-5deg] border-border bg-card text-foreground shadow-[0_4px_0_0_var(--border)] hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-ring/30 aria-expanded:bg-muted aria-expanded:text-foreground dark:bg-transparent dark:hover:bg-input/30",
secondary:
"skew-x-[-5deg] border-secondary bg-secondary text-secondary-foreground shadow-[0_4px_0_0_color-mix(in_oklch,var(--secondary-foreground)_15%,transparent)] focus-visible:border-ring focus-visible:ring-ring/30 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground dark:shadow-[0_4px_0_0_color-mix(in_oklch,var(--secondary)_70%,transparent)]",
ghost:
"!border-transparent text-foreground !shadow-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-ring/30 active:!translate-y-px aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
destructive:
"skew-x-[-5deg] border-destructive/25 bg-destructive/15 text-destructive shadow-[0_4px_0_0_color-mix(in_oklch,var(--destructive)_25%,transparent)] focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
link: "!border-transparent text-primary underline-offset-4 !shadow-none hover:underline focus-visible:border-ring focus-visible:ring-ring/30 active:!translate-y-px",
},
size: {
default:
"h-10 gap-1.5 px-3.5 text-sm has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5 [&_svg:not([class*='size-'])]:size-4",
xs: "h-6 gap-1 px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
sm: "h-8 gap-1 px-3 text-sm has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-4",
lg: "h-12 gap-2 px-5 text-base has-data-[icon=inline-end]:pr-4 has-data-[icon=inline-start]:pl-4 [&_svg:not([class*='size-'])]:size-4",
icon: "size-10 text-sm [&_svg:not([class*='size-'])]:size-4",
"icon-xs": "size-6 text-sm [&_svg:not([class*='size-'])]:size-3",
"icon-sm": "size-8 text-sm [&_svg:not([class*='size-'])]:size-4",
"icon-lg": "size-12 text-sm [&_svg:not([class*='size-'])]:size-4",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
function getNativeButton(props: ButtonPrimitive.Props) {
if (props.nativeButton !== undefined || !isValidElement(props.render)) {
return props.nativeButton
}
return props.render.type === "button"
}
function Button({
className,
variant = "default",
size = "default",
...props
}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
return (
<ButtonPrimitive
data-slot="button"
data-variant={variant}
data-size={size}
className={cn(buttonVariants({ variant, size, className }))}
{...props}
nativeButton={getNativeButton(props)}
/>
)
}
export { Button, buttonVariants }