# Texture Card

> Composable neumorphic card for React and shadcn/ui with layered borders and header, content, footer, and separator parts.

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

## Example

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

import { ArrowRight, Merge } from "lucide-react"

import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { TextureButton } from "@/components/ui/texture-button"
import {
  TextureCardContent,
  TextureCardFooter,
  TextureCardHeader,
  TextureCardStyled,
  TextureCardTitle,
  TextureSeparator,
} from "@/components/ui/texture-card"

export default function TextureCardDemo() {
  return (
    <div className="flex items-center justify-center py-4">
      <div className="h-full rounded-md dark:bg-stone-950">
        <div className="grid grid-cols-1 items-start justify-center gap-6 rounded-lg p-2 md:p-8">
          <div className="col-span-1 grid items-start gap-6 lg:col-span-1">
            <div>
              <TextureCardStyled>
                <TextureCardHeader className="flex flex-col items-center justify-center gap-1 p-4">
                  <div className="mb-3 rounded-full bg-neutral-950 p-3">
                    <Merge className="h-7 w-7 stroke-neutral-200" />
                  </div>
                  <TextureCardTitle>Create your account</TextureCardTitle>
                  <p className="text-center">
                    Welcome! Please fill in the details to get started.
                  </p>
                </TextureCardHeader>
                <TextureSeparator />
                <TextureCardContent>
                  <div className="mb-4 flex justify-center gap-2">
                    <TextureButton variant="icon">
                      {/* Google Icon */}
                      <svg
                        width="256"
                        height="262"
                        viewBox="0 0 256 262"
                        xmlns="http://www.w3.org/2000/svg"
                        preserveAspectRatio="xMidYMid"
                        className="h-5 w-5"
                      >
                        <path
                          d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622 38.755 30.023 2.685.268c24.659-22.774 38.875-56.282 38.875-96.027"
                          fill="#4285F4"
                        />
                        <path
                          d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055-34.523 0-63.824-22.773-74.269-54.25l-1.531.13-40.298 31.187-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1"
                          fill="#34A853"
                        />
                        <path
                          d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82 0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602l42.356-32.782"
                          fill="#FBBC05"
                        />
                        <path
                          d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0 79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251"
                          fill="#EB4335"
                        />
                      </svg>
                      <span className="pl-2">Google</span>
                    </TextureButton>
                    <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>
                  </div>
                  <div className="mb-4 text-center text-sm">or</div>

                  <form className="flex flex-col gap-6">
                    <div className="flex justify-between gap-2">
                      <div>
                        <Label htmlFor="first">First name</Label>
                        <Input
                          id="first"
                          type="first"
                          required
                          className="w-full rounded-md border border-neutral-300 bg-white/80 px-4 py-2 placeholder-neutral-400 dark:border-neutral-700 dark:bg-neutral-800/80 dark:placeholder-neutral-500"
                        />
                      </div>
                      <div>
                        <Label htmlFor="last">Last Name</Label>
                        <Input
                          id="last"
                          type="last"
                          required
                          className="w-full rounded-md border border-neutral-300 bg-white/80 px-4 py-2 placeholder-neutral-400 dark:border-neutral-700 dark:bg-neutral-800/80 dark:placeholder-neutral-500"
                        />
                      </div>
                    </div>

                    <div>
                      <Label htmlFor="username">Username</Label>
                      <Input
                        id="username"
                        type="username"
                        required
                        className="w-full rounded-md border border-neutral-300 bg-white/80 px-4 py-2 placeholder-neutral-400 dark:border-neutral-700 dark:bg-neutral-800/80 dark:placeholder-neutral-500"
                      />
                    </div>
                    <div>
                      <Label htmlFor="email">Email</Label>
                      <Input
                        id="email"
                        type="email"
                        required
                        className="w-full rounded-md border border-neutral-300 bg-white/80 px-4 py-2 placeholder-neutral-400 dark:border-neutral-700 dark:bg-neutral-800/80 dark:placeholder-neutral-500"
                      />
                    </div>
                    <div>
                      <Label htmlFor="password">Password</Label>
                      <Input
                        id="password"
                        type="password"
                        required
                        className="w-full rounded-md border border-neutral-300 bg-white/80 px-4 py-2 placeholder-neutral-400 dark:border-neutral-700 dark:bg-neutral-800/80 dark:placeholder-neutral-500"
                      />
                    </div>
                  </form>
                </TextureCardContent>
                <TextureSeparator />
                <TextureCardFooter className="rounded-b-sm border-b">
                  <TextureButton variant="accent" className="w-full">
                    <div className="flex items-center justify-center gap-1">
                      Continue
                      <ArrowRight className="mt-[1px] h-4 w-4 text-neutral-50" />
                    </div>
                  </TextureButton>
                </TextureCardFooter>

                <div className="overflow-hidden rounded-b-[20px] bg-stone-100 pt-px dark:bg-neutral-800">
                  <div className="flex flex-col items-center justify-center">
                    <div className="px-2 py-2">
                      <div className="text-center text-sm">
                        Already have an account?{" "}
                        <span className="text-primary">Sign in</span>
                      </div>
                    </div>
                  </div>
                  <TextureSeparator />
                  <div className="flex flex-col items-center justify-center">
                    <div className="px-2 py-2">
                      <div className="text-center text-xs">
                        Secured by Supabase
                      </div>
                    </div>
                  </div>
                </div>
              </TextureCardStyled>
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}
```

TextureCard is a composable React card component for shadcn/ui. Use it for settings panels, sign-in forms, pricing tiers, or dashboard widgets that need a tactile surface.

## Installation

### CLI

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

### Manual

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

```tsx title="texture-card.tsx"
import * as React from "react"

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

const TextureCardStyled = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement> & { children?: React.ReactNode }
>(({ className, children, ...props }, ref) => (
  <div
    ref={ref}
    className={cn(
      "rounded-[24px] border border-white/60 dark:border-stone-950/60",
      "bg-gradient-to-b from-neutral-100 to-white/70 dark:from-neutral-800 dark:to-neutral-900",
      className
    )}
    {...props}
  >
    {/* Nested structure for aesthetic borders */}
    <div className="rounded-[23px] border border-black/10 dark:border-neutral-900/80">
      <div className="rounded-[22px] border border-white/50 dark:border-neutral-950">
        <div className="rounded-[21px] border border-neutral-950/20 dark:border-neutral-900/70">
          {/* Inner content wrapper */}
          <div className="w-full rounded-[20px] border border-white/50 text-neutral-500 dark:border-neutral-700/50">
            {children}
          </div>
        </div>
      </div>
    </div>
  </div>
))

// Allows for global css overrides and theme support - similar to shad cn
const TextureCard = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement> & { children?: React.ReactNode }
>(({ className, children, ...props }, ref) => {
  return (
    <div
      ref={ref}
      className={cn(
        "dark:border-border/30 rounded-lg border border-white/60",
        "rounded-[calc(var(--radius))]", // Base radius with fallback
        className
      )}
      {...props}
    >
      <div className="rounded-[calc(var(--radius)-1px)] border border-black/10 dark:border-neutral-900/80">
        <div className="rounded-[calc(var(--radius)-2px)] border border-white/50 dark:border-neutral-950">
          <div className="rounded-[calc(var(--radius)-3px)] border border-neutral-950/20 dark:border-neutral-900/70">
            <div className="from-card/70 to-secondary/50 w-full rounded-[calc(var(--radius)-4px)] border border-white/50 bg-gradient-to-b text-neutral-500 dark:border-neutral-700/50">
              {children}
            </div>
          </div>
        </div>
      </div>
    </div>
  )
})

TextureCard.displayName = "TextureCard"

const TextureCardHeader = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div
    ref={ref}
    className={cn(
      "first:pt-6 last:pb-6", // Adjust padding for first and last child
      className
    )}
    {...props}
  />
))
TextureCardHeader.displayName = "TextureCardHeader"

const TextureCardTitle = React.forwardRef<
  HTMLHeadingElement,
  React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
  <h3
    ref={ref}
    className={cn(
      "pl-2 text-lg leading-tight font-semibold text-neutral-900 dark:text-neutral-100",
      className
    )}
    {...props}
  />
))
TextureCardTitle.displayName = "TextureCardTitle"

const TextureCardDescription = React.forwardRef<
  HTMLParagraphElement,
  React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
  <p
    ref={ref}
    className={cn(
      "pl-2 text-sm text-neutral-600 dark:text-neutral-400",
      className
    )}
    {...props}
  />
))
TextureCardDescription.displayName = "TextureCardDescription"

const TextureCardContent = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div ref={ref} className={cn("px-6 py-4", className)} {...props} />
))
TextureCardContent.displayName = "TextureCardContent"

const TextureCardFooter = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div
    ref={ref}
    className={cn(
      "flex items-center justify-between gap-2 px-6 py-4",

      className
    )}
    {...props}
  />
))
TextureCardFooter.displayName = "TextureCardFooter"

const TextureSeparator = () => {
  return (
    <div className="border border-t-neutral-50 border-r-transparent border-b-neutral-300/50 border-l-transparent dark:border-t-neutral-950 dark:border-b-neutral-700/50" />
  )
}

export {
  TextureCard,
  TextureCardHeader,
  TextureCardStyled,
  TextureCardFooter,
  TextureCardTitle,
  TextureSeparator,
  TextureCardDescription,
  TextureCardContent,
}

export default TextureCard
```

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

## Usage

```tsx
import {
  TextureCard,
  TextureCardContent,
  TextureCardFooter,
  TextureCardHeader,
  TextureCardTitle,
  TextureSeparator,
} from "@/components/ui/texture-card";
```

```tsx
<TextureCard>
  <TextureCardHeader className="flex flex-col gap-4 justify-center items-center  ">
    <div className="p-3 bg-neutral-950 rounded-full">
      <Mail className="h-4 w-4 stroke-neutral-200" />
    </div>
  </TextureCardHeader>

  <TextureCardContent className=" w-48 ">
    <p>
      Lorem ipsum dolor sit, amet consectetur adipisicing elit. Dolorum eos quia
      incidunt perspiciatis, ut, deleniti fugit a aliquam sequi, voluptatum
      pariatur quaerat. Temporibus sed facere at, voluptas dolorem officiis
      incidunt!
    </p>
  </TextureCardContent>
  <TextureSeparator />

  <div>
    <div className="dark:bg-neutral-800 bg-stone-100 pt-px rounded-b-[20px] overflow-hidden ">
      <div className="flex flex-col items-center justify-center">
        <div className="py-2 px-2">
          <p className="font-light dark:text-white text-black">
            Texture <span className="font-medium tracking-wide">card</span>
          </p>
        </div>
      </div>
    </div>
  </div>
</TextureCard>
```
