This note has an interactive demo. Try it in the original
Drawing a line between grid cells has always been a hack: borders on half the items, :nth-child() rules to remove the extra ones, or a coloured background peeking through the gaps. Since Chrome and Edge 149 there's a real property for it: row-rule and column-rule now work on grid and flex containers, not just on multi-column text.
I like this one because it fixes a very everyday problem. Bento layouts, dashboards, pricing tables, footers with columns: they all want separators, and separators belong to the gap, not to the items.
What gap decorations are
column-rule isn't new. It has drawn the line between columns of multi-column text for years. The CSS Gaps module takes that idea and extends it to grid and flexbox, adds the row direction with row-rule, and gives you control over where lines break, which gaps get a line, and how far the ends are pulled back.
Two rules from the spec are worth knowing before you start:
- They take up no space. A decoration never moves anything. If it's wider than the gap, the adjacent items simply overlap it.
- They're painted just above the container's border, and in a scroll container they scroll with the items.
The code the demo uses
The demo above is a four-column, four-row grid with nine tiles. The core is three declarations:
.bento {
display: grid;
gap: 18px;
row-rule: 2px solid #FF5200;
column-rule: 2px solid #00D4AA;
}
row-rule and column-rule are shorthands for -width, -style and -color, exactly like border. The defaults are also the same as a border's: medium, none and currentcolor. So if you forget the style, nothing gets painted. If you want the same line in both directions, rule sets both at once.
The part that has no equivalent with borders is that the value is a list. Choose the "Lists" pattern in the demo and you get this:
.bento {
row-rule: 3px solid #FF5200, repeat(auto, 1px dashed #94A3B8), 3px solid #FF5200;
column-rule: 2px dotted #00D4AA, 2px dashed #FF7E41;
}
Each value goes to one gap, in order. When there are more gaps than values, the list starts again, so the columns alternate dotted and dashed. repeat(auto, …) fills whatever the other values don't cover: here, first and last row gaps are thick and solid, and everything in between is a thin dashed line, however many rows there are. Only one repeat(auto, …) is allowed per value. There's also repeat(3, …) with a fixed count.
Breaks, empty cells and insets
The other three controls in the demo map to these properties:
-
rule-break:none,normalorintersection. Withnone, each line runs from one end of the gap to the other. Withintersection, it's cut at every T and every cross.normaldepends on the container: in grid it cuts at T junctions but runs through crosses; in flexbox it behaves likenone. -
rule-visibility-items:all,aroundorbetween. It decides whether a line is painted next to an empty cell. Tap the big tile to remove it and switch tobetween: the lines around the hole disappear, because there's no longer an item on both sides. The initial value,normal, behaves likeallin grid. This one only applies to grid and multi-column containers. -
rule-inset: moves the ends of each segment. A positive value pulls them back from the junction, and percentages are resolved against the width of the gap being crossed. There's also anoverlap-joinkeyword, and longhands that separate the ends at the container's edges (cap) from the ones at junctions (junction).
When lines don't break, rule-overlap decides which direction is painted on top. The initial value is row-over-column.
Change patterns in the demo and the colour and width animate: the spec makes -color and -width animatable as lists, and I checked in Chromium that a transition on row-rule-color interpolates through intermediate colours. The style switches without interpolation.
Support and gotchas
-
Chrome and Edge 149 only for now. MDN's compatibility data marks
row-rule,rule-break,rule-insetand the rest as experimental, with no support in Firefox or Safari. The spec is an Editor's Draft that's still changing: the last Working Draft is from 24 June 2026. -
Don't feature-detect with
column-rule. It has existed for multi-column since long before this, so the browser will say yes even if it doesn't paint anything in a grid. Ask aboutrow-ruleinstead (MDN's examples do the same with@supports):
const supported = CSS.supports('row-rule', '1px solid red');
-
The fallback is free, but it's nothing. In a browser that doesn't know these properties, the declarations are ignored and the gaps remain empty. For decorative lines, that's usually acceptable. If the separator carries meaning, you need a plan B. The demo uses the classic one when detection fails: paint the container with the line colour clipped to
content-box, with opaque, square-cornered items covering it. It works only for solid lines across every gap: no dashes, no breaks and no clean empty cells. I tested it in Chromium by forcing the fallback. -
Lines don't make room. If you increase the width without increasing the
gap, the items cover the line. Keepgapat least as wide as the rule.
What to keep an eye on
- Firefox and Safari implementing it. Until then, use it as progressive enhancement and nothing more.
- Changes to the spec: property names and behaviours are still being refined, including how it works with the new grid lanes containers.
- Whether the design systems that today draw dividers with borders on each item start moving that to the container.
Sources
Originally published at richi.codes.
Top comments (0)