30 CSS Grid Layouts09 / 30
Bento Box
The Apple-keynote bento: a dark grid of feature tiles where one 2×2 hero anchors the composition and satellites of varied spans orbit it. Each tile owns a different oklch accent glow, hover lifts are compositor-only, and the whole board reflows to a clean 2-column stack on phones.
Published Updated
The code
<section class="cgl-09" aria-label="Bento box grid demo">
<div class="cgl-09__board">
<article class="cgl-09__tile cgl-09__tile--hero" style="--glow:oklch(0.7 0.19 300)">
<h3>One grid.<br>Every tile.</h3><p>The 2×2 hero anchors the bento; satellites auto-place around it.</p>
</article>
<article class="cgl-09__tile" style="--glow:oklch(0.78 0.16 200)"><h4>Auto rows</h4><p>minmax(96px, auto) — floors, not ceilings.</p></article>
<article class="cgl-09__tile" style="--glow:oklch(0.8 0.17 140)"><h4>Spans</h4><p>span 2 wide, span 2 tall.</p></article>
<article class="cgl-09__tile cgl-09__tile--tall" style="--glow:oklch(0.75 0.17 60)"><h4>Tall satellite</h4><p>grid-row: span 2 gives vertical rhythm without fixed heights.</p></article>
<article class="cgl-09__tile cgl-09__tile--wide" style="--glow:oklch(0.68 0.2 20)"><h4>Wide satellite</h4><p>grid-column: span 2 — stats, charts, marquees.</p></article>
<article class="cgl-09__tile" style="--glow:oklch(0.72 0.15 250)"><h4>Theming</h4><p>One --glow variable per tile.</p></article>
</div>
</section><section class="cgl-09" aria-label="Bento box grid demo">
<div class="cgl-09__board">
<article class="cgl-09__tile cgl-09__tile--hero" style="--glow:oklch(0.7 0.19 300)">
<h3>One grid.<br>Every tile.</h3><p>The 2×2 hero anchors the bento; satellites auto-place around it.</p>
</article>
<article class="cgl-09__tile" style="--glow:oklch(0.78 0.16 200)"><h4>Auto rows</h4><p>minmax(96px, auto) — floors, not ceilings.</p></article>
<article class="cgl-09__tile" style="--glow:oklch(0.8 0.17 140)"><h4>Spans</h4><p>span 2 wide, span 2 tall.</p></article>
<article class="cgl-09__tile cgl-09__tile--tall" style="--glow:oklch(0.75 0.17 60)"><h4>Tall satellite</h4><p>grid-row: span 2 gives vertical rhythm without fixed heights.</p></article>
<article class="cgl-09__tile cgl-09__tile--wide" style="--glow:oklch(0.68 0.2 20)"><h4>Wide satellite</h4><p>grid-column: span 2 — stats, charts, marquees.</p></article>
<article class="cgl-09__tile" style="--glow:oklch(0.72 0.15 250)"><h4>Theming</h4><p>One --glow variable per tile.</p></article>
</div>
</section>.cgl-09,
.cgl-09 *,
.cgl-09 *::before,
.cgl-09 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cgl-09 {
width: 100%;
min-height: 100vh;
--bg: oklch(0.14 0.015 290);
--txt: oklch(0.95 0.01 290);
--dim: oklch(0.68 0.02 290);
font-family: system-ui,sans-serif;
background: var(--bg);
padding: 1.4rem;
}
.cgl-09 .cgl-09__board {
display: grid;
grid-template-columns: repeat(4,minmax(0,1fr));
grid-auto-rows: minmax(96px,auto);
gap: 12px;
}
.cgl-09 .cgl-09__tile {
--glow: oklch(0.7 0.15 290);
position: relative;
border-radius: 16px;
padding: 1.1rem;
color: var(--txt);
background: radial-gradient(140% 120% at 15% 0%,color-mix(in oklab,var(--glow) 16%,transparent),transparent 55%),oklch(0.19 0.02 290);
border: 1px solid color-mix(in oklab,var(--glow) 28%,oklch(0.3 0.02 290));
display: flex;
flex-direction: column;
gap: .45rem;
justify-content: flex-end;
transition: translate .25s ease,box-shadow .25s ease;
}
.cgl-09 .cgl-09__tile:hover {
translate: 0 -3px;
box-shadow: 0 18px 36px -18px color-mix(in oklab,var(--glow) 45%,transparent);
}
.cgl-09 .cgl-09__tile--hero {
grid-column: span 2;
grid-row: span 2;
justify-content: center;
}
.cgl-09 .cgl-09__tile--wide {
grid-column: span 2;
}
.cgl-09 .cgl-09__tile--tall {
grid-row: span 2;
}
.cgl-09 h3 {
font-size: clamp(1.3rem,3vw,1.8rem);
line-height: 1.15;
color: color-mix(in oklab,var(--glow) 70%,white);
}
.cgl-09 h4 {
font-size: .95rem;
color: color-mix(in oklab,var(--glow) 70%,white);
}
.cgl-09 p {
font-size: .78rem;
line-height: 1.55;
color: var(--dim);
}
@media (max-width: 560px) {
.cgl-09 .cgl-09__board {
grid-template-columns: repeat(2,minmax(0,1fr));
}
.cgl-09 .cgl-09__tile--hero {
grid-column: span 2;
}
}
@media (prefers-reduced-motion: reduce) {
.cgl-09 .cgl-09__tile {
transition: none;
}
}.cgl-09,
.cgl-09 *,
.cgl-09 *::before,
.cgl-09 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cgl-09 {
width: 100%;
min-height: 100vh;
--bg: oklch(0.14 0.015 290);
--txt: oklch(0.95 0.01 290);
--dim: oklch(0.68 0.02 290);
font-family: system-ui,sans-serif;
background: var(--bg);
padding: 1.4rem;
}
.cgl-09 .cgl-09__board {
display: grid;
grid-template-columns: repeat(4,minmax(0,1fr));
grid-auto-rows: minmax(96px,auto);
gap: 12px;
}
.cgl-09 .cgl-09__tile {
--glow: oklch(0.7 0.15 290);
position: relative;
border-radius: 16px;
padding: 1.1rem;
color: var(--txt);
background: radial-gradient(140% 120% at 15% 0%,color-mix(in oklab,var(--glow) 16%,transparent),transparent 55%),oklch(0.19 0.02 290);
border: 1px solid color-mix(in oklab,var(--glow) 28%,oklch(0.3 0.02 290));
display: flex;
flex-direction: column;
gap: .45rem;
justify-content: flex-end;
transition: translate .25s ease,box-shadow .25s ease;
}
.cgl-09 .cgl-09__tile:hover {
translate: 0 -3px;
box-shadow: 0 18px 36px -18px color-mix(in oklab,var(--glow) 45%,transparent);
}
.cgl-09 .cgl-09__tile--hero {
grid-column: span 2;
grid-row: span 2;
justify-content: center;
}
.cgl-09 .cgl-09__tile--wide {
grid-column: span 2;
}
.cgl-09 .cgl-09__tile--tall {
grid-row: span 2;
}
.cgl-09 h3 {
font-size: clamp(1.3rem,3vw,1.8rem);
line-height: 1.15;
color: color-mix(in oklab,var(--glow) 70%,white);
}
.cgl-09 h4 {
font-size: .95rem;
color: color-mix(in oklab,var(--glow) 70%,white);
}
.cgl-09 p {
font-size: .78rem;
line-height: 1.55;
color: var(--dim);
}
@media (max-width: 560px) {
.cgl-09 .cgl-09__board {
grid-template-columns: repeat(2,minmax(0,1fr));
}
.cgl-09 .cgl-09__tile--hero {
grid-column: span 2;
}
}
@media (prefers-reduced-motion: reduce) {
.cgl-09 .cgl-09__tile {
transition: none;
}
}/* No JavaScript — span geometry builds the bento; each tile re-themes through a single --glow custom property. */
/* No JavaScript — span geometry builds the bento; each tile re-themes through a single --glow custom property. */Here's a working CSS Grid Layout from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Bento Box
Source: https://codefronts.com/layouts/css-grid-layouts/bento-box/
The Apple-keynote bento: a dark grid of feature tiles where one 2×2 hero anchors the composition and satellites of varied spans orbit it. Each tile owns a different oklch accent glow, hover lifts are compositor-only, and the whole board reflows to a clean 2-column stack on phones.
## HTML
```html
<section class="cgl-09" aria-label="Bento box grid demo">
<div class="cgl-09__board">
<article class="cgl-09__tile cgl-09__tile--hero" style="--glow:oklch(0.7 0.19 300)">
<h3>One grid.<br>Every tile.</h3><p>The 2×2 hero anchors the bento; satellites auto-place around it.</p>
</article>
<article class="cgl-09__tile" style="--glow:oklch(0.78 0.16 200)"><h4>Auto rows</h4><p>minmax(96px, auto) — floors, not ceilings.</p></article>
<article class="cgl-09__tile" style="--glow:oklch(0.8 0.17 140)"><h4>Spans</h4><p>span 2 wide, span 2 tall.</p></article>
<article class="cgl-09__tile cgl-09__tile--tall" style="--glow:oklch(0.75 0.17 60)"><h4>Tall satellite</h4><p>grid-row: span 2 gives vertical rhythm without fixed heights.</p></article>
<article class="cgl-09__tile cgl-09__tile--wide" style="--glow:oklch(0.68 0.2 20)"><h4>Wide satellite</h4><p>grid-column: span 2 — stats, charts, marquees.</p></article>
<article class="cgl-09__tile" style="--glow:oklch(0.72 0.15 250)"><h4>Theming</h4><p>One --glow variable per tile.</p></article>
</div>
</section>
```
## CSS
```css
.cgl-09,
.cgl-09 *,
.cgl-09 *::before,
.cgl-09 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cgl-09 {
width: 100%;
min-height: 100vh;
--bg: oklch(0.14 0.015 290);
--txt: oklch(0.95 0.01 290);
--dim: oklch(0.68 0.02 290);
font-family: system-ui,sans-serif;
background: var(--bg);
padding: 1.4rem;
}
.cgl-09 .cgl-09__board {
display: grid;
grid-template-columns: repeat(4,minmax(0,1fr));
grid-auto-rows: minmax(96px,auto);
gap: 12px;
}
.cgl-09 .cgl-09__tile {
--glow: oklch(0.7 0.15 290);
position: relative;
border-radius: 16px;
padding: 1.1rem;
color: var(--txt);
background: radial-gradient(140% 120% at 15% 0%,color-mix(in oklab,var(--glow) 16%,transparent),transparent 55%),oklch(0.19 0.02 290);
border: 1px solid color-mix(in oklab,var(--glow) 28%,oklch(0.3 0.02 290));
display: flex;
flex-direction: column;
gap: .45rem;
justify-content: flex-end;
transition: translate .25s ease,box-shadow .25s ease;
}
.cgl-09 .cgl-09__tile:hover {
translate: 0 -3px;
box-shadow: 0 18px 36px -18px color-mix(in oklab,var(--glow) 45%,transparent);
}
.cgl-09 .cgl-09__tile--hero {
grid-column: span 2;
grid-row: span 2;
justify-content: center;
}
.cgl-09 .cgl-09__tile--wide {
grid-column: span 2;
}
.cgl-09 .cgl-09__tile--tall {
grid-row: span 2;
}
.cgl-09 h3 {
font-size: clamp(1.3rem,3vw,1.8rem);
line-height: 1.15;
color: color-mix(in oklab,var(--glow) 70%,white);
}
.cgl-09 h4 {
font-size: .95rem;
color: color-mix(in oklab,var(--glow) 70%,white);
}
.cgl-09 p {
font-size: .78rem;
line-height: 1.55;
color: var(--dim);
}
@media (max-width: 560px) {
.cgl-09 .cgl-09__board {
grid-template-columns: repeat(2,minmax(0,1fr));
}
.cgl-09 .cgl-09__tile--hero {
grid-column: span 2;
}
}
@media (prefers-reduced-motion: reduce) {
.cgl-09 .cgl-09__tile {
transition: none;
}
}
```
## JavaScript
```js
/* No JavaScript — span geometry builds the bento; each tile re-themes through a single --glow custom property. */
```Here's a working CSS Grid Layout from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Bento Box
Source: https://codefronts.com/layouts/css-grid-layouts/bento-box/
The Apple-keynote bento: a dark grid of feature tiles where one 2×2 hero anchors the composition and satellites of varied spans orbit it. Each tile owns a different oklch accent glow, hover lifts are compositor-only, and the whole board reflows to a clean 2-column stack on phones.
## HTML
```html
<section class="cgl-09" aria-label="Bento box grid demo">
<div class="cgl-09__board">
<article class="cgl-09__tile cgl-09__tile--hero" style="--glow:oklch(0.7 0.19 300)">
<h3>One grid.<br>Every tile.</h3><p>The 2×2 hero anchors the bento; satellites auto-place around it.</p>
</article>
<article class="cgl-09__tile" style="--glow:oklch(0.78 0.16 200)"><h4>Auto rows</h4><p>minmax(96px, auto) — floors, not ceilings.</p></article>
<article class="cgl-09__tile" style="--glow:oklch(0.8 0.17 140)"><h4>Spans</h4><p>span 2 wide, span 2 tall.</p></article>
<article class="cgl-09__tile cgl-09__tile--tall" style="--glow:oklch(0.75 0.17 60)"><h4>Tall satellite</h4><p>grid-row: span 2 gives vertical rhythm without fixed heights.</p></article>
<article class="cgl-09__tile cgl-09__tile--wide" style="--glow:oklch(0.68 0.2 20)"><h4>Wide satellite</h4><p>grid-column: span 2 — stats, charts, marquees.</p></article>
<article class="cgl-09__tile" style="--glow:oklch(0.72 0.15 250)"><h4>Theming</h4><p>One --glow variable per tile.</p></article>
</div>
</section>
```
## CSS
```css
.cgl-09,
.cgl-09 *,
.cgl-09 *::before,
.cgl-09 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cgl-09 {
width: 100%;
min-height: 100vh;
--bg: oklch(0.14 0.015 290);
--txt: oklch(0.95 0.01 290);
--dim: oklch(0.68 0.02 290);
font-family: system-ui,sans-serif;
background: var(--bg);
padding: 1.4rem;
}
.cgl-09 .cgl-09__board {
display: grid;
grid-template-columns: repeat(4,minmax(0,1fr));
grid-auto-rows: minmax(96px,auto);
gap: 12px;
}
.cgl-09 .cgl-09__tile {
--glow: oklch(0.7 0.15 290);
position: relative;
border-radius: 16px;
padding: 1.1rem;
color: var(--txt);
background: radial-gradient(140% 120% at 15% 0%,color-mix(in oklab,var(--glow) 16%,transparent),transparent 55%),oklch(0.19 0.02 290);
border: 1px solid color-mix(in oklab,var(--glow) 28%,oklch(0.3 0.02 290));
display: flex;
flex-direction: column;
gap: .45rem;
justify-content: flex-end;
transition: translate .25s ease,box-shadow .25s ease;
}
.cgl-09 .cgl-09__tile:hover {
translate: 0 -3px;
box-shadow: 0 18px 36px -18px color-mix(in oklab,var(--glow) 45%,transparent);
}
.cgl-09 .cgl-09__tile--hero {
grid-column: span 2;
grid-row: span 2;
justify-content: center;
}
.cgl-09 .cgl-09__tile--wide {
grid-column: span 2;
}
.cgl-09 .cgl-09__tile--tall {
grid-row: span 2;
}
.cgl-09 h3 {
font-size: clamp(1.3rem,3vw,1.8rem);
line-height: 1.15;
color: color-mix(in oklab,var(--glow) 70%,white);
}
.cgl-09 h4 {
font-size: .95rem;
color: color-mix(in oklab,var(--glow) 70%,white);
}
.cgl-09 p {
font-size: .78rem;
line-height: 1.55;
color: var(--dim);
}
@media (max-width: 560px) {
.cgl-09 .cgl-09__board {
grid-template-columns: repeat(2,minmax(0,1fr));
}
.cgl-09 .cgl-09__tile--hero {
grid-column: span 2;
}
}
@media (prefers-reduced-motion: reduce) {
.cgl-09 .cgl-09__tile {
transition: none;
}
}
```
## JavaScript
```js
/* No JavaScript — span geometry builds the bento; each tile re-themes through a single --glow custom property. */
```How this works
The board is grid-template-columns: repeat(4, minmax(0,1fr)) with grid-auto-rows: minmax(96px, auto). The hero pins itself with grid-column: span 2; grid-row: span 2; wide tiles span 2 columns, tall tiles span 2 rows. Because auto-rows have a floor but no ceiling, tiles grow with content instead of clipping it — the difference between a bento and a broken masonry.
Each tile's glow is one custom property: --glow feeds both a radial-gradient wash and the border tint via color-mix(), so re-theming a tile is a single line. Hover raises tiles with translate + shadow (transform-only, GPU-composited) and the reduced-motion query stills everything.
Make it yours
- Re-anchor the hero anywhere with explicit lines (
grid-column: 3 / 5) instead of spans. - Add tiles freely — auto-placement fills the board; add
grid-auto-flow: denseif holes appear. - Set each tile's personality by changing only its
--glowhue. - Tighten the board with a smaller auto-row floor (96px → 80px) for denser bentos.
Gotchas — read before shipping
- Give the board minmax(0,1fr) columns or one long unbreakable string distorts every tile in that track.
- Span-2 tiles need the 4-column board to stay 4 columns — the mobile query must also shrink spans (included) or you get horizontal scroll.
- Keep glows under ~20% alpha; bento charm dies when tiles turn into neon signs.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 113+ | 111+ |
oklch + color-mix drive the theming (2023 evergreens); the span geometry itself is original-grid and runs anywhere.