Skip to content
IconMind

Documentation

Design guidelines

The rules every icon follows, the vocabulary they share, and the measurements behind both.

Icons here are not drawn in a vector editor. Each one is a TypeScript declaration — a list of shapes on a 24-unit grid — and the build compiles it into all six cells: outline and duotone, across thin, regular and bold. The shape constructors throw on illegal geometry and pnpm icons:validate checks thirty-odd rules in under five seconds, so everything mechanical on this page is enforced by machine. What follows is the reasoning, which a validator cannot give you.

The grid

Canvas24 × 24, viewBox="0 0 24 24"
Anchorsinside 2..22, multiples of 0.5
Strokeround cap and join; 1.5 / 2 / 2.5 by weight
Fillnone — duotone adds a 20% tint, never a solid
Anglesmultiples of 45°
Sizean icon's longer side spans 18–22 units
Optical centrewithin 2 units of the canvas centre
Crossingsat most 2
Stroke gaps≥ 2.5 units apart (3 at bold), runs ≥ 2.5

Ink reaches 1..23 because a 2px stroke bleeds 1px past the axis.

Why the live area is that wide

The first version of this set capped anchors at 3..21, which limits any shape to a 20-unit span. Measured against comparable sets, ours reached 20 where theirs reached 22 — every icon was 10 to 26% smaller inside the same frame, and shapes that do not fill their frame read as timid.

Widening it also settled the stroke question. At 2px the set averages 28.7% ink coverage, which is where established sets sit. Pushing to 2.5px took seven of ten icons past 34% and turned them to mush at 16px. The confidence came from scale, not from a heavier line.

Why a weight is a drawing, not a multiplier

Thin, regular and bold share their geometry — the build proves it — but the rules tighten with weight: a gap that reads at 1.5px closes up at 2.5px, so the validator's minimum gaps grow with the stroke. Drawing once and checking three times is what keeps bold from becoming a smudged copy of regular.

The shared bodies

A concept's outer shape is decided by what kind of thing it is, and the bodies live in one file (scripts/draw/bodies.ts) so families cannot drift apart:

BodyMeaning
Ring (gapped circle)An agent — autonomous, has state
Chamfered frameA machine or MCP server — gets invoked
Plug with prongsA tool or capability
Page with a folded cornerA document or resource
CylinderStored data
ShieldSecurity
Window frameAn interface, a page, a sandbox
Square loop with a chevronRotation and repetition — refresh, retry, lifecycle

The mark inside the body says which member of the family this is: agent is the bare ring, agent-run holds a triangle, agent-stop a square, mcp-tool-call puts a small arrow in the plug's hollow. The body is byte-identical across a family — that is what makes the family read as one.

Corners and terminals

Caps and joins are round. Corner radius follows the size of the shape carrying it: 2 units at 8 and over, 1 below, because a 2-unit radius on a 5-unit box leaves almost no straight side and the shape stops reading as a box.

An earlier version cut every container's corners at 45° instead, as a house signature. It was dropped. A signature made of a corner treatment governs the container and then fights the metaphor — vector-database drawn with cut corners stopped reading as a database at all. The set is distinguished by its vocabulary and by having six cells where most sets have one, not by a cut that has to be argued for on every shape.

Duotone is derived, not drawn

Nobody draws the duotone cell. The build tints every closed shape at 20% opacity behind the strokes; an icon made only of open strokes gets a halo instead — the same paths echoed behind it, three units wider, at the same 20%. Small closed marks (a status dot) tint without suppressing the halo. One drawing, and the second variant can never disagree with the first.

Make the inner mark large

This is measured, not taste. The first agent-thinking was a circle holding three 2px dots, and agent was the same circle holding one — perceptually 0.011 apart, which is indistinguishable. Enlarging the dots to 4px and spreading them across 40% of the width moved it to 0.059.

A small mark in the middle of a large shape distinguishes nothing at 16px, however different it looks at 48px.

What actually kills legibility at 16px

Not element count. An icon with five elements stays sharp as long as its strokes do not cross; one with four elements and four crossings becomes a dark blot. That is why max-crossings is a rule and the twelve-element limit is left loose.

The other killer is fusion: two strokes closer than the gap minimum melt together at bold. When a composition will not fit — two arrowheads and a shaft between two bodies, say — the answer is to redesign the layout (route the arrows around the middle), not to shave the gaps.

Before you open a pull request

Four things the validator cannot judge:

  1. Can you guess the meaning without a label?
  2. Does it sit with its neighbours in the same subcategory?
  3. Is it readable at 16px?
  4. Does the outer shape match the entity type?