shadimage
pane
uibuttonsource: ok · hit

Button

A glass button in default and destructive variants across four sizes, with asChild support via Radix Slot.

npx shadcn add "https://pane.theui.company/r/button.json"
free

Visual style

glass-blur

Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light
class-variance-authorityradix-ui

Registry dependencies

@pane/pane@pane/utils

Files1 · source cached 10/15/2026, 6:18:02 AM

registry/pane/ui/button.tsx
import { cva, type VariantProps } from "class-variance-authority";
import { Slot } from "radix-ui";
import type * as React from "react";
import { Pane } from "@/components/ui/pane";
import { cn } from "@/lib/utils";

const wrapperVariants = cva("inline-flex shrink-0", {
  variants: {
    size: {
      default: "",
      sm: "",
      lg: "",
      icon: "",
    },
  },
  defaultVariants: { size: "default" },
});

const radiusBySize: Record<string, number> = {
  default: 16,
  sm: 13,
  lg: 20,
  icon: 16,
};

const buttonVariants = cva(
  // flex-1: when a parent stretches the glass (a stacked dialog footer, say)
  // the button fills it and keeps its label centred
  "inline-flex flex-1 shrink-0 items-center justify-center gap-2 whitespace-nowrap text-sm font-medium outline-none transition-colors disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
  {
    variants: {
      variant: {
        default: "text-foreground",
        destructive: "text-red-600 dark:text-red-400",
      },
      size: {
        default: "h-9 px-4",
        sm: "h-8 px-3 text-xs",
        lg: "h-11 px-6 text-base",
        icon: "size-9 px-0",
      },
    },
    defaultVariants: { variant: "default", size: "default" },
  },
);

export interface ButtonProps
  extends React.ComponentProps<"button">,
    VariantProps<typeof buttonVariants> {
  asChild?: boolean;
}

function Button({
  className,
  variant = "default",
  size = "default",
  asChild = false,
  ...props
}: ButtonProps) {
  const Comp = asChild ? Slot.Root : "button";

  return (
    <Pane
      variant="clear"
      interactive
      radius={radiusBySize[size ?? "default"]}
      className={cn(
        wrapperVariants({ size }),
        // keyboard only: `focus-within` leaves the ring painted around the pane
        // after every mouse click
        "has-[:focus-visible]:ring-[3px] has-[:focus-visible]:ring-ring/50 has-[:focus-visible]:ring-offset-0",
      )}
    >
      <Comp
        data-slot="button"
        data-variant={variant}
        data-size={size}
        className={cn(
          buttonVariants({ variant, size, className }),
          "bg-transparent",
        )}
        {...props}
      />
    </Pane>
  );
}

export { Button, buttonVariants };

More from pane

Browse all components in this library →