LayoutGrid

Grid

Flexible grid layout. Children separated by horizontal rules (---) become cells arranged in a responsive CSS grid. Supports explicit column spans, auto-fill mode, masonry, aspect ratio enforcement, and responsive collapse.

Basic usage

Equal columns created by separating content with horizontal rules.

{% grid %}
**First column.** This content sits in the first cell of the grid layout.

---

**Second column.** Grid cells are separated by horizontal rules in the Markdown source.

---

**Third column.** The grid automatically distributes available space across columns.
{% /grid %}
<section data-rune="grid">
  <div data-layout="grid">
    <div data-colspan="1" data-name="cell">
      <p>
        <strong marker="**">First column.</strong>
        This content sits in the first cell of the grid layout.
      </p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>
        <strong marker="**">Second column.</strong>
        Grid cells are separated by horizontal rules in the Markdown source.
      </p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>
        <strong marker="**">Third column.</strong>
        The grid automatically distributes available space across columns.
      </p>
    </div>
  </div>
</section>

First column. This content sits in the first cell of the grid layout.

Second column. Grid cells are separated by horizontal rules in the Markdown source.

Third column. The grid automatically distributes available space across columns.

<section class="rf-grid rf-grid--columns" data-mode="columns" data-rune="grid" data-density="full">
  <div data-layout="grid">
    <div data-colspan="1" data-name="cell">
      <p>
        <strong marker="**">First column.</strong>
         This content sits in the first cell of the grid layout.
      </p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>
        <strong marker="**">Second column.</strong>
         Grid cells are separated by horizontal rules in the Markdown source.
      </p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>
        <strong marker="**">Third column.</strong>
         The grid automatically distributes available space across columns.
      </p>
    </div>
  </div>
</section>

Unequal columns

Use the spans attribute to control column widths. Values are space-separated span sizes.

{% grid spans="2 1" %}
**Wide column.** This column spans two units of the grid, taking up twice the space of the narrow column beside it.

---

**Narrow column.** This column takes one unit.
{% /grid %}
<section data-rune="grid">
  <div data-layout="grid">
    <div data-colspan="2" data-name="cell">
      <p>
        <strong marker="**">Wide column.</strong>
        This column spans two units of the grid, taking up twice the space of the narrow column beside it.
      </p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>
        <strong marker="**">Narrow column.</strong>
        This column takes one unit.
      </p>
    </div>
  </div>
</section>

Wide column. This column spans two units of the grid, taking up twice the space of the narrow column beside it.

Narrow column. This column takes one unit.

<section class="rf-grid rf-grid--columns" data-mode="columns" data-rune="grid" data-density="full">
  <div data-layout="grid">
    <div data-colspan="2" data-name="cell">
      <p>
        <strong marker="**">Wide column.</strong>
         This column spans two units of the grid, taking up twice the space of the narrow column beside it.
      </p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>
        <strong marker="**">Narrow column.</strong>
         This column takes one unit.
      </p>
    </div>
  </div>
</section>

Gap

Control spacing between cells with the gap attribute.

{% grid gap="tight" %}
**Tight gap.** Less space between cells.

---

**Second cell.** The gap is smaller than default.

---

**Third cell.** Useful for compact layouts.
{% /grid %}
<section data-rune="grid" data-rune-fields="{&quot;gap&quot;:&quot;tight&quot;}">
  <div data-layout="grid">
    <div data-colspan="1" data-name="cell">
      <p>
        <strong marker="**">Tight gap.</strong>
        Less space between cells.
      </p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>
        <strong marker="**">Second cell.</strong>
        The gap is smaller than default.
      </p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>
        <strong marker="**">Third cell.</strong>
        Useful for compact layouts.
      </p>
    </div>
  </div>
</section>

Tight gap. Less space between cells.

Second cell. The gap is smaller than default.

Third cell. Useful for compact layouts.

<section class="rf-grid rf-grid--columns" data-mode="columns" data-gap="tight" data-rune="grid" data-density="full" style="--grid-gap: var(--rf-spacing-sm)">
  <div data-layout="grid">
    <div data-colspan="1" data-name="cell">
      <p>
        <strong marker="**">Tight gap.</strong>
         Less space between cells.
      </p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>
        <strong marker="**">Second cell.</strong>
         The gap is smaller than default.
      </p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>
        <strong marker="**">Third cell.</strong>
         Useful for compact layouts.
      </p>
    </div>
  </div>
</section>

Alignment

Vertically align cells within their row using the valign attribute. Useful when cells have different heights.

{% grid valign="center" spans="2 1" %}
**Tall cell.** This cell has more content and takes up more vertical space. The adjacent cell will be vertically centred relative to this one.

Additional paragraph to increase the height of this cell.

---

**Centred.** This shorter cell is vertically centred.
{% /grid %}
<section data-rune="grid" data-rune-fields="{&quot;valign&quot;:&quot;center&quot;}">
  <div data-layout="grid">
    <div data-colspan="2" data-name="cell">
      <p>
        <strong marker="**">Tall cell.</strong>
        This cell has more content and takes up more vertical space. The adjacent cell will be vertically centred relative to this one.
      </p>
      <p>Additional paragraph to increase the height of this cell.</p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>
        <strong marker="**">Centred.</strong>
        This shorter cell is vertically centred.
      </p>
    </div>
  </div>
</section>

Tall cell. This cell has more content and takes up more vertical space. The adjacent cell will be vertically centred relative to this one.

Additional paragraph to increase the height of this cell.

Centred. This shorter cell is vertically centred.

<section class="rf-grid rf-grid--columns" data-mode="columns" data-valign="center" data-rune="grid" data-density="full" style="--grid-valign: center">
  <div data-layout="grid">
    <div data-colspan="2" data-name="cell">
      <p>
        <strong marker="**">Tall cell.</strong>
         This cell has more content and takes up more vertical space. The adjacent cell will be vertically centred relative to this one.
      </p>
      <p>Additional paragraph to increase the height of this cell.</p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>
        <strong marker="**">Centred.</strong>
         This shorter cell is vertically centred.
      </p>
    </div>
  </div>
</section>

Responsive collapse

Force the grid to collapse to a single column at a given breakpoint.

{% grid collapse="md" %}
Sidebar content.

---

Main content.
{% /grid %}

Breakpoints: sm (640px), md (768px), lg (1024px), or never.

Auto mode

Responsive auto-fill layout. The grid creates as many columns as fit, each at least min wide. No explicit column count needed.

{% grid mode="auto" min="200px" %}
Card one.

---

Card two.

---

Card three.

---

Card four.
{% /grid %}
<section data-rune="grid" data-rune-fields="{&quot;mode&quot;:&quot;auto&quot;,&quot;min&quot;:&quot;200px&quot;}">
  <div data-layout="grid">
    <div data-colspan="1" data-name="cell">
      <p>Card one.</p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>Card two.</p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>Card three.</p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>Card four.</p>
    </div>
  </div>
</section>

Card one.

Card two.

Card three.

Card four.

<section class="rf-grid rf-grid--auto" data-mode="auto" data-min="200px" data-rune="grid" data-density="full" style="--grid-min: 200px">
  <div data-layout="grid">
    <div data-colspan="1" data-name="cell">
      <p>Card one.</p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>Card two.</p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>Card three.</p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>Card four.</p>
    </div>
  </div>
</section>

Masonry mode

Progressive enhancement for masonry-style layouts. Falls back to a standard grid in browsers that don't support CSS masonry.

{% grid mode="masonry" columns=3 %}
Short card.

---

A taller card with more content that takes up more vertical space.

---

Medium card.

---

Another short one.
{% /grid %}

Aspect ratio

Enforce a uniform aspect ratio on all grid cells. Content is cropped with object-fit: cover — ideal for image galleries.

{% grid mode="auto" min="200px" aspect="1/1" %}
![](https://picsum.photos/seed/grid1/400/400)

---

![](https://picsum.photos/seed/grid2/400/400)

---

![](https://picsum.photos/seed/grid3/400/400)

---

![](https://picsum.photos/seed/grid4/400/400)
{% /grid %}
<section data-rune="grid" data-rune-fields="{&quot;mode&quot;:&quot;auto&quot;,&quot;min&quot;:&quot;200px&quot;,&quot;aspect&quot;:&quot;1/1&quot;}">
  <div data-layout="grid">
    <div data-colspan="1" data-name="cell">
      <p>
        <img src="https://picsum.photos/seed/grid1/400/400" alt="">
      </p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>
        <img src="https://picsum.photos/seed/grid2/400/400" alt="">
      </p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>
        <img src="https://picsum.photos/seed/grid3/400/400" alt="">
      </p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>
        <img src="https://picsum.photos/seed/grid4/400/400" alt="">
      </p>
    </div>
  </div>
</section>

<section class="rf-grid rf-grid--auto" data-mode="auto" data-aspect="1/1" data-min="200px" data-rune="grid" data-density="full" style="--grid-min: 200px; --grid-aspect: 1/1">
  <div data-layout="grid">
    <div data-colspan="1" data-name="cell">
      <p>
        <img src="https://picsum.photos/seed/grid1/400/400" alt="" />
      </p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>
        <img src="https://picsum.photos/seed/grid2/400/400" alt="" />
      </p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>
        <img src="https://picsum.photos/seed/grid3/400/400" alt="" />
      </p>
    </div>
    <div data-colspan="1" data-name="cell">
      <p>
        <img src="https://picsum.photos/seed/grid4/400/400" alt="" />
      </p>
    </div>
  </div>
</section>

Stack order

Control the order of cells when the grid collapses to a single column on mobile. By default cells stack in source order; stack="reverse" places the last cell first.

{% grid spans="2 1" collapse="md" stack="reverse" %}
Main content (appears second on mobile).

---

Sidebar (appears first on mobile).
{% /grid %}

Attributes

AttributeTypeRequiredDescription
columnsnumberNumber of grid columns
rowsnumberNumber of grid rows
flow"row" | "column" | "dense" | "row dense" | "column dense"Direction items fill the grid
spansSpaceSeparatedListColumn span widths for each cell (space-separated)
ratiostringColumn width ratio (e.g. "2 1 1")
gap"none" | "tight" | "default" | "loose"Space between grid cells
valign"top" | "center" | "bottom" | "stretch" | "baseline"Vertical alignment of cell content
collapse"sm" | "md" | "lg" | "never"Breakpoint at which grid collapses to a single column
mode"columns" | "auto" | "masonry"Grid sizing: fixed columns, auto-fit, or masonry
minstringMinimum cell width for auto-fit mode
aspectstringAspect ratio for grid cells
stack"natural" | "reverse"Stacking order when collapsed
layoutSpaceSeparatedList

Universal attributes

bg

The background layer (SPEC-088): image, video, gradient, flat overlay wash and legibility scrim, in a single injected layer behind the rune's content.

AttributeTypeRequiredDescription
bgstringBackground preset applied to this block
bg-fromstringGradient start colour — a semantic token name (→ var(--rf-color-*))
bg-gradient"to-t" | "to-b" | "to-l" | "to-r" | "to-tr" | "to-br" | "to-bl" | "to-tl"Gradient direction (bounded named set)
bg-gradient-type"linear" | "radial" | "conic"Gradient type
bg-tostringGradient end colour — a semantic token name
bg-viastringOptional middle gradient stop — a semantic token name
scrim"top" | "bottom" | "left" | "right" | "none"Scrim direction (heaviest edge); presence turns the scrim on, "none" opts out of the default cover scrim
scrim-blur"none" | "sm" | "md" | "lg"Frost scrim blur amount
scrim-strength"sm" | "md" | "lg"Gradient scrim strength
scrim-tone"dark" | "light"Whether the scrim darkens (for light text) or lightens (for dark text)
scrim-type"gradient" | "frost"Scrim treatment: gradient (default) or frost (backdrop blur)
elevation

The chrome/depth ladder (SPEC-107). The skin maps each rung to a chrome bundle by attribute, so there is no BEM class.

AttributeTypeRequiredDescription
elevation"sunken" | "flush" | "flat" | "raised" | "floating" | "overlay" | "none" | "sm" | "md" | "lg"Surface depth on the SPEC-107 ladder (sunken→overlay); none/sm/md/lg are deprecated aliases
inset

Internal padding override.

AttributeTypeRequiredDescription
inset"flush" | "tight" | "default" | "loose" | "breathe"Inner padding of this block
motion

Scroll-reveal entrance (SPEC-105). The author declares the character, the theme owns the choreography, a behaviour owns the timing.

AttributeTypeRequiredDescription
reveal"none" | "fade" | "slide" | "scale" | "blur"Scroll-reveal entrance character (none|fade|slide|scale|blur); the theme owns the choreography
staggerbooleanCascade this block's items in as it reveals (no-op on single-child runes)
spacing

Block-level rhythm override.

AttributeTypeRequiredDescription
spacing"flush" | "tight" | "default" | "loose" | "breathe"Vertical spacing above and below this block
substrate

Generated pattern fills (SPEC-087). Markers only — the engine sets the attributes and cell/opacity custom properties, CSS draws the pattern.

AttributeTypeRequiredDescription
substrate"dots" | "grid" | "lines" | "cross" | "checker" | "none"Generated surface pattern
substrate-fill"inherit" | "inset"Surface fill the pattern sits on (full colour stays with tint)
substrate-opacity"sm" | "md" | "lg"Pattern ink strength
substrate-size"sm" | "md" | "lg"Pattern cell size
substrate-target"self" | "media"Which surface the pattern fills (overrides the rune/theme default)
tint

Per-rune colour override (SPEC-053): a named tint from the theme registry, with inline per-token overrides layered on top.

AttributeTypeRequiredDescription
tintstringColor tint preset applied to this block
tint-mode"auto" | "dark" | "light"Whether the tint adapts to auto, dark, or light mode
width

The track a block rune occupies.

AttributeTypeRequiredDescription
width"compact" | "narrow" | "content" | "wide" | "full"Maximum width constraint for this block
Not availableWhy
dropcap, readingthis rune declares no prose body
framethis rune declares neither a `frameTarget` nor a media section
prominencethis rune has no page-section header