All studies

2026-09 · Layout

Interactive bento grid

Seven tiles that declare the room they want, on a grid that clamps every span against its own column count. Each tile answers for the slot it lands in rather than for the modifier it was given — and opening one re-packs the board around it.

Default — four columns. Open a tile: it takes the lead size, the board re-packs around it, and the tile re-composes itself

  • Edition 04 · hand-knotted

    Nine colours that argue

    200 × 300 cm

    Gotland wool

    Knotted on a cotton warp over eleven weeks. The border is carried in two pitches so it reads as one band at a distance and as two up close.

  • Colourway

    Dyed in one bath, so nothing quite matches and nothing quite clashes.

    9 fields

    One bath

    Four of the nine show in a narrow slot; the rest arrive when there is room to read them as colour rather than as stripes.
    Lot
    04–B
    Fastness
    6 of 8
  • Weave

    Three pitches in one field: the coarse rows carry the pattern, the fine ones carry the ground.

    Coarse

    Medium

    Fine

    Pitch is set at the loom and cannot be changed once the warp is on.
    Warp
    Cotton, 12/6
    Pile
    11 mm
  • Knots

    Counted across the ground, not the border, which runs denser.

    160

    per dm²

    Ground
    160 / dm²
    Border
    190 / dm²
  • Motif

    The stepped diamond, repeated four times along the long edge.

    Repeat
    4 ×
    Origin
    Drawn, not traced
  • Size

    Cut to the loom width; the length is where the edition varies.

    200

    × 300 cm

    Width
    200 cm
    Length
    280–310 cm
  • Wool

    Undyed in the ground, so colour sits on grey, not white.

    100

    % Gotland

    Staple
    90 mm
    Spun
    2-ply, worsted

One tile's markup, three slots — identical HTML in a 1×1, a 2×1 and a 2×2. Nothing was added to it; the slot decided

  • Knots

    Counted across the ground, not the border, which runs denser.

    160

    per dm²

    Ground
    160 / dm²
    Border
    190 / dm²
  • Knots

    Counted across the ground, not the border, which runs denser.

    160

    per dm²

    Ground
    160 / dm²
    Border
    190 / dm²
  • Knots

    Counted across the ground, not the border, which runs denser.

    160

    per dm²

    Ground
    160 / dm²
    Border
    190 / dm²

The same board in a 26rem column — it reflows against its own width, not the window's, and each tile answers for the slot it now has

  • Edition 04 · hand-knotted

    Nine colours that argue

    200 × 300 cm

    Gotland wool

    Knotted on a cotton warp over eleven weeks. The border is carried in two pitches so it reads as one band at a distance and as two up close.

  • Colourway

    Dyed in one bath, so nothing quite matches and nothing quite clashes.

    9 fields

    One bath

    Four of the nine show in a narrow slot; the rest arrive when there is room to read them as colour rather than as stripes.
    Lot
    04–B
    Fastness
    6 of 8
  • Weave

    Three pitches in one field: the coarse rows carry the pattern, the fine ones carry the ground.

    Coarse

    Medium

    Fine

    Pitch is set at the loom and cannot be changed once the warp is on.
    Warp
    Cotton, 12/6
    Pile
    11 mm
  • Knots

    Counted across the ground, not the border, which runs denser.

    160

    per dm²

    Ground
    160 / dm²
    Border
    190 / dm²
  • Motif

    The stepped diamond, repeated four times along the long edge.

    Repeat
    4 ×
    Origin
    Drawn, not traced
  • Size

    Cut to the loom width; the length is where the edition varies.

    200

    × 300 cm

    Width
    200 cm
    Length
    280–310 cm
  • Wool

    Undyed in the ground, so colour sits on grey, not white.

    100

    % Gotland

    Staple
    90 mm
    Spun
    2-ply, worsted

The ladder, stepped — four columns to one, a rung every 600ms

  • Edition 04 · hand-knotted

    Nine colours that argue

    200 × 300 cm

    Gotland wool

    Knotted on a cotton warp over eleven weeks. The border is carried in two pitches so it reads as one band at a distance and as two up close.

  • Colourway

    Dyed in one bath, so nothing quite matches and nothing quite clashes.

    9 fields

    One bath

    Four of the nine show in a narrow slot; the rest arrive when there is room to read them as colour rather than as stripes.
    Lot
    04–B
    Fastness
    6 of 8
  • Weave

    Three pitches in one field: the coarse rows carry the pattern, the fine ones carry the ground.

    Coarse

    Medium

    Fine

    Pitch is set at the loom and cannot be changed once the warp is on.
    Warp
    Cotton, 12/6
    Pile
    11 mm
  • Knots

    Counted across the ground, not the border, which runs denser.

    160

    per dm²

    Ground
    160 / dm²
    Border
    190 / dm²
  • Motif

    The stepped diamond, repeated four times along the long edge.

    Repeat
    4 ×
    Origin
    Drawn, not traced
  • Size

    Cut to the loom width; the length is where the edition varies.

    200

    × 300 cm

    Width
    200 cm
    Length
    280–310 cm
  • Wool

    Undyed in the ground, so colour sits on grey, not white.

    100

    % Gotland

    Staple
    90 mm
    Spun
    2-ply, worsted

Decisions

Type
Layout

A board of seven tiles on a grid that is told how many columns it has and nothing else. Each tile declares the room it wants — two columns, two rows, both, neither — and every span clamps itself against whatever the board currently is, so the arrangement is a rule rather than a map of where things go. Each tile is also its own container, so what it shows is decided by the slot it landed in rather than by the modifier it was given; and opening one promotes it to the lead size, which re-packs the board and re-composes the tile in the same gesture.

  1. One number per breakpoint, and the spans clamp themselves.

    A tile carries --bento-grid-tile-cols and -rows; the board resolves grid-column: span min(var(--tile-cols), var(--cols)). The three container queries set --bento-grid-cols to 2, 3 and 4 and touch nothing else — no per-breakpoint span table, no grid-template-areas map to keep in step. Without the min() a one-column board grows a phantom track to fit a two-column tile: measured, grid-template-columns resolves to 134px 8px and the wide tiles sit 16px proud of the narrow ones. Rows are deliberately not clamped, so the tile that led at four columns still leads at 320px.

  2. Twelve cells, because twelve divides by four, three, two and one.

    One 2×2, one 2×1, one 1×2 and four 1×1. Measured at every rung: the board resolves to 4×3, 3×4, 2×6 and 1×9 and fills its rectangle exactly, with no ragged bottom row and no sideways scroll at 320px. Opening a tile breaks that on purpose — it is the reader asking for room, not the layout going wrong.

  3. A tile answers for its slot, not for its span.

    container: bento-grid-tile / size on every tile, and two queries decide the composition. Measured as tile content width: 15.31rem at four columns, 13.29 at three, 13.50 at two — and 20.38rem at one, because there is only one of it. So a unit tile is wider at one column than at four and takes the fuller composition there, which is the answer a span-based modifier cannot give: 1×1 is a share of a board, not a size. The threshold is 16rem, between the widest compact case and the narrowest wide one. Padding lives on the panel inside the tile rather than on the tile, so the box a container query measures stays equal to the slot the grid handed over.

  4. Opening a tile is the whole interaction, and it is a <details>.

    The tile is an <li>; the <details> inside owns the open state and the <li> reads it back with :has(), promoting the tile to 2×2. The board re-packs because the flow is dense, and the tile re-composes because it has just become large enough to answer both slot queries. One gesture, both mechanisms, and no script: disclosure is what that element already is, keyboard, screen reader and find-in-page included. A grid cannot tween a re-pack — track counts are not interpolable and neither is a tile's placement — so what softens it is a dip: the promoted tile settles in over 300ms, the revealed detail arrives rather than appears, and under --cycling every tile dips and returns on the ladder's own clock. Genuinely tweening the re-pack needs the View Transitions API, and therefore script.

  5. Flat, unlit colour, and the adjacencies are meant to disagree.

    Nine fields, no gradient and no tint on any of them; the lift is a 5px offset block of the same ink rather than a blur, because a soft shadow would put light into a register that has none. The cream sits fifth in the colourway rather than ninth — last, it was too close to the chalk the tile is made of and the strip read as though it had been cut short.

The row height is measured rather than chosen. A tile's face gets the row less 48px, and the tallest thing any face has to hold is the motif tile's 117px; 11rem is the first round figure that clears it. Every earlier value clipped something, and quietly — at 7rem six tiles, at 9rem four, by 2 to 37 pixels each. A figure set at line-height: 1 overflows its own line box by about 0.15em, which with an auto margin pinning it to the bottom lands exactly on the clip. And an aside gets two lines: about sixty characters at the narrowest width one appears at.

Three things cost a measurement each. An unnamed @container asks the nearest ancestor container, so once the tiles became containers a board-level rule was silently being answered by a 201px tile and every tile in a four-column board took the wide composition; two containers in one component is two names. And --bento-grid-cols is registered with @property as an <integer> — without that it cannot be animated at all, and the ladder above jumps straight to its last rung. And container-type: size applies size containment, so a tile is laid out as though it had no contents and grid-auto-rows: minmax(var(--row), auto) can never grow a row to fit an opened tile — height-based slot queries and rows that grow to their content are mutually exclusive.

Inspiration
Tal R — the colour register only. Flat unlit fields, forms drawn rather than traced, and colours set next to each other that are not trying to get along. None of his compositions are here and the arrangement is this study's own; what was taken is the refusal to shade anything.

notes.md — the full record, including what was tried and dropped