Marble UI
← Components

Bento Grid

Preview: Default

Bento layout: a 3-column grid (one column on small screens) of feature cards with varied spans, placed in DOM order.

Import

import { BentoGrid, BentoCard } from '@marbleui/ui'

Props

<BentoGrid>

PropTypeDefaultDescription
children *requiredReactNode—
classNamestring—

<BentoCard>

Feature card for BentoGrid: background art, icon, title, description, and a CTA that slides up on hover/focus. With `href` the card is a link; without one it's a static card with no CTA (no dead `#` links).

PropTypeDefaultDescription
description *requiredstring—
title *requiredstring—
backgroundReactNode—
classNamestring—
ctastring'Learn more'
hrefstring—
iconReactNode—
rows1 | 21
span1 | 2 | 31

Usage notes

Marketing grid of linked feature cards in mixed sizes: three columns from the sm breakpoint, one below, rows 14 rem tall. For app layouts use Grid.

<BentoGrid>
  <BentoCard span={2} title="Search everything" description="Issues, docs and people in one palette." icon={<SearchIcon />} href="/features/search" />
  <BentoCard rows={2} title="Agents" description="Hand off tasks; approve risky steps." href="/features/agents" cta="See agents" />
  <BentoCard title="Themes" description="Two presets, dark mode." href="/features/themes" />
</BentoGrid>
  • Each BentoCard is one link whose accessible name is all its text, including the cta (default “Learn more”). Without href the card is static: not a link, and no CTA.
  • span (1–3 columns) and rows (1 or 2) apply from sm up. The grid doesn’t backfill gaps, so make each row add up to three.
  • Rows are a fixed height and cards clip overflow, so keep text short. background art is hidden from assistive tech.
  • The CTA slides in on hover and keyboard focus; under prefers-reduced-motion it fades in without sliding, and the background doesn’t zoom.
Example source · show
import { BentoCard, BentoGrid } from './bento-grid'

const I = ({ d }: { d: string }) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d={d} /></svg>
const Bg = ({ h }: { h: number }) => <div className="size-full" style={{ background: `radial-gradient(circle at 80% 20%, color-mix(in oklab, var(--chart-${h}) 45%, transparent), transparent 60%)` }} />
export const styles = {
  Default: () => (
    <BentoGrid className="w-[44rem] max-w-full">
      <BentoCard span={2} title="Search everything" description="Issues, docs and people from one command palette." icon={<I d="M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16M21 21l-4.3-4.3" />} background={<Bg h={1} />} />
      <BentoCard rows={2} title="Agents" description="Hand off tasks; approve the risky steps." icon={<I d="M12 3v4M12 17v4M3 12h4M17 12h4" />} background={<Bg h={6} />} />
      <BentoCard title="Charts" description="Validated palettes." icon={<I d="M3 3v18h18M7 16l4-4 4 4 5-6" />} background={<Bg h={4} />} />
      <BentoCard title="Themes" description="Two presets, dark mode." icon={<I d="M12 22a10 10 0 1 1 10-10c0 2.5-2 4-4.5 4H15a2 2 0 0 0-1.5 3.3A2 2 0 0 1 12 22" />} background={<Bg h={5} />} />
    </BentoGrid>
  ),
}

Source: packages/ui/src/bento-grid/bento-grid.tsx

Catalog

Category
layout
Priority
Extra
Primitive
custom