Label
48
Placeholder line
2026-09 · Aesthetic · Figma · GitHub
How a soft gradient field gets made, and then what to do with it. Shapes drawn and blurred until they are one, or the same look written as a stop list — either way projected onto whatever geometry it is given. Shape and theme are separate axes, and everything else is a custom property.
Start here: this is where the colour comes from. Draw a few overlapping shapes in flat colour and blur them past recognition — the overlaps become the mid-tones a stop list has to be hand-tuned to produce. Each pair below is the same SVG: as drawn, then blurred. They are grouped by colour, three to a family — the theme is a separate axis from the drawing, so any of these shapes takes any of these colours.
Orange
Sunburst — radiating wedges
blur 0.16
Desert — dunes and a low sun
blur 0.18
One leaf, turned three times
transform="rotate(...)"
Blue
Quatrefoil, ribbon, star
--drawn / --raw
Pixels — a coarse mosaic
blur 0.1
Prism — crossed diagonals
blur 0.12
Mint
Ocean — stacked swells
blur 0.2
Crescent, teardrop, wedge
--drawn / --raw
Spots — a scattered field
blur 0.16
Night
Scanlines — analogue bands
blur 0.09
Ripples — concentric rings
blur 0.14
Nebula — drifting clouds
blur 0.24
With a field in hand, it gets projected onto geometry. One drawing and one theme are held constant here so geometry is the only variable — only border-radius and aspect-ratio change, and the artwork is re-projected to cover whatever it is poured into.
Tile
--tile
Circle
--circle
Arch
--arch
Pill
--pill
A theme sets three colour stops and nothing else, so it composes with any shape and either recipe. `--night` is the exception: it moves the base the colour settles to, and layers over any of them.
Five themes
--dawn --dusk --mint --ice --arc
Settling to ink
--night
Label
48
Placeholder line
Pools moved
--…-pool-x/y, --…-settle
Settle direction
default / --rise
Both recipes at once
--mix, --…-mix: 1 / 0.5 / 0
Content sits in the lower half, where the recipe has settled to the base and nothing needs a scrim to stay legible. --step is the other way round: the surface goes inside a block of type, its steps landing on the same lines.
Card ground
--card
Label
48
Placeholder line
Long to short — the step walks left
--step --ice --night
Short to long — the step walks right
--step --arc --drawn
Long to short, drawn rather than stopped
--step --arc --drawn --down
Two blocks, one field — the upper settle flipped
--step --rise / --step
Section band
--band
A section header
Section band
A gradient recipe and the surfaces that demonstrate it. Colour gathers at two off-centre pools near one edge and settles to the surface's own base at the other, so a shape reads as lit from a direction rather than as a two-colour blend — and the same recipe is re-projected onto every shape, so a tile, a circle, an arch, a pill and a full-width band all take the same light.
There are two ways to make that field and both are kept, because they are good at different things. The stop list is a hand-written set of radial and linear stops: cheap, static, exact. --drawn replaces it with two or three overlapping shapes in SVG blurred past recognition, which is how such a field is actually made — the overlaps supply mid-tones a stop list has to be tuned into producing, and a shape with points leaves soft spokes no arrangement of stops will give you. --mix puts both on one surface and cross-fades between them.
--step is the odd one in the set and the reason the recipe has to be re-projectable: a block of bands, one per line of type, each starting where its own label ends, so the ragged end of the copy is the staircase and the field runs across all three bands as one shape.
--drawn swaps the stop list for an SVG of two or three overlapping shapes blurred past recognition. The overlaps supply the mid-tones a stop list has to be hand-tuned to produce, and shapes with points — a star, a leaf — leave soft spokes that no arrangement of radial stops will give you. Two things make it work: the blur is a ratio of the surface rather than a fixed length, so it dissolves by the same amount at any size — and the ratio is per drawing, since a dense one (a mosaic, a scatter, stacked bands) averages to a single flat colour at the 0.26 a few large shapes want, and needs 0.1 or less; and the artwork bleeds 22% past the frame before being clipped, because blur samples the transparency outside the drawing and a fitted drawing fades to a vignette at every edge. --raw shows the same drawing before the blur, which is the useful half when building a new one. It carries no construction outline: at half fill-opacity the shapes already read from their own edges, and a stroke around each one fences off the overlaps — the part of the drawing actually worth seeing. --…-raw-stroke puts an outline back for anyone who wants one. On --night a drawing also has to cover the frame. Where the artwork leaves a gap the base shows through, and that gap reads as a highlight on paper but as a hole on ink — a mosaic with empty cells and crossed bands with open diamonds both blur to a blotchy field there, so they stay on the light themes and the night set is drawn edge to edge.
--drawn drops the stop list — background-image: none — because there the drawing is the surface, and that makes the swap a cut: nothing is underneath to fade into. --mix keeps both on one surface, the drawing over the stops, and --…-mix is its opacity: 1 is the drawn surface, 0 is the stopped one, and the middle is a genuine double exposure. It is the only part of the recipe besides the base that interpolates, which is what lets a state be eased through rather than cut to — the index card rests at 1 and dissolves to 0 when it is looked at. The curve is symmetric on purpose: a quint ease-out looks right on a single element and is the wrong shape for a cross-fade, down to 0.28 a fifth of the way in and then three fifths of the duration spent between 0.09 and 0, which reads as a cut followed by nothing. cubic-bezier(0.65, 0, 0.35, 1) over 520ms keeps the change in the middle, where both images are on screen together and the double exposure is the whole effect.
Colour gathers at two off-centre origins near the top edge and dissolves outward; a linear-gradient underneath carries the last of it down to the base. One linear ramp corner-to-corner is what makes cheap gradients band — the eye finds the axis. With the pools offset from each other there is no axis to find, and the falloff stays smooth at any size.
The bottom ~40% is plain #fff. That is what makes the shape read as lit from one corner rather than as a two-colour blend, and it is why body copy can sit on the lower half without a scrim.
The hot stop is the only saturated colour; the wash stop is already half-way to the base. Fading a gradient by lightness alone leaves a muddy middle — dropping saturation first keeps the mid-tones clean.
--drawn-gradients-art is declared once and every surface uses it; the shape modifiers change only border-radius and aspect-ratio. The pools are positioned in percentages, so a circle, a pill and a full-width band each get the same light from the same relative direction rather than needing their own gradient.
The figure, label and micro line are calc()s of --…-size, not fixed rem. Set in rem they stayed put while the card scaled, so a card shown small kept full-size type and the label ran outside its own box. The ratios resolve to the original values at the default size; only the legend that names a set keeps a fixed size, because it sits beside the surfaces rather than on them.
notes.md — the full record, including what was tried and dropped