CSS & layout

Gap decorations: column-rule finally leaves multi-column

Gap decorations: column-rule finally leaves multi-column

You wanted a vertical line between two flex items and reached for column-rule. It did nothing. Because until now, column-rule only worked inside a multi-column layout. For grid and flex you either put a border on every child and hoped the last one behaved, or you filled the gutter with a pseudo-element and prayed the tracks did not resize.

That workaround is done. In a piece dated August 3 on CSS-Tricks, Sam Davis Omekarajr writes up CSS gap decorations, now available in Chrome and Edge starting with version 149, plus the other Chromium-based browsers on the same version. The property you already know grew up. And it brought a partner.

column-rule crosses over, row-rule joins

column-rule still paints a line in the column-axis gap. What changed is where it applies. Multi-column, grid, flex — same property, one syntax. row-rule handles the other axis. A grid with gap: 3rem and both properties set draws a cross-hatch of lines through the gutters, no children touched.

The other thing to keep in your head: row-rule and column-rule accept a list. One rule per axis is the default. You can stack or repeat.

body {
  display: grid;
  gap: 4rem;
  margin: 2rem;
  row-rule:
    1rem solid #efefef,
    repeat(2, 2px solid #efefef);
}

That is one thick rule plus two thin ones, painted into every row-axis gap the grid has. repeat(auto, ...) is also accepted for a fill.

The rename that matters: outset became inset

If you followed this feature during origin trial, one thing changed. row-rule-outset and column-rule-outset have been renamed to row-rule-inset and column-rule-inset, and the semantics flipped with the name: values now inset the rule inward from the decoration edges. So the shorthand you would ship today looks like:

.home {
  display: flex;
  flex-wrap: wrap;
  gap: 3rem;
  rule: 2px solid #999;
  column-rule-inset: overlap-join;
}

rule is a new shorthand that sets both axes at once. A nice thing to have when the ruling is symmetric.

Independent control over caps, junctions, and ends

The interesting layer sits under the inset shorthand. Both axes now expose four longhands each:

  • column-rule-inset-cap and row-rule-inset-cap
  • column-rule-inset-junction and row-rule-inset-junction
  • column-rule-inset-start and row-rule-inset-start
  • column-rule-inset-end and row-rule-inset-end

Caps are the ends of a rule that hits an outer edge. Junctions are where a row rule and a column rule cross. Start and end pin the two individual endpoints. Each can be inset independently, which is the whole reason the longhand set exists: a clean cross-hatch where row lines stop short of column lines needs different values at the junction than at the caps.

Example — a footer link grid with a hairline horizontal between rows, a heavier vertical between columns, and the horizontal caps pulled in so only the verticals hit the outer edges.

.links ul {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1.5rem 2.5rem;
  row-rule: 1px solid #c9a96a;
  column-rule: 3px solid #6a707a;
  rule-overlap: column-over-row;
  column-rule-inset-cap: 1.75rem;
}

Notice rule-overlap. That is another rename: gap-rule-paint-order is gone. rule-overlap decides which axis paints on top at every junction. column-over-row means the columns win where the two cross, so the vertical stripes read as continuous.

Rules next to empty tracks

The last piece is rule-visibility-items. It controls whether decorations render alongside empty areas of the container — the trailing space in a wrapped flex row, the empty tail of an implicit grid track. around in the example below lets rules bracket the items on all sides rather than only between them.

.posts {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 3rem;
  rule: 2px solid #999;
  rule-visibility-items: around;
  rule-inset: overlap-join;
}

Support, and the polyfill

Availability today is Chromium 149, per the source: Chrome, Edge, and downstream Chromium browsers on the same version. Microsoft's Edge web platform team led the design and standardization, and a polyfill is in development for browsers without native support. Anything you write here degrades cleanly — an unsupported browser just skips the decoration and leaves the gap empty.

Try one thing on your next layout

Pick a component where you have a border on every flex child except the last, and swap it for column-rule on the parent. The rules paint in the gaps you have already declared with gap, they respond to wrap, they never care which child is last. That single substitution is where gap decorations earn their name: you were faking a gutter before, now you are decorating one.

Source: CSS-Tricks (css-tricks.com)

Related
CSS & layout

One grid, five rounds, no track matching

A rebuild of the FIFA knockout layout with CSS subgrid shows how nested rounds can share the parent grid's columns and rows, with negative line numbers doing the trimming.

July 19, 2026
CSS & layout

The CSS infinity keyword, and what it lets you delete

A tour through Adam Argyle's recipes for the CSS infinity keyword — pill radii, unbounded clamps, and a boolean pattern the cascade can decide on its own.

August 1, 2026
CSS & layout

The lh unit sizes to the line, not the font

Ahmad Shadeed's post on ishadeed.com tours the CSS lh unit, which resolves to the element's computed line-height. Here's where sizing against that line beats sizing against font-size.

August 1, 2026