Bento

Flexible bento grids for features, media, and metrics.

Bento layouts for mixing product details, media, and proof in one section. Replace the content and keep the rhythm.

Mosaic

A mixed set of media, stats, avatars, branding, and a CTA. It stacks on narrow widths.

Ship interfaces that feel finished

Build your interface with ready made blocks and modern design.

Free

Open source component library

95%

Teams ship faster with reusable blocks.

300+

Blocks and patterns

?????

Rapid development

Drop in blocks and keep shipping.

tsx
"use client";import {  Avatar,  AvatarFallback,  AvatarGroup,  AvatarImage,} from "@/components/ui/avatar";import { Button } from "@/components/ui/button";import { cn } from "@/lib/utils";type Bento01Props = {  className?: string;};const AVATARS = [  "/assets/brand/demos/avatars/avatar-1.png",  "/assets/brand/demos/avatars/avatar-2.png",  "/assets/brand/demos/avatars/avatar-3.png",  "/assets/brand/demos/avatars/avatar-4.png",  "/assets/brand/demos/avatars/avatar-5.png",] as const;function Mark({ className }: { className?: string }) {  return (    <img      src="/assets/brand/logo/ui.svg"      alt=""      className={cn("brightness-0 dark:invert", className)}    />  );}export function Bento01({ className }: Bento01Props) {  return (    <section className={cn("@container w-full bg-background", className)}>      <div className="mx-auto w-full max-w-5xl px-4 py-10 @[32rem]:px-6 @[32rem]:py-14">        <div className="grid grid-cols-1 gap-3 @[32rem]:grid-cols-2 @[48rem]:grid-cols-4 @[48rem]:grid-rows-[minmax(11rem,1fr)_minmax(9rem,1fr)_minmax(10rem,1fr)]">          {/* Tall image — full width on mobile, half on tablet, tall on desktop */}          <div className="relative min-h-[14rem] overflow-hidden rounded-xl bg-muted @[32rem]:col-span-2 @[32rem]:min-h-[16rem] @[48rem]:col-span-1 @[48rem]:row-span-2">            <img              src="/assets/brand/demos/attachments/attachment-1.png"              alt=""              className="absolute inset-0 size-full object-cover"            />            <div              aria-hidden="true"              className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/15 to-transparent"            />            <p className="absolute inset-x-0 bottom-0 p-4 text-sm font-medium tracking-wide text-white">              Ship interfaces that feel finished            </p>          </div>          {/* Big middle: pitch + CTA */}          <div className="flex min-h-[16rem] flex-col justify-between rounded-xl bg-muted p-5 @[32rem]:col-span-2 @[48rem]:row-span-2">            <Mark className="size-8" />            <div className="mt-8 flex flex-1 flex-col justify-end gap-6">              <p className="max-w-sm text-sm font-medium tracking-wide text-foreground">                Build your interface with ready made blocks and modern design.              </p>              <div>                <p className="text-3xl font-medium tracking-wide text-foreground">                  Free                </p>                <p className="mt-1 text-sm tracking-wide text-muted-foreground">                  Open source component library                </p>                <Button type="button" size="sm" className="mt-4">                  Get started                </Button>              </div>            </div>          </div>          {/* Stat */}          <div className="flex min-h-[10rem] flex-col justify-between rounded-xl bg-muted p-5 @[32rem]:min-h-[11rem]">            <p className="text-4xl font-medium tracking-wide text-foreground">              95%            </p>            <p className="text-sm tracking-wide text-muted-foreground">              Teams ship faster with reusable blocks.            </p>          </div>          {/* Mark tile */}          <div className="flex min-h-[10rem] items-center justify-center rounded-xl bg-muted p-5 @[32rem]:min-h-[11rem] @[48rem]:min-h-[9rem] @[48rem]:col-start-4 @[48rem]:row-start-2">            <Mark className="size-12" />          </div>          {/* Social proof */}          <div className="flex min-h-[10rem] flex-col justify-between rounded-xl bg-muted p-5 @[32rem]:col-span-1">            <div>              <p className="text-3xl font-medium tracking-wide text-foreground">                300+              </p>              <p className="mt-1 text-sm tracking-wide text-muted-foreground">                Blocks and patterns              </p>            </div>            <AvatarGroup overlap="sm" className="mt-4">              {AVATARS.map((src) => (                <Avatar key={src} size="sm">                  <AvatarImage src={src} alt="" />                  <AvatarFallback>?</AvatarFallback>                </Avatar>              ))}            </AvatarGroup>          </div>          {/* Wide mark — full row on tablet */}          <div className="flex min-h-[10rem] items-center justify-center rounded-xl bg-muted p-5 @[32rem]:col-span-1 @[48rem]:col-span-2">            <Mark className="size-14" />          </div>          {/* Image + copy — full width on mobile/tablet */}          <div className="relative min-h-[12rem] overflow-hidden rounded-xl bg-muted @[32rem]:col-span-2 @[48rem]:col-span-1 @[48rem]:min-h-[10rem]">            <img              src="/assets/brand/demos/attachments/attachment-3.png"              alt=""              className="absolute inset-0 size-full object-cover"            />            <div              aria-hidden="true"              className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent"            />            <div className="absolute inset-x-0 bottom-0 p-4">              <p className="text-sm font-medium tracking-wide text-white">                Rapid development              </p>              <p className="mt-1 text-xs tracking-wide text-white/75">                Drop in blocks and keep shipping.              </p>            </div>          </div>        </div>      </div>    </section>  );}

Installation

npx @arctis-sh/@arctis-sh/ui@latest add bento-01

Usage

import { Bento01 } from "@/components/blocks/bento/bento-01"
<Bento01 />

Feature bento

Uneven image tiles with concise copy overlays. They stack on narrow widths.

  • Token ready

    Restyle every block with your colors, radii, and type.

  • Copy and ship

    Install a block and drop it into a page.

  • Compose

    Mix sections freely.

  • Accessible

    Keyboard-first defaults.

  • Rapid development

    Ship interfaces that feel finished.

  • Theme sync

    Light and dark out of the box.

tsx
"use client";import { cn } from "@/lib/utils";type Bento02Props = {  className?: string;};const CELLS = [  {    title: "Token ready",    description: "Restyle every block with your colors, radii, and type.",    image: "/assets/brand/demos/attachments/attachment-1.png",    className:      "@[32rem]:col-span-2 @[32rem]:row-span-2 @[48rem]:col-span-2 @[48rem]:row-span-2",  },  {    title: "Copy and ship",    description: "Install a block and drop it into a page.",    image: "/assets/brand/demos/attachments/attachment-2.png",    className: "@[32rem]:col-span-2 @[48rem]:col-span-2",  },  {    title: "Compose",    description: "Mix sections freely.",    image: "/assets/brand/demos/attachments/attachment-3.png",    className: "@[32rem]:col-span-1 @[48rem]:col-span-1",  },  {    title: "Accessible",    description: "Keyboard-first defaults.",    image: "/assets/brand/demos/attachments/attachment-4.png",    className: "@[32rem]:col-span-1 @[48rem]:col-span-1",  },  {    title: "Rapid development",    description: "Ship interfaces that feel finished.",    image: "/assets/brand/demos/attachments/attachment-5.png",    className: "@[32rem]:col-span-2 @[48rem]:col-span-2",  },  {    title: "Theme sync",    description: "Light and dark out of the box.",    image: "/assets/brand/demos/attachments/attachment-2.png",    className: "@[32rem]:col-span-2 @[48rem]:col-span-2",  },] as const;export function Bento02({ className }: Bento02Props) {  return (    <section className={cn("@container w-full bg-background", className)}>      <div className="mx-auto w-full max-w-5xl px-4 py-10 @[32rem]:px-6 @[32rem]:py-14">        <ul className="grid grid-cols-1 gap-3 @[32rem]:grid-cols-4 @[32rem]:auto-rows-[11rem]">          {CELLS.map((cell) => (            <li              key={cell.title}              className={cn(                "group relative min-h-[12rem] overflow-hidden rounded-xl bg-muted @[32rem]:min-h-0",                cell.className,              )}            >              <img                src={cell.image}                alt=""                className="absolute inset-0 size-full object-cover transition-transform duration-500 ease-out group-hover:scale-[1.03]"              />              <div                aria-hidden="true"                className="absolute inset-0 bg-gradient-to-t from-black/75 via-black/25 to-transparent"              />              <div className="absolute inset-x-0 bottom-0 p-4">                <h3 className="text-sm font-medium tracking-wide text-white">                  {cell.title}                </h3>                <p className="mt-1 text-xs tracking-wide text-white/75">                  {cell.description}                </p>              </div>            </li>          ))}        </ul>      </div>    </section>  );}

Installation

npx @arctis-sh/@arctis-sh/ui@latest add bento-02

Usage

import { Bento02 } from "@/components/blocks/bento/bento-02"
<Bento02 />

Glow outline

Outlined tiles with a restrained glow that follows the cursor along the border.

  • Token ready

    Restyle every block with your theme tokens.

  • Copy and ship

    Install a block and drop it into a page.

  • Compose

    Mix sections freely.

  • Accessible

    Keyboard-first defaults.

  • Rapid development

    Ship interfaces that feel finished.

  • Theme sync

    Light and dark out of the box.

tsx
"use client";import { useRef, type MouseEvent } from "react";import { cn } from "@/lib/utils";type Bento03Props = {  className?: string;};const CELLS = [  {    title: "Token ready",    description: "Restyle every block with your theme tokens.",    image: "/assets/brand/demos/attachments/attachment-1.png",    className: "@[32rem]:col-span-2 @[32rem]:row-span-2",  },  {    title: "Copy and ship",    description: "Install a block and drop it into a page.",    image: "/assets/brand/demos/attachments/attachment-2.png",    className: "@[32rem]:col-span-2",  },  {    title: "Compose",    description: "Mix sections freely.",    image: "/assets/brand/demos/attachments/attachment-3.png",    className: "",  },  {    title: "Accessible",    description: "Keyboard-first defaults.",    image: "/assets/brand/demos/attachments/attachment-4.png",    className: "",  },  {    title: "Rapid development",    description: "Ship interfaces that feel finished.",    image: "/assets/brand/demos/attachments/attachment-5.png",    className: "@[32rem]:col-span-2",  },  {    title: "Theme sync",    description: "Light and dark out of the box.",    image: "/assets/brand/demos/attachments/attachment-2.png",    className: "@[32rem]:col-span-2",  },] as const;/** Visual reach past each tile so gaps between cards can light neighbors. */const GLOW_REACH_PX = 160;const GLOW_RADIUS = "16rem";function GlowTile({  className,  title,  description,  image,}: {  className?: string;  title: string;  description: string;  image: string;}) {  return (    <li      data-glow-tile      data-glow="off"      className={cn(        "relative min-h-[12rem] rounded-xl p-px @[32rem]:min-h-0",        "data-[glow=off]:![background:var(--border)]",        className,      )}      style={{        background: `radial-gradient(${GLOW_RADIUS} circle at var(--glow-x, 50%) var(--glow-y, 50%), rgb(120 200 255), rgb(190 140 255 / 0.85), rgb(255 180 140 / 0.4), var(--border) 55%)`,      }}    >      <div className="flex size-full flex-col rounded-[calc(var(--radius-xl)-1px)] bg-background p-2">        <div className="relative min-h-0 flex-1 overflow-hidden rounded-sm bg-muted">          <img            src={image}            alt=""            className="absolute inset-0 size-full object-cover"          />        </div>        <div className="px-1 pt-3 pb-1">          <h3 className="text-sm font-medium tracking-wide text-foreground">            {title}          </h3>          <p className="mt-1 text-xs tracking-wide text-muted-foreground">            {description}          </p>        </div>      </div>    </li>  );}export function Bento03({ className }: Bento03Props) {  const gridRef = useRef<HTMLUListElement>(null);  function syncGlow(clientX: number, clientY: number) {    const grid = gridRef.current;    if (!grid) return;    const tiles = grid.querySelectorAll<HTMLElement>("[data-glow-tile]");    for (const el of tiles) {      const rect = el.getBoundingClientRect();      const w = el.offsetWidth;      const h = el.offsetHeight;      const x = ((clientX - rect.left) / Math.max(rect.width, 1)) * w;      const y = ((clientY - rect.top) / Math.max(rect.height, 1)) * h;      el.style.setProperty("--glow-x", `${x}px`);      el.style.setProperty("--glow-y", `${y}px`);      const near =        clientX >= rect.left - GLOW_REACH_PX &&        clientX <= rect.right + GLOW_REACH_PX &&        clientY >= rect.top - GLOW_REACH_PX &&        clientY <= rect.bottom + GLOW_REACH_PX;      el.dataset.glow = near ? "on" : "off";    }  }  function onMove(event: MouseEvent<HTMLUListElement>) {    syncGlow(event.clientX, event.clientY);  }  function onLeave() {    const grid = gridRef.current;    if (!grid) return;    for (const el of grid.querySelectorAll<HTMLElement>("[data-glow-tile]")) {      el.dataset.glow = "off";    }  }  return (    <section className={cn("@container w-full bg-background", className)}>      <div className="mx-auto w-full max-w-5xl px-4 py-10 @[32rem]:px-6 @[32rem]:py-14">        <ul          ref={gridRef}          onMouseMove={onMove}          onMouseLeave={onLeave}          className="grid grid-cols-1 gap-3 @[32rem]:grid-cols-4 @[32rem]:auto-rows-[12.5rem]"        >          {CELLS.map((cell) => (            <GlowTile              key={cell.title}              className={cell.className}              title={cell.title}              description={cell.description}              image={cell.image}            />          ))}        </ul>      </div>    </section>  );}

Installation

npx @arctis-sh/@arctis-sh/ui@latest add bento-03

Usage

import { Bento03 } from "@/components/blocks/bento/bento-03"
<Bento03 />