# Texture Button

> Dark button with an SVG feTurbulence grain overlay; grain strength is adjustable.

- Author: Atelier UI
- License: MIT
- Page: https://chromotion.dev/en/components/texture-button
- Site: Atelier UI (https://chromotion.dev)

## Install

```bash
npx shadcn@latest add https://chromotion.dev/r/texture-button.json
```

Stack: React + TypeScript + Tailwind CSS v4, shadcn/ui conventions. `cn` comes from `@/lib/utils`.

## Dependencies

No extra dependencies.

## Usage

```tsx title="components/texture-button-demo.tsx"
import { ArrowRight, Download } from "lucide-react"

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

export default function TextureButtonDemo() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-4 rounded-2xl bg-neutral-200 p-10 dark:bg-neutral-800">
      <TextureButton>
        Get started <ArrowRight className="size-4" />
      </TextureButton>
      <TextureButton noise={0.7}>
        <Download className="size-4" /> Heavy grain
      </TextureButton>
      <TextureButton disabled>Disabled</TextureButton>
    </div>
  )
}
```

## Source

```tsx title="components/texture-button.tsx"
import * as React from "react"

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

export interface TextureButtonProps extends React.ComponentProps<"button"> {
  /** Opacity of the grain overlay, 0 to 1 */
  noise?: number
}

export function TextureButton({ noise = 0.35, className, children, ...props }: TextureButtonProps) {
  const filterId = `texture-${React.useId().replace(/[^a-zA-Z0-9]/g, "")}`

  return (
    <button
      className={cn(
        "group relative inline-flex h-11 items-center justify-center overflow-hidden rounded-xl bg-neutral-900 px-6 text-sm font-medium text-neutral-100",
        "shadow-[inset_0_1px_0_rgba(255,255,255,0.14),inset_0_-1px_0_rgba(0,0,0,0.6),0_0_0_1px_rgba(0,0,0,0.9),0_8px_16px_-8px_rgba(0,0,0,0.6)]",
        "transition-[transform,filter] duration-150 hover:brightness-125 active:translate-y-px active:scale-[0.99]",
        "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:outline-none",
        "disabled:pointer-events-none disabled:opacity-50",
        className
      )}
      {...props}
    >
      <svg
        aria-hidden
        className="pointer-events-none absolute inset-0 size-full mix-blend-overlay"
        style={{ opacity: noise }}
      >
        <filter id={filterId}>
          <feTurbulence type="fractalNoise" baseFrequency="0.8" numOctaves="3" stitchTiles="stitch" />
          <feColorMatrix type="saturate" values="0" />
        </filter>
        <rect width="100%" height="100%" filter={`url(#${filterId})`} />
      </svg>
      <span
        aria-hidden
        className="pointer-events-none absolute inset-x-0 top-0 h-1/2 bg-gradient-to-b from-white/10 to-transparent"
      />
      <span className="relative inline-flex items-center gap-2">{children}</span>
    </button>
  )
}
```
