Skip to content

PRIMITIVE

Rule stack

A vertical list whose separators are the background showing through a one-pixel gap. The source design has seven of these, every one spelled out by hand.

Key and value

The commonest row in the system: a tracked key, a value hard right, both on one baseline. Run out of room and they wrap rather than collide.

LABELS Above, mono, .16em
REQUIRED Accent asterisk
HINTS Below, 11px
HTML
<div class="rule-stack rule-stack-on-sunk" style="width:100%">
  <div class="rule-row rule-row-spec">
    <span class="rule-row-key">LABELS</span>
    <span class="rule-row-value">Above, mono, .16em</span>
  </div>
  <div class="rule-row rule-row-spec">
    <span class="rule-row-key">REQUIRED</span>
    <span class="rule-row-value">Accent asterisk</span>
  </div>
  <div class="rule-row rule-row-spec">
    <span class="rule-row-key">HINTS</span>
    <span class="rule-row-value">Below, 11px</span>
  </div>
</div>

One column line for the whole stack

Add .rule-stack-aligned and every value starts at the same x β€” the column is as wide as the widest key, which is a width nobody had to pick.

BASED IN KrakΓ³w, and remote since 2019
AVAILABILITY Two days a week from March
RATE On request, and it depends on the work
HTML
<div class="rule-stack rule-stack-aligned" style="width:100%">
  <div class="rule-row rule-row-spec">
    <span class="rule-row-key">BASED IN</span>
    <span class="rule-row-value">KrakΓ³w, and remote since 2019</span>
  </div>
  <div class="rule-row rule-row-spec">
    <span class="rule-row-key">AVAILABILITY</span>
    <span class="rule-row-value">Two days a week from March</span>
  </div>
  <div class="rule-row rule-row-spec">
    <span class="rule-row-key">RATE</span>
    <span class="rule-row-value">On request, and it depends on the work</span>
  </div>
</div>

Rows that are links

The whole row is the target. Tab to one and you will see the ring drawn inside it β€” an outset ring on a flush row gets clipped by its neighbours and comes out as two stubs down the sides.

HTML
<div class="rule-stack rule-stack-on-sunk" style="width:100%">
  <a href="#" class="rule-row rule-row-spec">
    <span class="rule-row-key">EMAIL</span>
    <span class="rule-row-value">[email protected]</span>
  </a>
  <a href="#" class="rule-row rule-row-spec">
    <span class="rule-row-key">GITHUB</span>
    <span class="rule-row-value">@yourhandle</span>
  </a>
</div>

The same list with the box taken off

rule-stack-open draws real borders instead of letting a painted container show through a gap β€” take the fill away and the paint-through trick has nothing left to show. Reach for it when the list IS the section rather than a panel inside it: a column of questions, a set of notes beside a heading. It is asking for --ds-pad-row-lg here, because with no frame around them the space is the only thing keeping one row apart from the next.

Is it really free?

Yes, for personal use, with no trial timer and no locked features. There is no account to make and nothing to enter a card into.

Does it change anything on my machine?

No. It runs from the file you downloaded, and nothing is written outside it apart from your own saved work.

Do I need to be online?

Only to download it. After that there is never a connection to make, and none is opened on its own.

HTML
<!-- The box taken off. Lines between the rows and one under the last, drawn on
     whatever ground the list is standing on. A question and its answer per row,
     which is the shape this was written for. -->
<div class="rule-stack rule-stack-open" style="--rule-row-pad: var(--ds-pad-row-lg)">
  <div class="rule-row flow flow-tight">
    <h3 class="feature-cell-title">Is it really free?</h3>
    <p class="feature-cell-body">Yes, for personal use, with no trial timer and no locked features. There is no account to make and nothing to enter a card into.</p>
  </div>
  <div class="rule-row flow flow-tight">
    <h3 class="feature-cell-title">Does it change anything on my machine?</h3>
    <p class="feature-cell-body">No. It runs from the file you downloaded, and nothing is written outside it apart from your own saved work.</p>
  </div>
  <div class="rule-row flow flow-tight">
    <h3 class="feature-cell-title">Do I need to be online?</h3>
    <p class="feature-cell-body">Only to download it. After that there is never a connection to make, and none is opened on its own.</p>
  </div>
</div>

Three columns, and a row that ends in a sentence

--rule-columns is the aligned stack's track list, and two columns is only its default. A release row is version, date, and what changed β€” the rows needed nothing at all, because subgrid already spans every column there is. The third cell is a rule-row-note: a SENTENCE, in the body face, where a key and a value are both label faces you scan rather than read. Same distinction section-head draws between its note and its lede.

v0.9.0 12 Aug 2026

Vector pane treatments: hatched, solid colour and paper, with per-line colour and width.

v0.8.0 3 Jun 2026

Free panel layouts. Dock, float or close anything, and keep the arrangement between sessions.

v0.7.0 19 Mar 2026

Image import, palette library, and per-layer Z offset.

HTML
<!-- Three columns rather than two. `--rule-columns` is the track list, and the
     rows needed nothing: `subgrid` already spans every column there is.

     The third cell is a `rule-row-note` β€” a SENTENCE, in the body face, where a
     key and a value are both label faces you scan rather than read. -->
<div class="rule-stack rule-stack-open rule-stack-aligned" style="--rule-columns: auto auto 1fr; width:100%">
  <div class="rule-row">
    <span class="rule-row-value">v0.9.0</span>
    <span class="rule-row-key">12 Aug 2026</span>
    <p class="rule-row-note">Vector pane treatments: hatched, solid colour and paper, with per-line colour and width.</p>
  </div>
  <div class="rule-row">
    <span class="rule-row-value">v0.8.0</span>
    <span class="rule-row-key">3 Jun 2026</span>
    <p class="rule-row-note">Free panel layouts. Dock, float or close anything, and keep the arrangement between sessions.</p>
  </div>
  <div class="rule-row">
    <span class="rule-row-value">v0.7.0</span>
    <span class="rule-row-key">19 Mar 2026</span>
    <p class="rule-row-note">Image import, palette library, and per-layer Z offset.</p>
  </div>
</div>

Reference

Read from primitives/rule-stack.css when this page was built, so it cannot fall behind the stylesheet.

CLASSES.rule-stack.rule-row.rule-stack-on-sunk.rule-stack-open.rule-row-spec.rule-stack-aligned.rule-row-value.rule-row-key.rule-row-note
SETTINGSSet the first on one element; a theme sets the second for all of them.--rule-row-pad β€” falls back to --ds-pad-row
READS--ds-focus-ring-inset--ds-focus-width--ds-font-body--ds-font-display--ds-gap-loose--ds-gap-tight--ds-ground--ds-hairline--ds-ink--ds-ink-faint--ds-ink-soft--ds-motion-fast--ds-radius--ds-raised--ds-rule--ds-sunk--ds-text-meta--ds-text-small--ds-text-ui--ds-track-key

Notes

WHY NOT BORDER-BOTTOM AND A LAST-CHILD RULE

Because this shape gives a closed outer frame for free, never double-draws between rows, and survives rows being reordered, filtered or conditionally rendered β€” where a :last-child rule ends up pointing at whichever element happens to be last today.

One thing to get right: a row must set a background, and it must be the surface the stack sits on. Use .rule-stack-on-sunk inside a sunk section. Getting this wrong is the most common misuse β€” the list reads as inset when it should be flush.

HOW THE ALIGNED STACK LINES UP

Subgrid, and it is the only thing that does this. A row has to stay a real box β€” it paints a surface, carries padding, lifts on hover and draws its focus ring inside itself β€” so it cannot be display: contents and hand its children up to the stack. Subgrid lets it keep all of that and still put its children on the stack’s column lines. The first column is auto: as wide as the widest key, which is a width nobody had to pick, and it gives ground towards min-content rather than pushing anything out of the box.

One trap, and you will not guess it from the CSS. A row that declares container-type cannot be a subgrid at all β€” containment forces an independent formatting context, so subgrid quietly computes to none. We watched the stack report four columns of 0px 0px 422px 0px with every rule correct in the build. If your row shape is a container, hand the container to the stack; the ledger does exactly that.