Skip to content

SECTION

Feature cell

One capability in a grid: a picture, a name, a sentence. Drop it inside a hairline grid.

Three capabilities

Editors β†—

Undo that survives contact with a real document.

Design systems

Token sets that survive four platforms and a rebrand.

Performance

Sixty frames on a canvas that has no right to hold them.

HTML
<div class="hairline-grid icon-on-sunk" style="--cell:205px; width:100%">
  <div class="hairline-cell feature-cell">
    <svg class="icon icon-duo icon-xl" viewBox="0 0 40 40" aria-hidden="true" stroke-width="2">
      <rect x="5" y="8" width="30" height="24"></rect><path d="M5 16 L35 16"></path>
    </svg>
    <div class="feature-cell-text">
      <span class="feature-cell-title"><a class="link-quiet" href="#">Editors β†—</a></span>
      <p class="feature-cell-body">Undo that survives contact with a real document.</p>
    </div>
  </div>
  <div class="hairline-cell feature-cell">
    <svg class="icon icon-duo icon-xl" viewBox="0 0 40 40" aria-hidden="true" stroke-width="2">
      <path d="M6 30 L14 30 L14 34 L6 34 Z"></path><path d="M14 20 L26 20 L26 34 L14 34 Z"></path><path d="M26 8 L34 8 L34 34 L26 34 Z"></path>
    </svg>
    <div class="feature-cell-text">
      <span class="feature-cell-title">Design systems</span>
      <p class="feature-cell-body">Token sets that survive four platforms and a rebrand.</p>
    </div>
  </div>
  <div class="hairline-cell feature-cell">
    <svg class="icon icon-duo icon-xl" viewBox="0 0 40 40" aria-hidden="true" stroke-width="2">
      <circle cx="20" cy="20" r="13"></circle><path d="M20 7 L20 20 L29 25"></path>
    </svg>
    <div class="feature-cell-text">
      <span class="feature-cell-title">Performance</span>
      <p class="feature-cell-body">Sixty frames on a canvas that has no right to hold them.</p>
    </div>
  </div>
</div>

A number where the picture goes

feature-cell-index is a numbered lead, for a cell that is a step rather than a capability. It takes the icon's slot rather than adding one, so there is no variant on the cell itself: write this span or write an icon, and the column is indifferent.

01 / DRAW
Paint on stacked layers

Brush, shapes, text and mixing modes, or bring your own images and place them on the canvas.

02 / EXTRUDE
Give each layer depth

Depth and offset per layer, recomputed as you work, so the model is never out of date with the drawing.

03 / FLATTEN
Get a drawing back

The model returns as a perspective-aware 2D drawing, hatched, solid or on paper.

HTML
<div class="hairline-grid" style="--cell:230px; width:100%">
  <div class="hairline-cell feature-cell">
    <span class="feature-cell-index">01 / DRAW</span>
    <div class="feature-cell-text">
      <span class="feature-cell-title">Paint on stacked layers</span>
      <p class="feature-cell-body">Brush, shapes, text and mixing modes, or bring your own images and place them on the canvas.</p>
    </div>
  </div>
  <div class="hairline-cell feature-cell">
    <span class="feature-cell-index">02 / EXTRUDE</span>
    <div class="feature-cell-text">
      <span class="feature-cell-title">Give each layer depth</span>
      <p class="feature-cell-body">Depth and offset per layer, recomputed as you work, so the model is never out of date with the drawing.</p>
    </div>
  </div>
  <div class="hairline-cell feature-cell">
    <span class="feature-cell-index">03 / FLATTEN</span>
    <div class="feature-cell-text">
      <span class="feature-cell-title">Get a drawing back</span>
      <p class="feature-cell-body">The model returns as a perspective-aware 2D drawing, hatched, solid or on paper.</p>
    </div>
  </div>
</div>

A bar, and no mesh at all

feature-cell-mark is the third lead: a short rule for an item in a row of equals, where an icon each would be six drawings nobody looked at. And the cells are in a split rather than a hairline-grid β€” the cell never knew which arrangement it was in.

Painting tools

Square and circle brushes, shapes, text, eight mixing modes, per-stroke build-up and speed.

Bring your own art

Import images, place them on the canvas, then trace over them or extrude them as they are.

Palettes

Ready palettes, colour picked straight off the canvas, and your own sets on top.

Layers with depth

Depth and offset on every layer, island counts tracked, and an outliner to keep it straight.

HTML
<div class="split" style="--split-min:230px; width:100%">
  <div class="feature-cell">
    <span class="feature-cell-mark"></span>
    <div class="feature-cell-text">
      <span class="feature-cell-title">Painting tools</span>
      <p class="feature-cell-body">Square and circle brushes, shapes, text, eight mixing modes, per-stroke build-up and speed.</p>
    </div>
  </div>
  <div class="feature-cell">
    <span class="feature-cell-mark"></span>
    <div class="feature-cell-text">
      <span class="feature-cell-title">Bring your own art</span>
      <p class="feature-cell-body">Import images, place them on the canvas, then trace over them or extrude them as they are.</p>
    </div>
  </div>
  <div class="feature-cell">
    <span class="feature-cell-mark"></span>
    <div class="feature-cell-text">
      <span class="feature-cell-title">Palettes</span>
      <p class="feature-cell-body">Ready palettes, colour picked straight off the canvas, and your own sets on top.</p>
    </div>
  </div>
  <div class="feature-cell">
    <span class="feature-cell-mark"></span>
    <div class="feature-cell-text">
      <span class="feature-cell-title">Layers with depth</span>
      <p class="feature-cell-body">Depth and offset on every layer, island counts tracked, and an outliner to keep it straight.</p>
    </div>
  </div>
</div>

A picture that reaches the hairline

The fourth lead, and the only one that changes the markup around it. A screenshot across the top of a cell wants the cell's EDGES, not its padding β€” so the cell is hairline-cell-flush, the picture goes in a feature-cell-media, and feature-cell-caption gives the words their padding back. That is the shape triptych has had since it was written; this is the same shape on the part that also has a title. --ratio is the same knob frame and placeholder read: set it on the grid, as this does, and every picture under it follows. --media-inset is the mat: nothing by default, so the picture reaches the hairline, and any value shows the cell's own surface as a border around it. These three carry 6px.

THE FLAT DRAWING
01 / DRAW
Paint layer by layer

Brush, shapes, text and mixing modes, or bring your own images and place them on the canvas.

THE SOLID ONE
02 / EXTRUDE
Give each layer depth

Depth and offset per layer, recomputed as you work, so the model is never out of date with the drawing.

AND FLAT AGAIN
03 / FLATTEN
Or get vector art back

The model returns as a drawing that keeps the camera you are looking through.

HTML
<!-- The cell is `hairline-cell-flush`, so the picture reaches the hairline and
     the caption gives the words their padding back. `--ratio` is the same knob
     `frame` and `placeholder` read; set it on the grid and every picture under
     it follows.

     `--media-inset` is the mat. Take it off and the pictures go back to the
     hairline, which is the shipped default β€” a screenshot filling a panel
     usually wants that, and a picture with a subject in it usually does not.

     `--ds-radius-frame` is set here to stand in for a theme that rounds its
     photographs; the library ships it at `0px`, because square media is a
     decision this design made. Turn it up and every picture in the system
     follows, this one included β€” capped at the width of its own mat. -->
<div class="hairline-grid" style="--cell:230px; --ratio:4/3; --media-inset:6px; --ds-radius-frame:6px; width:100%">
  <div class="hairline-cell hairline-cell-flush feature-cell">
    <div class="feature-cell-media">
      <div class="placeholder">
        <span class="placeholder-caption">THE FLAT DRAWING</span>
      </div>
    </div>
    <div class="feature-cell-caption">
      <span class="feature-cell-index">01 / DRAW</span>
      <div class="feature-cell-text">
        <span class="feature-cell-title">Paint layer by layer</span>
        <p class="feature-cell-body">Brush, shapes, text and mixing modes, or bring your own images and place them on the canvas.</p>
      </div>
    </div>
  </div>
  <div class="hairline-cell hairline-cell-flush feature-cell">
    <div class="feature-cell-media">
      <div class="placeholder placeholder-warm">
        <span class="placeholder-caption">THE SOLID ONE</span>
      </div>
    </div>
    <div class="feature-cell-caption">
      <span class="feature-cell-index">02 / EXTRUDE</span>
      <div class="feature-cell-text">
        <span class="feature-cell-title">Give each layer depth</span>
        <p class="feature-cell-body">Depth and offset per layer, recomputed as you work, so the model is never out of date with the drawing.</p>
      </div>
    </div>
  </div>
  <div class="hairline-cell hairline-cell-flush feature-cell">
    <div class="feature-cell-media">
      <div class="placeholder">
        <span class="placeholder-caption">AND FLAT AGAIN</span>
      </div>
    </div>
    <div class="feature-cell-caption">
      <span class="feature-cell-index">03 / FLATTEN</span>
      <div class="feature-cell-text">
        <span class="feature-cell-title">Or get vector art back</span>
        <p class="feature-cell-body">The model returns as a drawing that keeps the camera you are looking through.</p>
      </div>
    </div>
  </div>
</div>

Reference

Read from sections/feature-cell.css when this page was built, so it cannot fall behind the stylesheet.

CLASSES.feature-cell.feature-cell-index.feature-cell-mark.feature-cell-title.feature-cell-body.feature-cell-text.feature-cell-media.feature-cell-caption
SETTINGSSet the first on one element; a theme sets the second for all of them.--feature-title β€” falls back to --ds-text-ui-lg--media-inset β€” falls back to --ds-media-inset
READS--ds-accent--ds-cell-mark-h--ds-cell-mark-w--ds-font-body--ds-font-display--ds-gap-loose--ds-gap-tight--ds-hairline--ds-hit-min--ds-ink--ds-ink-soft--ds-pad-card--ds-radius-frame--ds-rule--ds-text-caption--ds-text-meta--ds-track-key--ds-track-subtle--ds-weight-display

Notes

THE MAT, AND WHEN TO WANT ONE

--media-inset puts a border of the cell's own surface around the picture. Nothing by default, because a screenshot that FILLS a panel wants the edge β€” it reads as a window into the thing, and a margin around it reads as a photograph of a window. A picture with a subject in it is the other case: matted, it sits inside the mesh rather than being cut by it.

A matted picture takes the frame's corner β€” --ds-radius-frame, the same token frame and placeholder read, deliberately off the --ds-radius ladder because square media is a decision rather than a value somebody forgot. So a theme that rounds its photographs rounds these. It is capped at the width of the mat: with no mat the picture is flush against the mesh's own hairlines, where a corner would show the cell's surface through a notch on two sides, so it resolves to zero and stays square.

All four corners, and not just the two at the mesh's own curve. In a grid that wraps there is no way to select "the last cell of the first row" β€” so a positional rule would put a rounded corner in the middle of the mesh the moment three cells became two rows. With a mat every picture is an island anyway: nothing is touching a hairline for a corner to fight.

Two levels, the way split and flow do it. --ds-media-inset is what a THEME decides, and it ships at 0px β€” zero as a decision rather than as the absence of one, which is the same argument --ds-shadow-flat makes. A theme that wants every picture matted says so once, there.

πŸ›‘ A NEGATIVE MARGIN CANNOT BLEED A PICTURE OUT OF THIS CELL

The obvious way to bleed a picture out of a padded box is to pull it back with a negative margin the width of the padding. It does not work here, and it fails quietly. --ds-pad-card is clamp(14px, 7%, 38px), and a percentage padding resolves against the cell's containing block while a percentage margin on a child resolves against the cell's content box. Two different widths: 26.6px against 22.9px in a 380px cell. The picture would sit a few pixels inside the hairline on every side, and nothing on the page would say why.

hairline-cell-flush moves the padding instead of cancelling it, which is why the library already had the answer before this needed one.

πŸ›‘ A NEGATIVE MARGIN CANNOT DO THIS JOB

The obvious way to bleed a picture out of a padded box is to pull it back with a negative margin the width of the padding. It does not work here, and it fails quietly. --ds-pad-card is clamp(14px, 7%, 38px), and a percentage padding resolves against the cell's containing block while a percentage margin on a child resolves against the cell's content box. Two different widths: 26.6px against 22.9px in a 380px cell. The picture would sit a few pixels inside the hairline on every side, and nothing on the page would say why.

hairline-cell-flush moves the padding instead of cancelling it, which is why the library already had the answer before this needed one.

A TITLE THAT IS A LINK

Put an link-quiet inside the title β€” the first cell above does β€” and it takes the title's own voice with a hover and focus underline. The library gives it the hit area, because you should not have to know it needs one.

A linked title is not the case WCAG 2.5.8 excepts. That exception is for a target inside a sentence, where making it bigger would break the line it belongs to. A title standing on its own line is not a sentence: measured at this size it is 18px tall, six short of the floor.

THE NUMBER READS ACCENT, NOT ACCENT-DEEP

For a week it read --ds-accent-deep, and that is a surface role. The contract says so in as many words, which is why it carries no text floor at all: a filled accent is measured by what sits on it, not by what it sits on.

So the label measured 2.22:1 on a raised cell in the dark theme, where AA asks 4.5 β€” and 7.42:1 on paper, because a deep accent on a light ground always looks right. That is the tell for this whole family of bug: it is only ever wrong in one of the two themes, so looking at the page you happen to be on tells you nothing.

check-css refuses it now, reading the seven surface roles straight out of the contract.

The bar below had the same fault from the other side, and it is the clearer of the two. The three leads are one slot β€” the cell does not know which it has β€” so they cannot be three colours. The icon takes the cell's own color and the number reads the accent; the bar alone was drawn in the fill role, which on a dark page is a dark thing on a dark thing. 2.59:1 against the ground, where a mark owes 3.

And that is not an exception to the role. A deep accent is for ink sitting on it, or for a mark on a bright plate β€” the bullet on a featured offer measures 5.36:1 on cream and would measure 1.80:1 in the accent. Which colour a mark takes is decided by the plate it is drawn on, and this one is drawn on the page.

SET --icon-bg ON THE GRID, NOT THE CELL

These icons are two-tone: the outline takes the cell's color and the interior fills with --icon-bg. Put .icon-on-sunk on the grid and every cell is right at once. Get it wrong and the interiors read as holes.

This is its own file rather than a variant of .hairline-cell because the grid is a layout device that knows nothing about content, and this is content that happens to live in one.