Feature 3

Bento feature grid: a large lead tile with a faux file-tree preview beside five smaller token-lit tiles, each with an icon, title and short blurb.

Preview

Installation

npx shadcn@latest add https://hirael.com/r/feature-03.json

Code

components/blocks/feature-03.tsx
import {
  Combine,
  Languages,
  Braces,
  Palette,
  Server,
  Check,
  type LucideIcon,
} from "lucide-react";

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

type Tile = {
  icon: LucideIcon;
  title: string;
  body: string;
  swatches?: boolean;
};

const TILES: readonly Tile[] = [
  {
    icon: Combine,
    title: "Dual API by default",
    body: "Every component ships a compound surface and a single-prop surface in one file. Reach for whichever fits.",
  },
  {
    icon: Languages,
    title: "RTL is built in",
    body: "Logical properties throughout, so every component works under dir=rtl with no extra config.",
  },
  {
    icon: Braces,
    title: "Typed end to end",
    body: "Strict TypeScript with generics that flow from options to onChange. No any, no manual hints.",
  },
  {
    icon: Palette,
    title: "Your theme tokens",
    body: "Reads the same CSS variables as shadcn/ui, so it re-skins live against any token system.",
    swatches: true,
  },
  {
    icon: Server,
    title: "SSR safe",
    body: "Server components by default, with 'use client' only where interactivity demands it.",
  },
];

const WRITTEN_FILES = ["button.tsx", "input.tsx", "combobox.tsx"] as const;
const SWATCHES = [
  "bg-background",
  "bg-card",
  "bg-muted",
  "bg-primary",
  "bg-foreground",
] as const;

export default function Feature03() {
  return (
    <section data-slot="feature" className="bg-background py-20 sm:py-28">
      <div className="mx-auto w-full max-w-6xl px-6 md:px-10">
        <div className="flex max-w-2xl flex-col gap-5">
          <span className="font-mono text-[10px] uppercase tracking-[0.12em] text-muted-foreground">
            why hirael
          </span>
          <h2 className="font-serif text-4xl font-medium leading-[1.04] tracking-tight sm:text-5xl">
            One system, every surface.
          </h2>
          <p className="text-base text-muted-foreground sm:text-lg">
            The decisions that hold across the whole catalog, from a single
            input to a full page template.
          </p>
        </div>

        <div
          data-slot="feature-bento"
          className="mt-14 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-6"
        >
          <div
            data-slot="feature-tile"
            className="relative flex flex-col justify-between overflow-hidden rounded-xl border border-border bg-card p-6 sm:col-span-2 lg:col-span-4 lg:row-span-2"
          >
            <div
              aria-hidden
              className="pointer-events-none absolute inset-0"
              style={{
                backgroundImage:
                  "radial-gradient(90% 70% at 100% 0%, color-mix(in oklch, var(--primary) 10%, transparent), transparent 60%)",
              }}
            />
            <div className="relative z-10 flex flex-col gap-2">
              <span className="font-mono text-[10px] uppercase tracking-[0.12em] text-muted-foreground">
                source you own
              </span>
              <h3 className="max-w-md text-2xl font-semibold leading-[1.1] tracking-[-0.02em] sm:text-3xl">
                Installed as plain TSX, never a black box.
              </h3>
              <p className="max-w-md text-sm text-muted-foreground">
                The CLI copies the source into your repo. No runtime package, no
                version pin, no upgrade path to fight. Edit it like it is yours,
                because it is.
              </p>
            </div>

            <div
              aria-hidden
              className="relative z-10 mt-8 overflow-hidden rounded-md border border-border bg-background"
            >
              <div className="flex items-center justify-between border-b border-border px-4 py-2.5">
                <span className="font-mono text-[10px] uppercase tracking-[0.12em] text-muted-foreground">
                  components/ui
                </span>
                <div className="flex gap-1">
                  <span className="size-1.5 rounded-full bg-border" />
                  <span className="size-1.5 rounded-full bg-border" />
                  <span className="size-1.5 rounded-full bg-foreground" />
                </div>
              </div>
              <ul className="flex flex-col gap-2.5 p-4 font-mono text-[12px]">
                {WRITTEN_FILES.map((file) => (
                  <li
                    key={file}
                    className="flex items-center gap-2 text-foreground"
                  >
                    <Check className="size-3.5 shrink-0 text-muted-foreground" />
                    {file}
                  </li>
                ))}
                <li className="ps-[22px] text-muted-foreground/60">
                  + 4 more files
                </li>
              </ul>
            </div>
          </div>

          {TILES.map((tile) => {
            const Icon = tile.icon;
            return (
              <div
                key={tile.title}
                data-slot="feature-tile"
                className="flex flex-col gap-4 rounded-xl border border-border bg-card p-6 lg:col-span-2"
              >
                <span className="inline-flex size-9 items-center justify-center rounded-md border border-border bg-background">
                  <Icon className="size-4" />
                </span>
                <div className="flex flex-col gap-1.5">
                  <h3 className="text-base font-semibold tracking-[-0.01em]">
                    {tile.title}
                  </h3>
                  <p className="text-sm text-muted-foreground">{tile.body}</p>
                </div>
                {tile.swatches ? (
                  <div className="mt-auto flex items-center gap-1.5 pt-2">
                    {SWATCHES.map((swatch) => (
                      <span
                        key={swatch}
                        className={cn(
                          "size-5 rounded-md border border-border",
                          swatch,
                        )}
                      />
                    ))}
                  </div>
                ) : null}
              </div>
            );
          })}
        </div>
      </div>
    </section>
  );
}

Dependencies

npm

lucide-react