---
name: bento-grid-layout
description: Use when you want "Professional, modern, organized" - Uses modular rectangular cards arranged like a clean dashboard.
---

# Bento Grid Layout

> **Category:** Premium / Futuristic  -  **Personality:** Professional, modern, organized
>
> **Best use:** SaaS, feature pages, portfolios
>
> **Ratings:** Professional 5/5 - Casual 4/5 - Exotic 2/5 - Visual impact 4/5 - Difficulty 2/5 - Performance cost 1/5

## What it is

A **bento grid** arranges content into modular cards of deliberately *different* sizes — one big anchor, a couple of tall tiles, several small ones — packed into a single tidy grid, like a Japanese bento box. It reads as premium and organized, which is why product-launch pages (Apple, Vercel, Linear) and SaaS feature sections lean on it. This demo is the **dark, futuristic, AI-product** take: a 2×2 hero, a *live* metric, a tall mini-chart, an image cell, a wide gradient CTA and small stat tiles, all lit by a pointer-tracked glow. The layout itself is pure CSS Grid with named `grid-template-areas`; everything else is theming.

## Dependencies / CDN

**None for the layout — pure CSS Grid.** The pointer-glow and live counter are a few lines of vanilla JS (optional). Display/body fonts are optional:

```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=Sora:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
```

## HTML

Each card is a flat child of the grid; its size comes entirely from the CSS area it is assigned to:

```html
<div class="ax-grid">
  <article class="ax-card ax-hero">  …big 2×2 anchor (headline + command chip)… </article>
  <article class="ax-card ax-live">  …live metric (animated number)…            </article>
  <article class="ax-card ax-chart"> …tall tile: SVG sparkline / area chart…     </article>
  <article class="ax-card ax-model"> …small feature with icon…                   </article>
  <article class="ax-card ax-cta">   …wide gradient CTA + button…               </article>
  <article class="ax-card ax-img">
    <img src="/path/abstract-02.jpg" alt="Glowing distributed network">
    <span class="ax-img-tag">Edge network</span>
    <div class="ax-img-cap">Live in 38 regions worldwide.</div>
  </article>
  <article class="ax-card ax-uptime"> …small stat + status bars…                 </article>
</div>
```

## CSS

```css
/* 1) THE GRID — named areas are what make it a *bento* (varied sizes, no gaps) */
.ax-grid{
  display:grid; gap:14px;
  grid-template-columns:repeat(4,1fr);
  grid-template-rows:repeat(3,minmax(150px,auto));
  grid-template-areas:
    "hero hero live  chart"
    "hero hero model chart"
    "cta  cta  img   uptime";
}
/* assign each card to an area — THIS is the step people forget */
.ax-hero{grid-area:hero}   .ax-live{grid-area:live}     .ax-chart{grid-area:chart}
.ax-model{grid-area:model} .ax-cta{grid-area:cta}       .ax-img{grid-area:img}
.ax-uptime{grid-area:uptime}

/* 2) Card shell — dark surface, hairline border, soft lift on hover */
.ax-card{
  position:relative; overflow:hidden; border-radius:18px; padding:20px;
  background:linear-gradient(180deg,#14161f,#0d0f16);
  border:1px solid rgba(255,255,255,.08);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.04), 0 20px 44px -32px rgba(0,0,0,.95);
  transition:transform .35s cubic-bezier(.2,.7,.2,1), border-color .35s, box-shadow .35s;
}
.ax-card:hover{
  transform:translateY(-4px); border-color:rgba(255,255,255,.18);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.07), 0 34px 60px -30px #000,
             0 0 30px -8px rgba(70,220,255,.18);
}

/* 3) Pointer-tracked glow — only the card under the cursor lights up.
      JS writes --mx/--my (card-local px); off-card cards keep them off-screen. */
.ax-card::before{
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none; opacity:0;
  background:radial-gradient(280px circle at var(--mx,-300px) var(--my,-300px),
            rgba(120,200,255,.16), transparent 62%);
  transition:opacity .4s;
}
.ax-grid:hover .ax-card::before{opacity:1}
.ax-card > .ax-c{position:relative; z-index:1}   /* keep content above the glow */

/* 4) Responsive — re-map the SAME areas, never break out of the frame */
@media (max-width:900px){
  .ax-grid{grid-template-columns:repeat(2,1fr);
    grid-template-rows:repeat(6,minmax(140px,auto));
    grid-template-areas:
      "hero  hero" "hero  hero" "live  chart"
      "model chart" "cta   cta"  "img   uptime";}
}
@media (max-width:560px){
  .ax-grid{grid-template-columns:1fr; grid-template-areas:none;
    grid-auto-rows:minmax(120px,auto); gap:12px}
  .ax-card{grid-area:auto !important}   /* areas dropped → cards flow in DOM order */
}

/* 5) Respect reduced motion */
@media (prefers-reduced-motion:reduce){
  .ax-card,.ax-card:hover{animation:none; transform:none}
}
```

## JavaScript

Optional. Two small enhancements — both degrade gracefully if removed:

```js
const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;

/* a) Pointer-tracked spotlight across the whole grid (rAF-throttled) */
const grid = document.querySelector('.ax-grid');
const cards = grid.querySelectorAll('.ax-card');
let px=0, py=0, queued=false;
function paint(){
  queued=false;
  for(const c of cards){
    const r = c.getBoundingClientRect();
    c.style.setProperty('--mx', (px-r.left)+'px');
    c.style.setProperty('--my', (py-r.top)+'px');
  }
}
grid.addEventListener('pointermove', e => {
  px=e.clientX; py=e.clientY;
  if(!queued){ queued=true; requestAnimationFrame(paint); }
}, {passive:true});

/* b) Live metric that ticks — only when motion is allowed */
const t = document.getElementById('ax-tokens');
if(t && !reduce){
  let v = 1284920;
  setInterval(() => {
    v += Math.round(Math.random()*16000 - 7600);
    v = Math.min(1460000, Math.max(1120000, v));
    t.textContent = v.toLocaleString('en-US');
  }, 150);
}
```

## How it works

- **`grid-template-areas` is the whole trick.** You paint an ASCII map of the layout, give each card a `grid-area` name, and the browser sizes/places every card — repeating a name across cells is what makes a card span 2×2 or 1×2. No `col-span`/`row-span` math, and the source order stays human-readable.
- **`minmax(150px, auto)` rows** give every tile a comfortable floor while letting text-heavy cards grow, so the grid never collapses into slivers.
- **Different sizes carry hierarchy:** the 2×2 hero anchors the eye, the wide CTA acts as a full-width band, and the 1×1 tiles read as supporting facts.
- **The glow** is a single `radial-gradient` in each card's `::before`, positioned at a CSS variable the pointer updates. Because cards not under the cursor receive an off-card coordinate, only the hovered tile actually lights — one cheap listener powers the whole grid.

## Customization

- **Re-shape the bento:** edit the `grid-template-areas` strings — that is the entire layout. Add a column (`repeat(5,1fr)`) or a row and repaint the map.
- **Density:** `gap` (12–18px) and the row floor (`minmax`) tune how tight/airy it feels.
- **Theme:** swap the accent pair (here cyan `#46dcff` → violet `#a78bfa`) in the gradient text, button, glow and chart stroke for an instant re-skin. Drop in a light palette for a classic SaaS bento.
- **Glow radius/strength:** change the `280px` circle and the `.16` alpha in `::before`.
- **Live tiles:** point the counter at any element by id; feed the SVG `<path>` real data for the chart.

## Accessibility & performance

- **Reduced motion:** the live counter, entrance stagger, drifting aura and chart pulse are all gated behind `prefers-reduced-motion` (CSS *and* a `matchMedia` check in JS before the `setInterval`).
- **Contrast:** body copy uses a lifted grey (`#8a92a8`) on near-black; metrics/headlines are near-white. Keep gradient-clipped text large (it loses contrast at small sizes).
- **Semantics:** cards are `<article>`s; purely decorative bits (orb, ring, bars, sparkline) carry `aria-hidden="true"`; images get real `alt` text.
- **Cheap:** the layout is GPU-free CSS Grid. Hover animates only `transform`/`box-shadow`, and the pointer handler is throttled to one `requestAnimationFrame` per frame.

## Gotchas

- **Assign every `grid-area`.** Defining `grid-template-areas` without giving each card a matching `grid-area` name silently auto-flows the cards and the bento falls apart — the single most common mistake.
- **Area maps must be rectangular.** Every row string needs the same number of columns and each name must form a solid rectangle, or the declaration is ignored.
- **Glow needs content lifted above it.** The `::before` is `z-index:0`; give the card's inner content `position:relative; z-index:1` or the gradient washes over your text.
- **`overflow:hidden` + `border-radius`** on the card is what clips the glow (and image zoom) to rounded corners — keep it.
- **Don't full-bleed.** Size the grid inside a normal container; `width:100vw`/margin breakouts trigger horizontal scrollbars. Re-map areas at breakpoints instead of letting tiles overflow.
