James BoothSep 22, 20263 min read
We drew our icons instead of picking a library
The rail down the left of our product is the screen a client looks at every day. It drew a popular open-source icon set at 16 pixels, and eight of those icons stood for more than one item. A brain meant three different things. So did an open book. Inbox, Chat and Interact wore near-identical speech bubbles.
The obvious fix was a better library. We drew our own instead, and shipped them on 14 September.
One grid, one angle
Every icon sits on a 24-unit grid with its mass inside the middle 20, so nothing touches the edge. Each one has two states on the same silhouette. The outline, drawn at a 1.75-unit stroke, is an item at rest. The solid fill is the page you are on.
The slanted lines all run at one angle, taken from our logo. The long leg of the Advizr mark measures 56.9 degrees. A rise of 3 over 2 measures 56.3, close enough to read as the same line, so every slanted stroke in the set uses it. It shows in the small arrow that opens a section, which is taller and narrower than the usual 45-degree one.
One motif is rationed. A small spark stands for an agent, and only one item a client sees carries it: Digest, the one page an agent writes for them.
Right as numbers, wrong at 16 pixels
The first round was correct on paper and read badly on screen. Inbox looked like a mask. Brain looked like a horseshoe. Documentation looked like two columns. Integrations looked like a dumbbell. Nothing in the coordinates hinted at any of it.
So every round after that was judged by eye. Each icon was rendered at 16, 20 and 24 pixels and reviewed at device-pixel scale. It took four rounds.
A highlight weaker than a hover
The page you are on now sits on a lit tile. Against the old rail colour that tile measured 1.04 to 1, and the hover state measured 1.09 to 1. The selected item read weaker than an item your mouse happened to cross.
We darkened the rail from 95% to 90% lightness and set hover at 86%. The tile now measures 1.16 to 1 and hover 1.10 to 1. The first build still drew the tile darker than the rail, because one style rule sat in a layer that a utility class overrode. The contrast maths was right. Only a screenshot showed it was wrong.
Darken a surface and everything on it changes
A darker rail re-rates every colour already painted on it. Our muted grey text was tuned against the page, and on the new rail it measured 4.49 to 1 against a floor of 4.5. Three parts of the product carried that grey onto the rail.
Our tests caught two. The keyboard shortcut chip failed the first CI run. The documentation tree failed the second, with 20 elements under the floor.
No test had ever seen the third, the value summary in the rail. In the test setup its data call fails, so it renders nothing. With real data its muted rows sat under the floor. When we faked the data so it rendered, the accessibility checker crashed on its animated number and dropped the contrast rule for the whole page. It reported 0 violations and 0 passes.
Each fix gave the component the rail's own muted ink, measured at 5.18 to 1 on the light rail. A new test renders all three and fails if any of them brings the page's grey onto the rail. Run against the old files, it fails every case.
What is not redrawn yet
The client rail is done. 59 menu entries that only our staff see still use the old set, so our own operator view shows two icon styles for now. That is the next round.
What to ask any vendor
Who looked at your product's icons at the size your team sees them? When a colour in the interface changes, what re-checks everything already painted on it? Can your accessibility test fail on a part of the screen that only renders with real data?
Good answers come with a demo. The insights are free. If you want this level of engineering pointed at your operation, start with the free audit. The plan is yours to keep either way.