Grids and bentos

Bento grids, feature grids and the viewer grid view, with their columns, gaps and spans adjustable and the line law holding at every junction.

Updated

A bento is a grid of cells whose seams are hairlines. Neighboring cells touch, so every seam is shared by two of them, and the grammar gives it one owner: the row. The shell's BentoRow draws every seam as a 1px gap over a hairline ground, and its cells have no border props at all, so a doubled line cannot be written with it.

Seams

Seams picks who draws the lines. Each setting opens in the shape its code was written for, and Columns, Cells and Gap then change the grid; a click on a cell widens it or makes it taller. The plate audits itself the way scripts/lint/lines.mjs audits a page: it reads every border from computed CSS, counts the strips of a row's ground as strokes, and marks each fault in the accent.

Row Ground mounts the live BentoRow and BentoCell. Their 1px gap is the seam, so a wider gap shows the ground as a bar, and a last row that does not fill shows it as a block. Below 1024 pixels of viewport the row stacks its cells, as the shipped pages do. Row Rules is the toolchain's .tc-row, which draws a rule on the left of every cell after the first: it holds one line of cells, and once it wraps, the first cell of the next line draws a rule against the column's line and nothing separates the two lines. Cell Borders is FeatureBento's rule set: the grid draws its own frame, and each cell its right and bottom edges, with nth-child exclusions written for eight cells at 2, 3 or 4 columns. Any other count, column number or span leaves a cell edge on the frame. The plate keeps FeatureBento's desktop rules at every width. Cell Frames gives every cell its own box: two frames under 4 pixels apart double, and further apart the cells read as separate bordered cards, which the landing grammar does not use.

FaultWhat the audit marks
Doubled linetwo strokes from two owners 1 to 4 pixels apart along most of their run
Coincident pairtwo strokes from two owners on one row
Missing seamtwo touching cells with no stroke between them
Seam wider than 1pxa strip of the row's ground wider than a hairline
Ground showing as a blockgrid area no cell covers, painted in the ground's hairline ink

Import and props

import { BentoCell, BentoRow } from '@/components/shell/Bento';

<BentoRow cols='minmax(0, 1.32fr) minmax(0, 1fr)'>
  <BentoCell framed={false} sub='One sentence about the cell.' title='Code'>
    {copy}
  </BentoCell>
  <BentoCell cell='is-tall'>{figure}</BentoCell>
</BentoRow>;
ExportPropTypeDefaultWhat it does
BentoRowcolsa grid-template-columns valuetwo equal columnsThe row's template; one column at 1023px and under.
eqHeadsbooleantrueGives every cell head in the row one height, so the first line under each head lands at one y.
headHa length112pxThe aligned head height.
classNamestringnoneThe page's variant classes, such as is-lead.
BentoCellframedbooleantrueMounts the nested .tc-card and carries .is-framed, so the row's ground shows as a 1px strip around the card.
cellstringnoneVariant classes on .tc-cell, such as is-tall.
title, sub, headnodenoneThe cell head: an h3, a paragraph, and anything under them.
styleCSSPropertiesnoneSpans and other per-cell layout.
RailsnoneDraws the column's pair for a full-bleed band outside a wrapper; a band inside a wrapper renders none.

The row and the cell write the class grammar (.tc-row, .tc-cell, .tc-card) that each page's stylesheet styles, so a cell's ground, padding and card corner come from the page.

Row templates

The toolchain engine names its rows by their templates. Every one stacks to a single column at 1023 pixels and under, the same width BentoRow folds at.

Classgrid-template-columnsWhere the toolchain uses it
is-lead1.32fr 1fra wide first cell beside a narrow one
is-split1.04fr 0.96frcopy beside a framed artifact
is-three1.13fr 0.93fr 0.94frthree diagrams of different widths
is-wide-left1.58fr 1fra wide figure beside a short list
is-wide-right1fr 1.34frthe mirror of the above
is-even1fr 1frtwo equal cells
is-tilt0.82fr 1fra stat list beside a wider field
is-grammar0.93fr 1frthe two grammar diagrams
is-one1frone cell across the column

A flat cell pads clamp(26px, 3vw, 36px) over and clamp(30px, 3.4vw, 40px) under, with the gutter at each side. A framed cell has no padding: its card sits on the hairline ground with a 1px strip around it, and the card pads itself by clamp(17px, 1.9vw, 21px), 36px over and 38px under at 720px and under. Where a framed row meets the column's line, the strip on that side is dropped, so no strip runs beside a line.

The feature grid

FeatureBento sets eight modules, each a heading, a sentence and a line drawing, at 4, 3 or 2 columns: two at 1023px and under, and one at 560px and under. Its cells pad 26px, 24px and 30px with a 268px floor. It draws its seams with the cell borders the plate's Cell Borders setting copies, in two inks: the grid's frame at --gtb-line and the cell seams at --gtb-line-2. Its stylesheet is skinned through --gtb-* custom properties, and a pointer-tracked wash lights each cell unless spotlight is off. No page on this site mounts it today.

import FeatureBento from '@/components/shared/FeatureBento';

<FeatureBento columns={3} heading='The full stack for localization' spotlight={false} />;

The grid view

The shell's grid mode lays a route's items out as captured frames over the stage. The plate mounts the live GridView with the ten /brand sections in it and reads the columns the browser resolved, so the readout is the grid's own answer.

The grid fills its columns with repeat(auto-fill, minmax(300px, 1fr)) and a 26px gap, inside 22px of padding over, 34px at each side and 56px under. A section label spans every column over a --pt-hair rule, and the first label has none. Each item is a 26px number column beside a 16:9 frame with a --pt-edge border on the card corner, so every tile owns its frame and no two tiles share an edge. The active tile draws its frame in ink with a 1px ink outline 2px outside it.

WindowStageColumns
1720, list closed1720px5 at 308.8px
1440, list in outline density1232px3 at 369.3px
1440, list in thumbnail density1184px3 at 353.3px
1440, list closed1440px4 at 322.5px
1280, list in outline density1072px3 at 316px
1024, list in outline density816px2 at 359px
390, a phone390px1 at 318px

The grid is a scroll region, so its 4px scrollbar gutter comes off the width before the padding does. Under a 372px stage the content is narrower than one 300px column, and the column keeps its 300px, so the frames run past the right edge.

import { GridView } from '@/components/viewer/GridView';

<GridView>
  <ThumbList density='thumbs' sections={sections} thumb='shot' />
</GridView>;

GridView takes its children and an accessible label, which reads Every item as a grid when it is left out. ViewerShell renders it over the stage while the mode is grid, with a ThumbList of the route's sections inside.

On this page