shadimage
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 · light
iconslucide-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 }

More from initium

Browse all components in this library →