This document is intended as a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions.
- This document status: Active
- Expected venue: CSS Working Group
- Current version: this document
- CSS Gap Decorations
CSS multi-column containers allow for rules to be drawn between columns. Applying similar styling to other container layouts such as grid and flex has been widely sought after, as seen in the discussion for CSS Working Group issue 2748 and in several StackOverflow questions ( [1] [2] [3] [4] ). Currently, developers seeking to draw such decorations must resort to non-ergonomic workarounds such as these examples:
- https://www.smashingmagazine.com/2017/09/css-grid-gotchas-stumbling-blocks/#how-do-i-add-backgrounds-and-borders-to-grid-areas
- https://x.com/geddski/status/1004731709764534274
- Extend CSS column rule properties to apply to grid, flex, and masonry in addition to multi-column containers.
- Introduce row-direction gap decorations on CSS grid, flex, and masonry containers.
- Allow gap decorations to vary over a given container to handle cases such as alternating row separators.
- Gap decorations on CSS Tables. The CSS Tables specification is currently Not Ready for Implementation, and there are interoperability differences among engines. Additionally, authors can achieve many of the scenarios covered by this explainer in a table already using cell borders.
- Row-direction gap decorations on multi-column containers. This is theoretically feasible for cases where an element spans across multiple columns, but currently row gaps do not apply to multi-column containers, so there is nowhere to put such a decoration. Support for row-gap on multi-column containers was proposed in issue 6746; discussion in that issue also notes the potential for multi-column to gain block-direction overflow with a corresponding gap. This non-goal could become a goal if either of these ideas are adopted.
- Images in gap decorations. Compared to, say, border-image, gap decoration images need to cover significantly more cases such as T intersections. See this comment for more detail. Further exploration is needed into the best way to handle these, so this scenario is left to a future level of the feature.
Use cases in this explainer were collected from the discussion in issue 2748. Additional inspiration was drawn from discussions in issues 5080, 6748, and 9482.
Unless otherwise noted, corresponding row-
and column-
properties should be
assumed to have identical syntax. All such pairs of properties also have gap-
shorthands that apply the same values in both directions.
For property grammar details, please see the Editor's Draft.
In addition to replicating the existing column-rule properties in the row
direction, we expand the syntax of both sets of properties to allow for multiple
definitions. Authors may use familiar syntax from CSS Grid such as repeat()
and auto
to create patterns of line definitions that apply within a given gap
decoration area. Note that while repeat()
and auto
are inspired by CSS Grid,
they may also be used to create patterns of decorations in flex, multi-column,
and masonry containers.
If the number of specified values (after expanding any repeats) in a given list is less than the number of gaps in the corresponding direction in the gap decoration area, the list cycles back to the beginning.
Shorthands are also available to combine the width, style, and color properties.
.alternate-red-blue {
display: grid;
grid-template: repeat(auto-fill, 30px) / repeat(3, 100px);
grid-gap: 10px;
row-rule: 1px solid;
row-rule-color: red blue;
}
.alternate-heavy-light {
display: grid;
grid-template: repeat(auto-fill, 30px) / repeat(3, 100px);
grid-gap: 10px;
row-rule: 2px solid black / 1px solid lightgray;
}
Like column rules in multi-column layout, gap decorations in other layout
containers do not take up space and do not affect the layout of items in the
container. Conceptually, gap decorations are considered after layout has
completed, and in particular after we already know the full extent of the
implicit grid in grid
layout, or the number of lines in flex layout, or the number of columns in
multi-column layout, or the number of tracks in masonry layout. Thus, the
repeat()
grammar, while modeled after the grid-template
properties, is
simpler for gap decorations as there are fewer unknowns to consider.
.varying-widths {
dispay: grid;
grid-template: repeat(auto-fill, 30px) / repeat(3, 100px);
row-gap: 9px;
row-rule: 5px solid black / repeat(auto, 1px solid black) / 3px solid black;
}
.item {
height: 30px;
padding: 5px;
border: 1px dotted lightgray;
}
By default, gap decorations are painted as continuous segments that extend as far as possible along the centerline of a given gap. The decoration is painted from one gap T intersection to another, with both endpoints at the centers of the T crossings and the decoration proceeding along the stems of both Ts. In grid layout, row decorations are painted on top of column decorations by default; changing this behavior is covered in a later section of this document.
.grid-with-spans {
display: grid;
grid-template: repeat(4, 100px) / repeat(4, 100px);
gap: 20px;
row-rule: 6px solid red;
column-rule: 6px solid blue;
}
.flex {
display: flex;
flex-wrap: wrap;
gap: 20px;
width: 500px;
row-rule: 6px solid red;
column-rule: 6px solid blue;
}
Authors may adjust the positions of endpoints relative to gap intersections, either as a fixed distance or as a percentage of the width of the intersection. The "zero point" is the edge of the intersection, with positive values extending into the intersection and negative values receding from it.
.outset-0px {
column-rule-outset: 0px;
}
.outset-5px {
column-rule-outset: 5px;
}
.outset-negative-5px {
column-rule-outset: -5px;
}
Authors may also change the set of intersections where gap decorations break, from the default "T intersections" behavior to either "all intersections" or "no intersections." In the latter case, gap decorations paint "behind" items in the container.
.t-intersections {
gap-rule-break: spanning-item;
gap-rule-outset: 0px;
}
.all-intersections {
gap-rule-break: intersection;
gap-rule-outset: 0px;
}
.no-intersections {
gap-rule-break: none;
}
When row and column gap decorations overlap, authors can control their painting order.
gap-rule-paint-order: [ row-over-column | column-over-row ]
.row-over-column {
row-rule: 6px solid red;
column-rule: 6px solid blue;
gap-rule-paint-order: row-over-column;
}
.column-over-row {
row-rule: 5px solid red;
column-rule: 5px solid blue;
gap-rule-paint-order: column-over-row;
}
w3c/csswg-drafts#2748 (comment), which links to: https://codepen.io/urlyman/pen/yGNOya
The desired effect is a line appearing only between the grid rows, and extending unbroken across the column gaps.
Note that I don't want a line to appear above or beneath all rows, only in the gaps between rows.
.container {
row-rule: 1px solid #ccc;
}
w3c/csswg-drafts#2748 (comment)
.container {
gap-rule: thick solid green;
}
w3c/csswg-drafts#2748 (comment) - last example
.container {
gap-rule: 1px solid black;
column-rule-outset: 0px;
}
- How do gap decorations apply to subgrids?
- Can we construct an all-encompassing
gap-rule
shorthand? The challenge here is that/
is already heavily loaded in the longhands.
Allow authors to specify where gap decorations start and end within a container.
An author may specify more than one such region and apply a different set of gap decorations to each. Within this document, we refer to such a region as a gap decoration area. Much like CSS Transitions and Animations, all gap decoration properties may take a comma-delimited list of values. Each entry in such a list is applied to the corresponding entry in the list of gap decoration areas. If a given property's list length is shorter than the gap decoration area list length, the shorter list cycles back to the beginning as needed.
Gap decoration area properties are defined per container type.
In grid containers, the author may specify any grid line based placement, as in
the 'grid-row-start', 'grid-row-end', 'grid-column-start', and
'grid-column-end'
properties. The
corresponding width-style-color gap decoration tuples in the row and column
directions will apply in that area. The initial value is 1 / 1 / -1 / -1
to
cover the entire grid.
.grid-multiple-decoration-areas {
display: grid;
grid-template-rows: [top] 30px [main-top] repeat(6, 30px) [bottom];
grid-template-columns: [left] 100px [main-left] repeat(3, 100px) [right];
grid-gap: 10px;
grid-row-rule-area: left / top / main-left / bottom,
main-left / main-top / right / bottom;
row-rule: 1px solid lightblue,
1px solid black;
grid-column-rule-area: main-left / top / main-left / bottom;
column-rule: 1px solid lightblue;
}
Gap decoration area properties for these container types are not yet defined.
w3c/csswg-drafts#2748 (comment)
.container {
gap-rule-style: solid:
gap-rule-color: lightgray;
column-rule-width: 1px repeat(auto, 2px) 1px;
row-rule-width: 0px repeat(auto, 2px 1px);
grid-gap-rule-area: 2 / 2 / -1 / -1;
}
This idea was dropped based on feedback raised in the initial proposal discussion.
These are designed to enable scenarios where authors wish to switch
flex-direction
or masonry-direction
based on space constraints or other
factors.
Property | row or row-reverse direction | column or column-reverse direction |
---|---|---|
main-rule-width | row-rule-width | column-rule-width |
main-rule-style | row-rule-style | column-rule-style |
main-rule-color | row-rule-color | column-rule-color |
main-rule | row-rule | column-rule |
cross-rule-width | column-rule-width | row-rule-width |
cross-rule-style | column-rule-style | row-rule-style |
cross-rule-color | column-rule-color | row-rule-color |
cross-rule | column-rule | row-rule |
And so on for other properties.
For flex and masonry containers, the logical properties map based on
flex-direction
or masonry-direction
following the convention above.
For grid containers, main
maps to row
, and cross
maps to column
.
For multi-column containers, main
maps to column
, and cross
maps to row
.
In 2021, Mats Palmgren from Mozilla posted a draft specification for gap decorations. We believe the proposal in this explainer improves on developer ergonomics by (a) reusing concepts from grid layout such as repeat and grid lines, and (b) simplifying the model for fine-tuning segment placement. We also believe the proposal in this explainer offers developers more flexibility even absent support for gap decoration images; see Scenario 3 for one example.
Many thanks for valuable feedback and advice from:
- Alison Maher
- Benoît Rouleau
- Ian Kilpatrick
- Josh Thumath
- Kurt Catti-Schmidt
- Lea Verou
- Rachel Andrew
- Sebastian Zartner
- Tab Atkins-Bittner