# Texture Button

> Tactile neumorphic button for React and shadcn/ui with layered borders and gradients, multiple variants and sizes, and asChild.

Source: https://www.cult-ui.com/docs/components/texture-button

## Example

```tsx title="texture-button-demo.tsx"
"use client"

import { ChevronLeft, Trash, X } from "lucide-react"

import { TextureButton } from "@/components/ui/texture-button"

export default function TextureButtonDemo() {
  return (
    <div className="flex justify-center rounded-md px-4 py-6 md:px-0">
      <div>
        <div className="mt-4 flex max-w-lg flex-col gap-3">
          <div className="flex gap-3">
            <div>
              <TextureButton key="primary" size="sm">
                Primary
              </TextureButton>
            </div>
            <div className="">
              <TextureButton key="primary2">Primary</TextureButton>
            </div>
            <div className="hidden md:w-36">
              <TextureButton key="primary3" size="lg">
                Primary
              </TextureButton>
            </div>
          </div>
        </div>
        <div className="mt-4 flex max-w-lg flex-col gap-3">
          <div className="flex gap-3">
            <div>
              <TextureButton key="accent" variant="accent" size="sm">
                Accent
              </TextureButton>
            </div>
            <div className="">
              <TextureButton key="accent2" variant="accent">
                Accent
              </TextureButton>
            </div>
            <div className="hidden md:w-36">
              <TextureButton key="accent3" variant="accent" size="lg">
                Accent
              </TextureButton>
            </div>
          </div>
        </div>
        <div className="mt-4 flex max-w-lg flex-col gap-3">
          <div className="flex w-full gap-3">
            <div className="">
              <TextureButton key="secondary" variant="secondary" size="sm">
                Secondary
              </TextureButton>
            </div>
            <div className="">
              <TextureButton key="secondary2" variant="secondary">
                Secondary
              </TextureButton>
            </div>
            <div className="hidden md:w-48">
              <TextureButton key="secondary3" variant="secondary" size="lg">
                Secondary
              </TextureButton>
            </div>
          </div>
        </div>
        <div className="mt-4 flex max-w-lg flex-col gap-3">
          <div className="flex w-full gap-3">
            <div className="">
              <TextureButton key="destructive" variant="destructive" size="sm">
                Destructive
              </TextureButton>
            </div>
            <div className="">
              <TextureButton key="destructive2" variant="destructive">
                Destructive
              </TextureButton>
            </div>
            <div className="hidden md:w-48">
              <TextureButton key="destructive3" variant="destructive" size="lg">
                Destructive
              </TextureButton>
            </div>
          </div>
        </div>
        <div className="mt-4 flex max-w-lg flex-col gap-3">
          <div className="flex w-full gap-3">
            <div className="">
              <TextureButton key="minimal" variant="minimal" size="sm">
                Minimal
              </TextureButton>
            </div>
            <div className="">
              <TextureButton key="minimal2" variant="minimal">
                Minimal
              </TextureButton>
            </div>
            <div className="hidden md:w-48">
              <TextureButton key="minimal3" variant="minimal" size="lg">
                Minimal
              </TextureButton>
            </div>
          </div>
        </div>
        <div className="mt-4 flex max-w-xs flex-col gap-3">
          <div className="flex gap-3">
            <TextureButton key="icon1" variant="icon" size="icon">
              <ChevronLeft className="h-6 w-6 p-1" />
            </TextureButton>

            <TextureButton key="icon2" variant="icon" size="icon">
              <Trash className="h-5 w-6 p-1" />
            </TextureButton>

            <TextureButton key="icon3" variant="icon" size="icon">
              <X className="h-6 w-6 p-1" />
            </TextureButton>
          </div>
        </div>
      </div>
    </div>
  )
}
```

TextureButton is a React button component for shadcn/ui built on Radix Slot. Use it for primary and secondary actions in forms, dialogs, and dashboards with a tactile visual style.

## Installation

### CLI

```bash
npx shadcn@latest add @cult-ui/texture-button
```

### Manual

**Copy and paste the following code into your project.**

```tsx title="texture-button.tsx"
"use client"

import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva } from "class-variance-authority"

import { cn } from "@/lib/utils"

const buttonVariantsOuter = cva("", {
  variants: {
    variant: {
      primary:
        "w-full border border-[1px] border-black/10 bg-gradient-to-b from-black/70 to-black p-[1px] transition duration-300 ease-in-out dark:border-[2px] dark:border-black dark:from-white dark:to-white/80",
      accent:
        "w-full border-[1px] border-black/10 bg-gradient-to-b from-indigo-300/90 to-indigo-500 p-[1px] transition duration-300 ease-in-out dark:border-[2px] dark:border-neutral-950 dark:from-indigo-200/70 dark:to-indigo-500",
      destructive:
        "w-full border-[1px] border-black/10 bg-gradient-to-b from-red-300/90 to-red-500 p-[1px] transition duration-300 ease-in-out dark:border-[2px] dark:border-neutral-950 dark:from-red-300/90 dark:to-red-500",
      secondary:
        "w-full border-[1px] border-black/20 bg-white/50 p-[1px] transition duration-300 ease-in-out dark:border-[2px] dark:border-neutral-950 dark:bg-neutral-600/50",
      minimal:
        "group/texture-button w-full border-[1px] border-black/20 bg-white/50 to-white p-[1px] hover:bg-gradient-to-t hover:from-neutral-100 active:bg-neutral-200 dark:border-[2px] dark:border-neutral-950 dark:bg-neutral-600/80 dark:hover:from-neutral-600/50 dark:hover:to-neutral-600/70 dark:active:bg-neutral-800",
      icon: "group/texture-button rounded-full border border-black/10 bg-white/50 to-white p-[1px] hover:bg-gradient-to-t hover:from-neutral-100 active:bg-neutral-200 dark:border-neutral-950 dark:bg-neutral-600/50 dark:hover:from-neutral-700 dark:hover:to-neutral-600 dark:active:bg-neutral-800",
    },
    size: {
      sm: "rounded-[6px]",
      default: "rounded-[12px]",
      lg: "rounded-[12px]",
      icon: "rounded-full",
    },
  },
  defaultVariants: {
    variant: "primary",
    size: "default",
  },
})

const innerDivVariants = cva(
  "text-muted-foreground flex h-full w-full items-center justify-center",
  {
    variants: {
      variant: {
        primary:
          "gap-2 bg-gradient-to-b from-neutral-800 to-black text-sm text-white/90 transition duration-300 ease-in-out hover:from-stone-800 hover:to-neutral-800/70 active:bg-gradient-to-b active:from-black active:to-black dark:from-neutral-200 dark:to-neutral-50 dark:text-black/80 dark:hover:from-stone-200 dark:hover:to-neutral-200 dark:active:from-stone-300 dark:active:to-neutral-300",
        accent:
          "gap-2 bg-gradient-to-b from-indigo-400 to-indigo-600 text-sm text-white/90 transition duration-300 ease-in-out hover:bg-gradient-to-b hover:from-indigo-400/70 hover:to-indigo-600/70 active:bg-gradient-to-b active:from-indigo-400/80 active:to-indigo-600/80 dark:hover:from-indigo-400/70 dark:hover:to-indigo-600/70 dark:active:from-indigo-400 dark:active:to-indigo-600",
        destructive:
          "gap-2 bg-gradient-to-b from-red-400/60 to-red-500/60 text-sm text-white/90 transition duration-300 ease-in-out hover:bg-gradient-to-b hover:from-red-400/70 hover:to-red-600/70 active:bg-gradient-to-b active:from-red-400/80 active:to-red-600/80 dark:hover:from-red-400/70 dark:hover:to-red-500/80 dark:active:from-red-400 dark:active:to-red-500",
        secondary:
          "bg-gradient-to-b from-neutral-100/80 to-neutral-200/50 text-sm transition duration-300 ease-in-out hover:bg-gradient-to-b hover:from-neutral-200/40 hover:to-neutral-300/60 active:bg-gradient-to-b active:from-neutral-200/60 active:to-neutral-300/70 dark:from-neutral-800 dark:to-neutral-700/50 dark:hover:from-neutral-700 dark:hover:to-neutral-700/60 dark:active:from-neutral-800 dark:active:to-neutral-700",
        minimal:
          "bg-gradient-to-b from-white to-neutral-50/50 text-sm transition duration-300 ease-in-out group-hover/texture-button:bg-gradient-to-b group-hover/texture-button:from-neutral-50/50 group-hover/texture-button:to-neutral-100/60 group-active/texture-button:bg-gradient-to-b group-active/texture-button:from-neutral-100/60 group-active/texture-button:to-neutral-100/90 dark:from-neutral-800 dark:to-neutral-700/50 dark:group-hover/texture-button:from-neutral-700 dark:group-hover/texture-button:to-neutral-700/60 dark:group-active/texture-button:from-neutral-800 dark:group-active/texture-button:to-neutral-700",
        icon: "rounded-full bg-gradient-to-b from-white to-neutral-50/50 group-active/texture-button:bg-neutral-200 dark:from-neutral-800 dark:to-neutral-700/50 dark:group-active/texture-button:bg-neutral-800",
      },
      size: {
        sm: "rounded-[4px] px-4 py-1 text-xs",
        default: "rounded-[10px] px-4 py-2 text-sm",
        lg: "rounded-[10px] px-4 py-2 text-base",
        icon: "rounded-full p-1",
      },
    },
    defaultVariants: {
      variant: "primary",
      size: "default",
    },
  }
)

export interface UnifiedButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  variant?:
    | "primary"
    | "secondary"
    | "accent"
    | "destructive"
    | "minimal"
    | "icon"
  size?: "default" | "sm" | "lg" | "icon"
  asChild?: boolean
}

const TextureButton = React.forwardRef<HTMLButtonElement, UnifiedButtonProps>(
  (
    {
      children,
      variant = "primary",
      size = "default",
      asChild = false,
      className,
      ...props
    },
    ref
  ) => {
    const Comp = asChild ? Slot : "button"

    return (
      <Comp
        className={cn(buttonVariantsOuter({ variant, size }), className)}
        ref={ref}
        {...props}
      >
        <div className={cn(innerDivVariants({ variant, size }))}>
          {children}
        </div>
      </Comp>
    )
  }
)

TextureButton.displayName = "TextureButton"

export { TextureButton }

// export default TextureButton
```

**Update the import paths to match your project setup.**

## Usage

```tsx
<TextureButton variant="icon">
  {/* GithubIcon   */}
  <svg
    viewBox="0 0 256 250"
    width="256"
    height="250"
    fill="currentColor"
    xmlns="http://www.w3.org/2000/svg"
    preserveAspectRatio="xMidYMid"
    className="h-5 w-5"
  >
    <path
      d="M128.001 0C57.317 0 0 57.307 0 128.001c0 56.554 36.676 104.535 87.535 121.46 6.397 1.185 8.746-2.777 8.746-6.158 0-3.052-.12-13.135-.174-23.83-35.61 7.742-43.124-15.103-43.124-15.103-5.823-14.795-14.213-18.73-14.213-18.73-11.613-7.944.876-7.78.876-7.78 12.853.902 19.621 13.19 19.621 13.19 11.417 19.568 29.945 13.911 37.249 10.64 1.149-8.272 4.466-13.92 8.127-17.116-28.431-3.236-58.318-14.212-58.318-63.258 0-13.975 5-25.394 13.188-34.358-1.329-3.224-5.71-16.242 1.24-33.874 0 0 10.749-3.44 35.21 13.121 10.21-2.836 21.16-4.258 32.038-4.307 10.878.049 21.837 1.47 32.066 4.307 24.431-16.56 35.165-13.12 35.165-13.12 6.967 17.63 2.584 30.65 1.255 33.873 8.207 8.964 13.173 20.383 13.173 34.358 0 49.163-29.944 59.988-58.447 63.157 4.591 3.972 8.682 11.762 8.682 23.704 0 17.126-.148 30.91-.148 35.126 0 3.407 2.304 7.398 8.792 6.14C219.37 232.5 256 184.537 256 128.002 256 57.307 198.691 0 128.001 0Zm-80.06 182.34c-.282.636-1.283.827-2.194.39-.929-.417-1.45-1.284-1.15-1.922.276-.655 1.279-.838 2.205-.399.93.418 1.46 1.293 1.139 1.931Zm6.296 5.618c-.61.566-1.804.303-2.614-.591-.837-.892-.994-2.086-.375-2.66.63-.566 1.787-.301 2.626.591.838.903 1 2.088.363 2.66Zm4.32 7.188c-.785.545-2.067.034-2.86-1.104-.784-1.138-.784-2.503.017-3.05.795-.547 2.058-.055 2.861 1.075.782 1.157.782 2.522-.019 3.08Zm7.304 8.325c-.701.774-2.196.566-3.29-.49-1.119-1.032-1.43-2.496-.726-3.27.71-.776 2.213-.558 3.315.49 1.11 1.03 1.45 2.505.701 3.27Zm9.442 2.81c-.31 1.003-1.75 1.459-3.199 1.033-1.448-.439-2.395-1.613-2.103-2.626.301-1.01 1.747-1.484 3.207-1.028 1.446.436 2.396 1.602 2.095 2.622Zm10.744 1.193c.036 1.055-1.193 1.93-2.715 1.95-1.53.034-2.769-.82-2.786-1.86 0-1.065 1.202-1.932 2.733-1.958 1.522-.03 2.768.818 2.768 1.868Zm10.555-.405c.182 1.03-.875 2.088-2.387 2.37-1.485.271-2.861-.365-3.05-1.386-.184-1.056.893-2.114 2.376-2.387 1.514-.263 2.868.356 3.061 1.403Z"
      fill="#545454"
    />
  </svg>
  <span className="pl-2">Github</span>
</TextureButton>
```
