Skip to content

Bento layout

2 min read · Updated 9 Oct 2026

A bento page packs tiles of different sizes and kinds into one board, with no single reading order. Reflow rules, grid traps and the shared tokens are in Layout and responsiveness.

Bento layout: Different tile sizes reflect different questions.

Illustrative example with synthetic data. Different tile sizes reflect different questions.

Use the overview defaults for page sizing. The thresholds below describe this example’s geometry; recalculate them for your objects.

Use it for overview pages that answer several unrelated questions at a glance, where the tiles differ in importance: a store overview, a fitness tracker, a risk snapshot.

The page is mainly Use instead
Values readers compare across tiles Card grid or the KPI-led grid; tiles of different sizes draw the same value at different scales
Headline numbers explained by charts KPI-led
One item at a time, picked from a list Master–detail
  1. Does one tile hold a list or feed that readers scan beside every other tile? Use side column.
  2. Otherwise, use tiles.
Variant Layout Use cases
Tiles Tiles of mixed sizes in a four-column grid Store overview: revenue by day as the large tile, orders, sales by channel, top products
Side column Tiles on the left, one tile down the full height on the right Support queue: volume by hour, SLA and backlog, with the live queue down the side
  • Write one grid-template-areas map per width tier.
  • Put the tiles in the DOM most important first. The narrowest tier is one column in DOM order.
  • Give the most important tile the largest area, and keep it largest when folded.
  • Keep to six or seven tiles; each extra tile must be placed in every map.
:root {
/* The narrowest one-column tile that keeps a donut and its legend readable. */
--tile-w: 280px;
}
.board {
display: grid;
gap: var(--gap);
}
.tile-a {
grid-area: a;
}
.tile-b {
grid-area: b;
}
.tile-c {
grid-area: c;
}
.tile-d {
grid-area: d;
}
.tile-e {
grid-area: e;
}
.tile-f {
grid-area: f;
}

.board takes its fixed height from Bands above the board. Width sums include 15px for a classic scrollbar.

┌─────────────┬──────┬──────┐
│ │ B │ C │
│ A ├──────┼──────┤
│ │ │ │
├─────────────┤ D │ E │
│ F │ │ │
└─────────────┴──────┴──────┘

Put small tiles, such as KPIs, in the one-row areas and charts that need height in the two-row areas.

.board--bento {
grid-template-columns: repeat(4, minmax(0, 1fr));
grid-template-rows: repeat(3, minmax(0, 1fr));
grid-template-areas:
'a a b c'
'a a d e'
'f f d e';
}
/* Width: 2 × 16 + 4 × 280 + 3 × 16 + 15 = 1215
Height: 64 + 2 × 16 + 3 × 252 + 2 × 16 = 884 */
@media (width < 1215px), (height < 884px) {
.board--bento {
height: auto;
grid-template-rows: repeat(3, minmax(var(--chart-min), auto));
}
}
@media (width < 1215px) {
/* A keeps the height of two rows and a gap. */
.board--bento {
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-template-rows:
minmax(calc(2 * var(--chart-min) + var(--gap)), auto)
repeat(3, minmax(var(--chart-min), auto));
grid-template-areas:
'a a'
'b c'
'd e'
'f f';
}
}
/* 2 × 16 + 2 × 280 + 16 + 15 = 623 */
@media (width < 623px) {
.board--bento {
grid-template-columns: minmax(0, 1fr);
grid-template-rows: minmax(calc(2 * var(--chart-min) + var(--gap)), auto);
grid-auto-rows: minmax(var(--chart-min), auto);
grid-template-areas: 'a' 'b' 'c' 'd' 'e' 'f';
}
}
Width Map Height
1215px and wider 4 columns, 3 rows Fits the window from 884px tall
623–1214px A, then B and C, D and E, then F Scrolls
Under 623px 1 column in DOM order Scrolls
┌─────────────┬──────┬──────┐
│ │ B │ │
│ A ├──────┤ │
│ │ C │ S │
├─────────────┴──────┤ │
│ D │ │
└────────────────────┴──────┘
.board--side {
grid-template-columns: repeat(4, minmax(0, 1fr));
grid-template-rows: repeat(3, minmax(0, 1fr));
grid-template-areas:
'a a b s'
'a a c s'
'd d d s';
}
.tile-s {
grid-area: s;
overflow: auto;
}
/* Same sums as the tiles variant: 1215 wide, 884 tall.
Fixed rows, not minmax(…, auto), so a long list scrolls inside its tile
instead of stretching the rows. */
@media (width < 1215px), (height < 884px) {
.board--side {
height: auto;
grid-template-rows: repeat(3, var(--chart-min));
}
}
@media (width < 1215px) {
.board--side {
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-template-rows:
calc(2 * var(--chart-min) + var(--gap))
var(--chart-min)
var(--chart-min)
calc(2 * var(--chart-min) + var(--gap));
grid-template-areas:
'a a'
'b c'
'd d'
's s';
}
}
@media (width < 623px) {
.board--side {
grid-template-columns: minmax(0, 1fr);
grid-template-rows:
calc(2 * var(--chart-min) + var(--gap))
repeat(3, var(--chart-min))
calc(2 * var(--chart-min) + var(--gap));
grid-template-areas: 'a' 'b' 'c' 'd' 's';
}
}

Show the list’s full count in its heading, since the tile shows only part of it; see A panel’s height must not follow its data.

Width Map Height
1215px and wider Tiles in the left three columns, S down the right Fits the window from 884px tall
623–1214px A, then B and C, D, then S Scrolls
Under 623px 1 column in DOM order Scrolls