Colour, type, grid, space, icons, motion. The rules this site is built out of, each one shown running rather than described.
A system is only worth having if it decides things for you. These four do the deciding; every token further down is a consequence.
No token is chosen by eye. Each is generated from a rule, which is why there are twenty-four of them and only three numbers to remember.
Every section owns a hue and carries it from the tile into the page. Colour is how you know where you are, so it never gets used decoratively.
Nothing moves for charm. Every transition either shows where a thing came from or where it is going. Four curves, used consistently.
Contrast ratios, chroma spreads and hue angles here are computed at runtime. If the system breaks, the page says so.
#d4622a lands at
44°. That single point is the seed for everything
else on this page.Lite is the tile. Ink is its label, its artwork, and the ground its page opens on. Mid carries rules, links and accents. Click any swatch to copy it.
Two ways to read “bright.” Watch the bars arrive: one set lines up and the other can’t.
Worst case …, best …, a spread of … across eight different hues. That evenness is the rule working, not luck.
One face shouts and one explains. Nothing sits between them. The moment a third weight appears to bridge the two, the hierarchy stops working.
The house display face. Condensed, heavy, uppercase, tracked tight. Impact first so it’s the real thing where installed; Anton covers Linux, where it isn’t.
Box Buddy is the deliberate exception: it carries its own product’s display face, Baloo 2 800, because a case study that argues for a design system should be set in it, and its tile hands over to that face on hover at the same moment it hands over to the colour. Mass Virtual takes the slot next to it and does not, because its own voice is condensed uppercase, which is what Anton already is. Nothing to hand over.
Everything you actually read. Optical-size axis, weights 300–600, 1.6 line height, capped at 52 characters so a paragraph never outruns a comfortable measure.
Four columns at 20/30/30/20, the middle two offset from each other by half a button, every seam tangent to an edge of the centre button. All eight tiles are different sizes, which is what stops it reading as a spreadsheet.
The whole site changes shape at exactly two widths: 1024px and 640px. Everything else, meaning column count, where the section numeral sits, which rules are structural, how many cards fit in a row, is derived from those. Drag the handle to watch the grid re-form.
| State | Width | Columns | Numeral | Rules drawn | Rows of three |
|---|
Desktop keeps two columns for the margin numeral, which leaves twelve for the body, and twelve divides by 2, 3, 4 and 6, so every repeating row lands on a drawn rule instead of near one. Below that the numeral moves above the content and the body takes the full width: eight divides by 2 and 4, four only by 2. Three across is the one arity that never divides cleanly, so it gets a shared utility that narrows the row rather than being re-derived per page.
A rule is drawn solid only where content actually starts or stops on it; a rule content crosses stays dashed and says so. That verdict is re-made at each state. Boundary 2 is structural on desktop because the body begins there. Below desktop the body spans the full width, so the same line stops marking anything and demotes to a dashed reference. Drawing it solid on a phone put a hard line straight through body copy.
Two numbers, and they are widths. Every breakpoint on this site is a width. There was once a single height-based one, on a case study whose full-screen hero pushed its product shot below the fold on a short phone, but that page has since been retired and the exception went with it. The rule it proved still holds: reach for a height query only when the problem is genuinely vertical, which is rarer than it looks.
One icon per tile, in that tile’s own deep tier. Five are the tile’s real vector reused rather than traced, including Mayo’s crest, coordinate for coordinate. Every one is a filled shape that hollows to a 2px stroke on hover: same silhouette, inverted weight. Hover them.
Mass Virtual is the exception, and for the opposite reason to the rest. There is no vector mark to reuse: the only copy of that logo here is burnt into a photograph, and tracing a wordmark off a JPEG ships a slightly wrong version of somebody else’s brand. So its cell is drawn, a headset rather than a logo, which is the subject of the work instead of the client’s identity.
Each glyph keeps whatever viewBox its source came on:
100×100 for the drawn ones, 300×200 for the cloud, 45.4×28 for the crest.
That is because reusing a real mark means taking its coordinate space with it rather than
refitting it. vector-effect: non-scaling-stroke holds the outline at exactly
2px whatever the box, and fill and stroke are both currentColor, which is
what lets a tile invert without a single extra rule.
Four rules decide whether motion on this site feels built or bolted on. None of them are about taste. Each one is a structural choice that a curve cannot rescue. Every demo below is live.
nav.js0.14 per frame. The top dot is bound to input;
the bottom one is damped.
design-system.jsinnerHTML every
frame. That throws away compositing, forces layout on every node, and makes each
marker pop instead of move. Building the nodes once and only ever transforming
them was the entire fix. The easing never changed.
One more, learned the hard way: direction belongs
in a custom property, not a selector. A retired case study's mockups alternated
the side they
entered from. Written as :nth-child(even) that rule counted the tab row
as a child and outranked the arrived state on specificity, so two of four
panels stopped mid-flight. Moving the direction into --dx / --rot
fixed it, because custom properties carry no specificity at all.
None are stock. Each exists because a stock easing visibly flattened the thing it was applied to. Each curve draws itself in its own shape; the dots run on the real values.
cubic-bezier(.4, 0, 0, 1)
The workhorse. Every reveal on the site. The second control point at 0,1 is
what produces the long decelerating tail.
cubic-bezier(.4, 0, .2, 1)
Hover states, tile colour changes, the menu. Symmetric enough to stay
neutral when it fires dozens of times a minute.
cubic-bezier(.6, 0, 0, 1)
The pinwheel opening and closing. Slower to leave, faster to arrive, so it
reads as weight rather than speed.
cubic-bezier(1, .25, .85, 1)
The home page scroll. x1 = 1 holds the tiles nearly still through
the first stretch, so the grid arrives with a rush.
Reveals are one-shot. Once an element has arrived it never resets, even on the way back up. Replaying them makes a long page feel twitchy and makes re-reading unpleasant.
Contrast wasn’t checked at the end. It picked the lightness the whole system sits at, which is why all eight clear AA without a single hand adjustment.
Label on tile never drops below 4.6:1, computed live at the top of this page. No hue got a manual nudge to pass.
prefers-reduced-motion is honoured everywhere: the wheel stops,
reveals resolve instantly, the marquee holds still.
The parked navigation is inert, not just invisible, so a
keyboard never tabs into eight offscreen tiles it can’t see.
Every hue is paired with a label and a position in the grid. Nothing here is identified by colour on its own.
Everything above documents the site you are standing in. This one is the other half of the practice: a working Figma library, open and messy at the edges, that I use to keep up with the tool. When Figma ships variables modes or a new way to handle properties, this is where I go and rebuild something in it.
It started as the design system for a reading app called Pagepaw. I shelved the product after some research turned up two competitors doing it better, which was the right call and a cheap one to make at that stage. The library outlived it. It is the piece that was always going to be useful again.
The tokens were written in code first and the app was built from them. The library was then generated out of that same code, so it can’t quietly disagree with what shipped. A library maintained by hand starts lying the first week somebody fixes something without opening Figma.
Light and dark are two modes of one variable collection rather than two sets of screens. Flip the mode on any frame in the file and it holds, which is the only version of dark mode that survives contact with a second designer.
Page five is eight calls where my first answer was wrong, each set against what replaced it. More honest about how the work went than the finished screens are, and the page I would open first if I were you.
The file’s own cover, which doubles as its index. Use the page menu top left to move through foundations, components, screens, wireframes, decisions, the prototype and the art.
The pinwheel grid, the tile-to-page transition and the idea that a brand site should behave like the thing it documents are all lifted from one place, and it would be dishonest not to say so.
The direct reference. The eight-tile pinwheel with a button as a hole in the grid, the scroll-driven assembly, and the label-in-its-own-deep-hue rule are theirs. Built by Dropbox Brand Studio with Daybreak Studio.
Their palette is hand-picked; this one is generated. Eight hues derived from a single brand orange and its complement, held at one lightness so they weigh the same, which their set, measured, does not.
The section order, running principles, colour, type, grid, space, iconography, motion, accessibility, follows the structure common to Atlassian, Carbon, Polaris and Material.
Angela Clemons designed and built this system, and the site running on it. Available now for contract or full-time, remote, US and international. angela.b.clemons@gmail.com
Keep scrolling and the grid comes back. Or hit the tab, or press Escape.Keep swiping up and the grid comes back. Or tap the button.