# Background Image Texture

> Texture background wrapper for React and shadcn/ui that layers fabric, grid-noise, or paper textures behind content at any opacity.

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

## Example

```tsx title="bg-image-texture-demo.tsx"
"use client"

import { useState } from "react"
import { Download } from "lucide-react"

import { Button } from "@/components/ui/button"
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
import { Label } from "@/components/ui/label"
import { Slider } from "@/components/ui/slider"
import { BackgroundImageTexture } from "@/components/ui/bg-image-texture"
import type { TextureVariant } from "@/components/ui/bg-image-texture"

const textureVariants: TextureVariant[] = [
  "fabric-of-squares",
  "grid-noise",
  "inflicted",
  "debut-light",
  "groovepaper",
  "none",
]

const textureMap: Record<Exclude<TextureVariant, "none">, string> = {
  "fabric-of-squares": "/textures/fabric-of-squares.png",
  "grid-noise": "/textures/grid-noise.png",
  inflicted: "/textures/inflicted.png",
  "debut-light": "/textures/debut-light.png",
  groovepaper: "/textures/groovepaper.png",
}

async function downloadTexture(variant: Exclude<TextureVariant, "none">) {
  const url = textureMap[variant]
  try {
    const response = await fetch(url)
    const blob = await response.blob()
    const blobUrl = window.URL.createObjectURL(blob)
    const link = document.createElement("a")
    link.href = blobUrl
    link.download = `${variant}.png`
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
    window.URL.revokeObjectURL(blobUrl)
  } catch {
    // Fallback to direct link if fetch fails
    const link = document.createElement("a")
    link.href = url
    link.download = `${variant}.png`
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
  }
}

export default function BackgroundImageTextureDemo() {
  const [selectedVariant, setSelectedVariant] =
    useState<TextureVariant>("fabric-of-squares")
  const [opacity, setOpacity] = useState([0.5])

  return (
    <div className="space-y-8 p-6">
      {/* Controls */}
      <Card>
        <CardHeader>
          <CardTitle>Texture Controls</CardTitle>
          <CardDescription>
            Select a texture variant and adjust the opacity
          </CardDescription>
        </CardHeader>
        <CardContent className="space-y-6">
          <div className="space-y-2">
            <Label>Texture Variant</Label>
            <div className="flex flex-wrap gap-2">
              {textureVariants.map((variant) => (
                <Button
                  key={variant}
                  variant={selectedVariant === variant ? "default" : "outline"}
                  size="sm"
                  onClick={() => setSelectedVariant(variant)}
                >
                  {variant === "none" ? "None" : variant}
                </Button>
              ))}
            </div>
          </div>
          <div className="space-y-2">
            <Label>Opacity: {opacity[0].toFixed(2)}</Label>
            <Slider
              value={opacity}
              onValueChange={setOpacity}
              min={0}
              max={1}
              step={0.01}
              className="w-full"
            />
          </div>
        </CardContent>
      </Card>

      {/* Preview with Content */}
      <Card>
        <CardHeader>
          <div className="flex items-center justify-between">
            <div>
              <CardTitle>Preview with Content</CardTitle>
              <CardDescription>
                See how the texture looks with content on top
              </CardDescription>
            </div>
            {selectedVariant !== "none" && (
              <Button
                variant="outline"
                size="sm"
                onClick={() => downloadTexture(selectedVariant)}
                className="gap-2"
              >
                <Download className="h-4 w-4" />
                Download Texture
              </Button>
            )}
          </div>
        </CardHeader>
        <CardContent>
          <BackgroundImageTexture
            variant={selectedVariant}
            opacity={opacity[0]}
            className="border-border min-h-[400px] rounded-lg border p-8"
          >
            <div className="space-y-6">
              <div>
                <h3 className="text-foreground mb-2 text-3xl font-bold">
                  Texture Background
                </h3>
                <p className="text-muted-foreground text-lg">
                  This content sits on top of the texture layer. Adjust the
                  opacity slider to see how the texture affects the background.
                  The texture is fully interactive and customizable.
                </p>
              </div>
              <div className="grid grid-cols-2 gap-4">
                <div className="bg-card/50 border-border rounded-lg border p-4">
                  <h4 className="mb-2 font-semibold">Feature 1</h4>
                  <p className="text-muted-foreground text-sm">
                    Content with texture background
                  </p>
                </div>
                <div className="bg-card/50 border-border rounded-lg border p-4">
                  <h4 className="mb-2 font-semibold">Feature 2</h4>
                  <p className="text-muted-foreground text-sm">
                    More content examples
                  </p>
                </div>
              </div>
            </div>
          </BackgroundImageTexture>
        </CardContent>
      </Card>

      {/* All Variants Showcase */}
      <div className="space-y-4">
        <h3 className="text-2xl font-semibold">All Texture Variants</h3>
        <div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
          {textureVariants
            .filter((v) => v !== "none")
            .map((variant) => (
              <Card key={variant}>
                <CardHeader>
                  <div className="flex items-center justify-between">
                    <CardTitle className="text-lg capitalize">
                      {variant.replace(/-/g, " ")}
                    </CardTitle>
                    <Button
                      variant="ghost"
                      size="sm"
                      onClick={() => downloadTexture(variant)}
                      className="h-8 w-8 p-0"
                      title={`Download ${variant}.png`}
                    >
                      <Download className="h-4 w-4" />
                    </Button>
                  </div>
                </CardHeader>
                <CardContent>
                  <BackgroundImageTexture
                    variant={variant}
                    opacity={0.5}
                    className="border-border flex h-48 items-center justify-center rounded-lg border p-6"
                  >
                    <div className="text-center">
                      <p className="text-foreground text-sm font-medium">
                        {variant}
                      </p>
                      <p className="text-muted-foreground mt-1 text-xs">
                        Opacity: 0.5
                      </p>
                    </div>
                  </BackgroundImageTexture>
                </CardContent>
              </Card>
            ))}
        </div>
      </div>

      {/* Opacity Variations */}
      <div className="space-y-4">
        <h3 className="text-2xl font-semibold">Opacity Variations</h3>
        <div className="grid grid-cols-1 gap-4 md:grid-cols-3">
          {[0.2, 0.5, 0.8].map((opacityValue) => (
            <Card key={opacityValue}>
              <CardHeader>
                <CardTitle className="text-lg">
                  Opacity: {opacityValue}
                </CardTitle>
              </CardHeader>
              <CardContent>
                <BackgroundImageTexture
                  variant="grid-noise"
                  opacity={opacityValue}
                  className="border-border flex h-48 items-center justify-center rounded-lg border p-6"
                >
                  <div className="text-center">
                    <p className="text-foreground text-sm font-medium">
                      grid-noise texture
                    </p>
                    <p className="text-muted-foreground mt-1 text-xs">
                      Opacity: {opacityValue}
                    </p>
                  </div>
                </BackgroundImageTexture>
              </CardContent>
            </Card>
          ))}
        </div>
      </div>

      {/* Use Case Examples */}
      <div className="space-y-4">
        <h3 className="text-2xl font-semibold">Use Case Examples</h3>
        <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
          {/* Card with Texture */}
          <Card>
            <CardHeader>
              <CardTitle>Card with Texture</CardTitle>
            </CardHeader>
            <CardContent className="p-0">
              <BackgroundImageTexture
                variant="debut-light"
                opacity={0.3}
                className="rounded-b-lg p-6"
              >
                <div className="space-y-2">
                  <h4 className="font-semibold">Beautiful Card</h4>
                  <p className="text-muted-foreground text-sm">
                    Cards look great with subtle texture backgrounds
                  </p>
                </div>
              </BackgroundImageTexture>
            </CardContent>
          </Card>

          {/* Hero Section */}
          <Card>
            <CardHeader>
              <CardTitle>Hero Section</CardTitle>
            </CardHeader>
            <CardContent className="p-0">
              <BackgroundImageTexture
                variant="groovepaper"
                opacity={0.4}
                className="flex min-h-[200px] items-center justify-center rounded-b-lg p-6"
              >
                <div className="space-y-2 text-center">
                  <h4 className="text-2xl font-bold">Hero Title</h4>
                  <p className="text-muted-foreground text-sm">
                    Perfect for hero sections and banners
                  </p>
                </div>
              </BackgroundImageTexture>
            </CardContent>
          </Card>
        </div>
      </div>
    </div>
  )
}
```

BackgroundImageTexture is a React wrapper component for shadcn/ui. Use it to add subtle surface texture to hero sections, cards, or pricing blocks.

## Installation

### CLI

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

### Manual

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

```tsx title="bg-image-texture.tsx"
import type React from "react"

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

export type TextureVariant =
  | "fabric-of-squares"
  | "grid-noise"
  | "inflicted"
  | "debut-light"
  | "groovepaper"
  | "none"

interface BackgroundImageTextureProps {
  variant?: TextureVariant
  opacity?: number
  className?: string
  children?: React.ReactNode
}

const textureMap: Record<Exclude<TextureVariant, "none">, string> = {
  "fabric-of-squares": "/textures/fabric-of-squares.png",
  "grid-noise": "/textures/grid-noise.png",
  inflicted: "/textures/inflicted.png",
  "debut-light": "/textures/debut-light.png",
  groovepaper: "/textures/groovepaper.png",
}

export function BackgroundImageTexture({
  variant = "fabric-of-squares",
  opacity = 0.5,
  className,
  children,
}: BackgroundImageTextureProps) {
  const textureUrl = variant !== "none" ? textureMap[variant] : null

  return (
    <div className={cn("relative", className)}>
      {textureUrl && (
        <div
          aria-hidden="true"
          className="pointer-events-none absolute inset-0"
          style={{
            backgroundImage: `url(${textureUrl})`,
            backgroundRepeat: "repeat",
            opacity,
          }}
        />
      )}
      {children && <div className="relative">{children}</div>}
    </div>
  )
}
```

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

## Usage

```tsx
import { BackgroundImageTexture } from "@/components/ui/bg-image-texture";
```

### Basic Usage

Apply a texture background to any container:

```tsx
<BackgroundImageTexture variant="fabric-of-squares" opacity={0.5}>
  <div className="p-8">
    <h2>Content with texture background</h2>
    <p>Your content goes here</p>
  </div>
</BackgroundImageTexture>
```

### Texture Variants

Choose from five different texture patterns:

```tsx
// Fabric of squares pattern
<BackgroundImageTexture variant="fabric-of-squares" opacity={0.5}>
  <div>Content</div>
</BackgroundImageTexture>

// Grid noise pattern
<BackgroundImageTexture variant="grid-noise" opacity={0.5}>
  <div>Content</div>
</BackgroundImageTexture>

// Inflicted pattern
<BackgroundImageTexture variant="inflicted" opacity={0.5}>
  <div>Content</div>
</BackgroundImageTexture>

// Debut light pattern
<BackgroundImageTexture variant="debut-light" opacity={0.5}>
  <div>Content</div>
</BackgroundImageTexture>

// Groovepaper pattern
<BackgroundImageTexture variant="groovepaper" opacity={0.5}>
  <div>Content</div>
</BackgroundImageTexture>

// No texture (transparent)
<BackgroundImageTexture variant="none" opacity={0.5}>
  <div>Content</div>
</BackgroundImageTexture>
```

### Opacity Control

Adjust the opacity to control how visible the texture is:

```tsx
// Subtle texture (low opacity)
<BackgroundImageTexture variant="grid-noise" opacity={0.2}>
  <div>Content</div>
</BackgroundImageTexture>

// Medium texture (default)
<BackgroundImageTexture variant="grid-noise" opacity={0.5}>
  <div>Content</div>
</BackgroundImageTexture>

// Bold texture (high opacity)
<BackgroundImageTexture variant="grid-noise" opacity={0.8}>
  <div>Content</div>
</BackgroundImageTexture>
```

### Without Children

The component can be used without children for a pure background texture:

```tsx
<BackgroundImageTexture
  variant="fabric-of-squares"
  opacity={0.5}
  className="h-64 w-full rounded-lg"
/>
```

## API Reference

### BackgroundImageTexture Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `TextureVariant` | `"fabric-of-squares"` | Texture pattern variant |
| `opacity` | `number` | `0.5` | Opacity of the texture (0 to 1) |
| `className` | `string` | - | Additional CSS classes for the container |
| `children` | `React.ReactNode` | - | Content to display on top of the texture |

### TextureVariant Type

```tsx
type TextureVariant =
  | "fabric-of-squares"
  | "grid-noise"
  | "inflicted"
  | "debut-light"
  | "groovepaper"
  | "none";
```

## Features

- **Multiple Texture Variants**: Choose from 5 different texture patterns
- **Customizable Opacity**: Control texture visibility from 0 to 1
- **Flexible Usage**: Use with or without children content
- **Accessible**: Texture layer is marked with `aria-hidden="true"`
- **Non-Interactive**: Texture layer has `pointer-events-none` to allow interaction with content
- **Responsive**: Works seamlessly across all screen sizes

## Examples

### Card with Texture

Add texture to card components:

```tsx
<Card>
  <CardHeader>
    <CardTitle>Card Title</CardTitle>
  </CardHeader>
  <CardContent className="p-0">
    <BackgroundImageTexture
      variant="debut-light"
      opacity={0.3}
      className="rounded-b-lg p-6"
    >
      <div>
        <h4 className="font-semibold">Card Content</h4>
        <p className="text-sm text-muted-foreground">
          Cards look great with subtle texture backgrounds
        </p>
      </div>
    </BackgroundImageTexture>
  </CardContent>
</Card>
```

### Hero Section

Use texture in hero sections:

```tsx
<BackgroundImageTexture
  variant="groovepaper"
  opacity={0.4}
  className="rounded-lg p-12 min-h-[400px] flex items-center justify-center"
>
  <div className="text-center space-y-4">
    <h1 className="text-4xl font-bold">Hero Title</h1>
    <p className="text-lg text-muted-foreground">
      Perfect for hero sections and banners
    </p>
  </div>
</BackgroundImageTexture>
```

### All Variants Showcase

Display all texture variants:

```tsx
const variants = [
  "fabric-of-squares",
  "grid-noise",
  "inflicted",
  "debut-light",
  "groovepaper",
] as const

<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
  {variants.map((variant) => (
    <BackgroundImageTexture
      key={variant}
      variant={variant}
      opacity={0.5}
      className="rounded-lg border border-border p-6 h-48 flex items-center justify-center"
    >
      <div className="text-center">
        <p className="text-sm font-medium">{variant}</p>
      </div>
    </BackgroundImageTexture>
  ))}
</div>
```

### Opacity Variations

Compare different opacity levels:

```tsx
const opacities = [0.2, 0.5, 0.8]

<div className="grid grid-cols-3 gap-4">
  {opacities.map((opacity) => (
    <BackgroundImageTexture
      key={opacity}
      variant="grid-noise"
      opacity={opacity}
      className="rounded-lg border border-border p-6 h-48 flex items-center justify-center"
    >
      <div className="text-center">
        <p className="text-sm font-medium">Opacity: {opacity}</p>
      </div>
    </BackgroundImageTexture>
  ))}
</div>
```

### Dynamic Opacity Control

Use state to control opacity dynamically:

```tsx
import { useState } from "react";

import { BackgroundImageTexture } from "@/components/ui/bg-image-texture";
import { Slider } from "@/components/ui/slider";

function DynamicTextureExample() {
  const [opacity, setOpacity] = useState([0.5]);

  return (
    <div className="space-y-4">
      <Slider
        value={opacity}
        onValueChange={setOpacity}
        min={0}
        max={1}
        step={0.01}
      />
      <BackgroundImageTexture
        variant="fabric-of-squares"
        opacity={opacity[0]}
        className="rounded-lg p-8 min-h-[300px]"
      >
        <div>
          <h3 className="text-2xl font-bold mb-2">Dynamic Texture</h3>
          <p>Adjust the slider to change texture opacity</p>
        </div>
      </BackgroundImageTexture>
    </div>
  );
}
```

### Custom Styling

Apply custom styles to the container:

```tsx
<BackgroundImageTexture
  variant="grid-noise"
  opacity={0.5}
  className="rounded-xl border-2 border-primary p-8 shadow-lg"
>
  <div>Custom styled content</div>
</BackgroundImageTexture>
```

### Section Dividers

Use texture as decorative section dividers:

```tsx
<section className="py-12">
  <BackgroundImageTexture
    variant="inflicted"
    opacity={0.2}
    className="h-32 w-full"
  />
</section>
```
