# Features Icon Grid Hover — Tailwind Feature Section

> A hairline grid of six features: grid lines glow under the cursor, a beam laps the hovered cell's border and a detail row slides up.

- Category: [Feature Sections](https://pixelpapa.com/components/features.md)
- Tags: features, grid, hover, border-beam, spotlight
- Dependencies: lucide-react, motion (plus React and Tailwind CSS v4)
- Last updated: 2026-11-13
- Live preview: https://pixelpapa.com/components/features/features-icon-grid-hover

## Install

```bash
npx shadcn@latest add "https://pixelpapa.com/r/features-icon-grid-hover.json"
```

The file is written to `components/pixelpapa/features-icon-grid-hover.tsx`. To install manually, run `npm install lucide-react motion` and copy the source below into your project. It imports `cn` from `@/lib/utils`, which every shadcn project has.

## Source

```tsx
"use client";

import { ArrowUpRight, Bell, GitBranch, Globe, History, KeyRound, ScanLine, type LucideIcon } from "lucide-react";
import { motion } from "motion/react";
import { useRef, type PointerEvent } from "react";

type Feature = { icon: LucideIcon; title: string; body: string; detail: string };

const defaultFeatures: Feature[] = [
  {
    icon: History,
    title: "Instant rollbacks",
    body: "Every deploy is an immutable snapshot. Point production back at any of them in one click.",
    detail: "Median rollback: 4.2s",
  },
  {
    icon: GitBranch,
    title: "Preview per branch",
    body: "Open a pull request and get a full environment with its own database branch and URL.",
    detail: "Ready before CI finishes",
  },
  {
    icon: Globe,
    title: "Edge in 38 regions",
    body: "Static assets and cached responses are served from the city nearest to each visitor.",
    detail: "p50 time to first byte: 21ms",
  },
  {
    icon: KeyRound,
    title: "Secrets that rotate",
    body: "Environment variables are encrypted per project and rotated on a schedule you choose.",
    detail: "AES-256, keys never logged",
  },
  {
    icon: Bell,
    title: "Spend alerts",
    body: "Set a monthly budget per team. We warn at 50, 80 and 100 percent, then pause if you ask.",
    detail: "No surprise invoices",
  },
  {
    icon: ScanLine,
    title: "Audit trail",
    body: "Who shipped what, when and from where. Export to your SIEM or query it with SQL.",
    detail: "13 months of retention",
  },
];

export default function FeaturesIconGridHover({ features = defaultFeatures }: { features?: Feature[] }) {
  const ref = useRef<HTMLDivElement>(null);

  function onPointerMove(e: PointerEvent<HTMLDivElement>) {
    const el = ref.current;
    if (!el) return;
    const r = el.getBoundingClientRect();
    el.style.setProperty("--x", `${e.clientX - r.left}px`);
    el.style.setProperty("--y", `${e.clientY - r.top}px`);
  }

  return (
    <section className="w-full bg-background px-6 py-20 sm:py-24">
      <style>{`@keyframes pp-features-icon-grid-hover-spin { to { transform: rotate(360deg); } }`}</style>
      <div className="mx-auto max-w-6xl">
        <div className="flex flex-col justify-between gap-6 lg:flex-row lg:items-end">
          <h2 className="max-w-xl text-4xl font-semibold tracking-tight text-balance text-foreground sm:text-5xl">
            The platform work you would rather not build twice
          </h2>
          <p className="max-w-sm text-muted-foreground">
            Kiln ships with the release tooling mature teams end up writing themselves, ready on the first deploy.
          </p>
        </div>

        {/* gap-px over a bg-border container draws the hairlines; the glow layer shows through them. */}
        <div
          ref={ref}
          onPointerMove={onPointerMove}
          className="group/grid relative mt-12 grid gap-px overflow-hidden rounded-3xl border border-border bg-border sm:grid-cols-2 lg:grid-cols-3"
        >
          <div
            aria-hidden
            className="pointer-events-none absolute inset-0 opacity-0 transition-opacity duration-500 group-hover/grid:opacity-100"
            style={{ background: "radial-gradient(380px circle at var(--x) var(--y), var(--primary), transparent 65%)" }}
          />
          {features.map((f, i) => (
            <motion.a
              key={f.title}
              href="#"
              initial={{ opacity: 0 }}
              whileInView={{ opacity: 1 }}
              viewport={{ once: true, amount: 0.3 }}
              transition={{ duration: 0.6, delay: i * 0.06 }}
              className="group relative flex min-h-64 flex-col bg-card p-8 outline-none focus-visible:z-10 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset"
            >
              {/* Border beam: a spinning conic gradient masked down to a 1px frame. */}
              <span
                aria-hidden
                className="pointer-events-none absolute inset-0 overflow-hidden p-px opacity-0 transition-opacity duration-300 group-hover:opacity-100 [mask:linear-gradient(#000_0_0)_content-box_exclude,linear-gradient(#000_0_0)]"
              >
                <span className="absolute top-1/2 left-1/2 -mt-[100%] -ml-[100%] aspect-square w-[200%] animate-[pp-features-icon-grid-hover-spin_3.2s_linear_infinite] [background:conic-gradient(transparent_0_280deg,var(--primary)_360deg)] motion-reduce:animate-none" />
              </span>
              <span
                aria-hidden
                className="pointer-events-none absolute inset-0 opacity-0 transition-opacity duration-500 group-hover:opacity-100 [background:radial-gradient(60%_60%_at_20%_0%,color-mix(in_oklch,var(--primary)_10%,transparent),transparent)]"
              />

              <span className="relative flex size-11 items-center justify-center rounded-xl border border-border bg-background text-foreground transition-all duration-300 group-hover:-translate-y-1 group-hover:border-primary group-hover:bg-primary group-hover:text-primary-foreground group-hover:shadow-lg group-hover:shadow-primary/30">
                <f.icon className="size-5" />
              </span>
              <h3 className="relative mt-6 text-lg font-medium text-card-foreground">{f.title}</h3>
              <p className="relative mt-2 text-sm leading-relaxed text-muted-foreground">{f.body}</p>

              <span className="relative mt-auto flex items-center justify-between pt-6 text-sm">
                <span className="translate-y-2 font-mono text-xs text-primary opacity-0 transition-all duration-300 group-hover:translate-y-0 group-hover:opacity-100 group-focus-visible:translate-y-0 group-focus-visible:opacity-100">
                  {f.detail}
                </span>
                <ArrowUpRight className="size-4 text-muted-foreground transition-all duration-300 group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-primary" />
              </span>
            </motion.a>
          ))}
        </div>
      </div>
    </section>
  );
}
```

## AI prompt

```text
Add the "Features Icon Grid Hover" component from PixelPapa UI to this project.

What it is: A hairline grid of six features: grid lines glow under the cursor, a beam laps the hovered cell's border and a detail row slides up.
Category: Feature Sections. Depends on: lucide-react, motion.

1. Install it with the shadcn CLI:
   npx shadcn@latest add "https://pixelpapa.com/r/features-icon-grid-hover.json"
   (If the CLI is unavailable, fetch that URL, write files[0].content to components/pixelpapa/features-icon-grid-hover.tsx and install the listed dependencies.)
2. Import it from "@/components/pixelpapa/features-icon-grid-hover" and place it where I describe below.
3. Keep its animation timings and accessibility attributes. Replace the demo copy and data with content that fits my product, and use my existing theme tokens (bg-background, text-foreground, bg-primary, border-border) rather than hard-coded colours.

Where to use it: <describe the page and position here>
```

## More feature sections

- [Features Accordion Visual](https://pixelpapa.com/components/features/features-accordion-visual.md) (Pro): An accordion on the left steers a product mock on the right: a spring-driven focus frame glides to the matching region while the rest dims.
- [Features Alternating Rows](https://pixelpapa.com/components/features/features-alternating-rows.md) (Pro): Three text-and-illustration rows that swap sides; each drawing animates in on view and its floating layer drifts with scroll.
- [Features Animated Workflow](https://pixelpapa.com/components/features/features-animated-workflow.md) (Pro): A four-stage pipeline where an order token hops card to card with a shared-layout spring, leaving a completed result behind at every stage.
- [Features Big Numbered List](https://pixelpapa.com/components/features/features-big-numbered-list.md): An editorial list with oversized serif numerals that rise through a mask on view; the hovered or focused row opens its note and a marker bar slides to it.
- [Features Code And Result](https://pixelpapa.com/components/features/features-code-and-result.md) (Pro): A code snippet with clickable values beside a live preview: cycle a prop in the code and the toast on the right springs in with the new tone and position.
- [Features Comparison Table](https://pixelpapa.com/components/features/features-comparison-table.md): An us-versus-them table with a lifted highlight column; checks pop and draw their stroke row by row as the table scrolls in.
