the repo

By Stephen Walker

CSS Grid Decoration

CSS, at least in Chromium browsers, has a new feature to create lines (rules) between columns and rows.

Published 4 min read

The snippet

CSS, at least in Chromium browsers, has a new feature to create lines (rules) between columns and rows — think CSS column rules extended to grid. This will be a popular feature in the future, given all the styling it enables. While we wait for Safari and Firefox, here is a utility for creating a visual grid. It uses a technique Kevin Geary shared a few years ago as a fallback.

.u-ruled-grid {
  /* Rule appearance */
  --rule-grid-color: currentColor;
  --rule-grid-width: 1px;
  --rule-grid-style: solid;

  /* Grid spacing */
  --rule-grid-row-gap: 1.5rem;
  --rule-grid-column-gap: 1.5rem;

  /*
   * Native controls that are not part of the `rule` shorthand.
   *
   * break:      none | normal | intersection
   * inset:      <length-percentage> | overlap-join
   * visibility: all | around | between | normal
   * overlap:    row-over-column | column-over-row
   */
  --rule-grid-break: none;
  --rule-grid-inset: 0;
  --rule-grid-visibility: all;
  --rule-grid-overlap: row-over-column;

  /* Fallback controls */
  --rule-grid-span: 100vmax;
  --rule-grid-row-span: var(--rule-grid-span);
  --rule-grid-column-span: var(--rule-grid-span);
  --rule-grid-fallback-inset-start: 0px;
  --rule-grid-fallback-inset-end: 0px;
  --rule-grid-row-z-index: 2;
  --rule-grid-column-z-index: 1;

  display: grid;
  row-gap: var(--rule-grid-row-gap);
  column-gap: var(--rule-grid-column-gap);

  /* Native gap decorations */
  @supports (rule: 1px solid) {
    rule: var(--rule-grid-width) var(--rule-grid-style) var(--rule-grid-color);

    /* Keep these separate because browsers may ship them independently. */
    @supports (rule-break: intersection) {
      rule-break: var(--rule-grid-break);
    }

    @supports (rule-inset: 0) {
      rule-inset: var(--rule-grid-inset);
    }

    @supports (rule-visibility-items: all) {
      rule-visibility-items: var(--rule-grid-visibility);
    }

    @supports (rule-overlap: row-over-column) {
      rule-overlap: var(--rule-grid-overlap);
    }

    &.u-ruled-grid--rows {
      column-rule: none;
    }

    &.u-ruled-grid--columns {
      row-rule: none;
    }
  }

  /* Pseudo-element fallback */
  @supports not (rule: 1px solid) {
    overflow: hidden;
    overflow: clip;
    isolation: isolate;

    & > * {
      position: relative;

      &::before,
      &::after {
        content: '';
        position: absolute;
        pointer-events: none;
      }

      /* Column rules */
      &::before {
        inline-size: 0;
        block-size: calc(var(--rule-grid-column-span) - var(--rule-grid-fallback-inset-start) - var(--rule-grid-fallback-inset-end));
        inset-block-start: var(--rule-grid-fallback-inset-start);
        inset-inline-start: calc((var(--rule-grid-column-gap) + var(--rule-grid-width)) / -2);
        z-index: var(--rule-grid-column-z-index);
        border-inline-start: var(--rule-grid-width) var(--rule-grid-style) var(--rule-grid-color);
      }

      /* Row rules */
      &::after {
        inline-size: calc(var(--rule-grid-row-span) - var(--rule-grid-fallback-inset-start) - var(--rule-grid-fallback-inset-end));
        block-size: 0;
        inset-inline-start: var(--rule-grid-fallback-inset-start);
        inset-block-start: calc((var(--rule-grid-row-gap) + var(--rule-grid-width)) / -2);
        z-index: var(--rule-grid-row-z-index);
        border-block-start: var(--rule-grid-width) var(--rule-grid-style) var(--rule-grid-color);
      }
    }

    &.u-ruled-grid--rows > *::before {
      display: none;
    }

    &.u-ruled-grid--columns > *::after {
      display: none;
    }
  }

  /* Break rules at row and column intersections. */
  &.u-ruled-grid--break-intersection {
    --rule-grid-break: intersection;
    --rule-grid-row-span: 100%;
    --rule-grid-column-span: 100%;
  }

  /* Keep rules continuous through intersections. */
  &.u-ruled-grid--break-none {
    --rule-grid-break: none;
    --rule-grid-row-span: var(--rule-grid-span);
    --rule-grid-column-span: var(--rule-grid-span);
  }

  /* Paint column rules above row rules. */
  &.u-ruled-grid--column-over-row {
    --rule-grid-overlap: column-over-row;
    --rule-grid-row-z-index: 1;
    --rule-grid-column-z-index: 2;
  }
}