SPEC-085
ID:SPEC-085Status:draft

Bento as a composition substrate

bento should be a foundational, versatile rune — driving everything from marketing magazine sections to dashboards. Today it is heading-only, a cell has no real structure (only an icon is recognized as a visual; everything else is one undifferentiated body), its sizing vocabulary overlaps confusingly, and it is modelled as a page-section. This spec gives bento a solid foundation by converging the cell onto card's zone contract, a precise sizing model, and two clean authoring front doors — and by reducing bento to a pure grid primitive.

  • draft 1
  • ready 1
  • done 4
Status flow: 1 draft, 1 ready, 4 done as of Jun 11 (dayly buckets)0246draft: 5draft: 5draft: 5draft: 2done: 3draft: 2done: 3draft: 1ready: 1done: 3draft: 1ready: 1done: 4Jun 6Jun 11
Implemented by 6
Decisions 2
Related 9
Branches 2
History 5
  1. 6839d7f
    Created (draft)by bjornolofandersson
  2. c99efd9
    Content editedby Claude
    plan: author-controlled bento collapse + defer responsive per-cell spans
  3. ae814c3
    Content editedby Claude
    plan: consolidate the bento redesign into SPEC-085 + WORK-345/347/348
  4. dc5b2d7
    Content editedby Claude
    plan: capture showcase-bleed + per-cell tint as bento signature composit
  5. 9e6d3e8
    Content editedby Claude
    plan: expand bento into a composition substrate (SPEC-085)

The model: a bento cell is a card in a grid track

A bento-cell adopts card's structure rather than inventing its own:

  • Same zonesdata-section="media" / body / footer, split from cell content by the shared --- convention (SPEC-084 media-zone contract).
  • Same media placement — the existing data-media-position vocabulary (top | bottom | start | end), reused verbatim from card.
  • Same guest styling — the name-agnostic media-zone selector (WORK-339) applies unchanged, so any visual rune (chart, map, gallery, …) sits cleanly.
  • Same link affordance — an optional href makes the whole cell a link.

The cell adds only what is genuinely bento's: grid placement and the heading sugar. The payoff is enormous reuse — one zone model, one CSS dimension surface, one media contract — and a cell that is already composition-ready.

Bento is a grid primitive, not a page-section

Bento is only a grid of cells; it does not carry page-section chrome (eyebrow / title / blurb). The current sections + pageSectionAutoLabel preamble is removed — it forced a bento title to be an # h1 (page-title level inside a section) and consumed the top heading instead of letting it size a cell.

A titled bento is a composition: wrap it in a page-section rune (feature / hero / a section) or put a heading above it. Section chrome belongs to the section runes; bento supplies the grid. Bento keeps its universal block dimensions (width / spacing / inset) — those are not tied to the preamble — it just stops being a contentSection.

Consequence: with no preamble eating the top heading, every heading is a cell.

Sizing model

One grid, one vocabulary. The grid defaults to 6 columns (highly divisible — halves 3+3, thirds 2+2+2, 1+2+3, 4+2), the same for both authoring modes, author-overridable via columns.

  • size — a named preset, expressed as a proportion of the column count so it holds its ratio at any columns value:

    sizeproportion@ 6 colstiles with
    small2 × 1small+small+small, small+medium+?
    medium½3 × 1medium+medium
    large⅔ × 2 rows4 × 2large+small
    fullall6 × 1

    (The widths 2/3/4/6 all tile cleanly into 6.)

  • cols / rows — precise per-axis grid spans (grid-column: span cols; grid-row: span rows). Fully expressive (any W×H). Override size per-axis when both are present (size="large" rows="3" → 4 cols × 3 rows).

The old span cell attribute and the parent sizing="span" mode are removedspan is subsumed by cols, and the per-cell column control it provided now lives in cols/rows. (No content uses explicit cells today, so this is a free cleanup, not a migration.)

Tiered heading → size (sugar)

In the heading-sugar path, the input heading level controls tile size only. The base level is auto-detected (the shallowest heading present), and relative depth maps to a size preset:

heading (relative)size preset@ 6 cols
base (e.g. h2)large4 × 2
base + 1 (h3)medium3 × 1
base + 2 and deeper (h4–h6)small2 × 1

Three coarse tiers are deliberate — heading depth is a blunt signal; finer or taller layouts are what explicit cols/rows cells are for. (Differentiating deep headings by row span is explicitly rejected: rows = height = prominence, so it would invert the hierarchy, and small is already at the 1-row floor.)

Titles are uniform, not scaled by input level

A cell title renders as a real heading at one uniform, auto-detected outline level (cells are siblings in the grid), with uniform visual size via the headline zone typography — never the global h2/h3/h4 type scale. The input heading level is consumed purely for sizing and is not reflected in the rendered title's level or size. (Today the title is emitted as a bare <span>, invisible to the document outline / TOC; promoting it to a uniform heading is an accessibility upgrade, and it falls out of the card-zone headline for free.)

Two authoring front doors (no mixing)

A grid is authored one of two ways; they do not mix within a single bento:

  1. Heading sugar (default). Each heading becomes a cell; its level sets the tile size (tiered, above), its text becomes the cell title. Ergonomic, low-syntax — marketing and magazine layouts.
  2. Explicit cells. A bento whose children are {% bento-cell %} tags uses them directly, short-circuiting heading conversion. Full control per tile: cols/rows (or size), media-position, href, and a ----split media zone. This is the dashboard path.

Detection: if the grid contains explicit bento-cell tags, it is explicit-mode; otherwise headings are converted. (Mixing the two in one grid is out of scope.)

Media placement

media-position is author-controllable per cell via the shared data-media-position attribute (top | bottom | start | end; start/end place the media beside the body for wide cells). The default is derived from cell size — small cells stack media on top; large/full cells place it prominently / beside — and the explicit attribute overrides.

Responsive collapse

Collapse is author-controlled at the grid level, mirroring split: collapse="sm | md | lg | never" sets the breakpoint at which the grid drops to a single stacked column (sensible default), replacing the current hard-coded !important collapse. Between full grid and stacked, the column count steps down progressively and every span auto-caps via min(span, current-columns), so wide cells degrade gracefully at each step with no per-cell config — and a tied row height is rejected precisely because it explodes vertically as columns reduce.

Per-cell breakpoint control is intentionally not a per-cell collapse trigger (cells collapsing at different breakpoints go ragged); the coherent primitive, deferred to WORK-354, is responsive spans (cols="4 lg:2 sm:full").

Signature compositions (drive the design + the docs)

Two compositions define the bento "feel" and are first-class demo targets — both already have partial infrastructure:

  • showcase bleed in a cell. The cell media zone is a clipping container (overflow: hidden), so a showcase guest (wrapping an image/mockup/sandbox) can bleed/offset to show a partial, peeking view — the classic bento screenshot-in-the-corner look. showcase already declares a bento-cell → in-bento-cell context modifier, and showcase.css already keeps the bleed clipped inside bento cells (vs overflow: visible elsewhere); this just needs the cell's clipping media zone (WORK-345) and end-to-end verification.
  • tint per cell. A {% tint %} first child (or tint-mode attribute) gives an individual cell its own background + text colour, so a grid can be multi-coloured. tint is parent: '*' and paints via zero-specificity :where(), so it works provided the cell does not hard-set its own background — the cell must defer its background to the tint-bridged token.

These are requirements on the cell structure, not just demos: the media zone clips, and the cell background is tint-deferrable.

Non-goals

  • Mixing heading-sugar and explicit cells in a single grid.
  • Literal card nesting inside cells — the cell adopts the same contract (zones / media-position / selector), it does not embed a card rune.
  • Page-section chrome on bento itself (use composition for a titled section).
  • Capability-token composition (SPEC-084 future layer).

Acceptance Criteria

  • Bento is no longer a page-section: the sections preamble (eyebrow/title/blurb) is removed; bento keeps width/spacing/inset block dimensions. A titled bento is documented as a composition (wrap in feature/section).
  • A bento-cell exposes card's zone contract: data-section="media"/body/footer via ---, data-media-position, optional href; the WORK-339 media-zone selector styles guests in a cell unchanged.
  • Sizing: columns defaults to 6 for both modes; size presets resolve as proportions of the column count; precise cols/rows per-axis spans are supported and override size. The span attribute and sizing="span" mode are removed.
  • Tiered sugar maps auto-detected relative heading depth → size preset (base=large 4×2, +1=medium 3×1, +2+=small 2×1); the input heading level controls tile size only.
  • Cell titles render as a uniform, auto-detected heading level with uniform visual size (not a <span>, not the input level) — contributing to the document outline.
  • Explicit {% bento-cell %} authoring is first-class and short-circuits heading conversion; heading sugar is unchanged when no explicit cells are present.
  • The icon-as-visual path and the doc-vs-code image/emoji gap are reconciled under the new zone model; the media zone clips and the cell background is tint-deferrable.
  • Dashboards and marketing sections are both demonstrable (compositions docs, WORK-346).

References

  • plugins/marketing/src/tags/bento.ts, plugins/marketing/src/config.ts
  • card zone model: packages/runes/src/config.ts (Card), packages/lumina/styles/runes/card.css, split.css (data-media-position)
  • Media-zone contract: SPEC-084
  • Realised by WORK-345, WORK-347, WORK-348