A set is a grid with rules, not a sheet of pictures. Below: the anatomy of one cell, optical balance, and an audit of the glyphs against the paint, which is where the field turns out never to have been set and where the centre has drifted. The set was consolidated on 2026-08-09: 52 glyphs and two hands became 35 glyphs and one rule, and section 03 is what the rule turned out to be. 34 the same day, when the two warning marks were decided in favour of the triangle and the circle left with its last placement.
viewBox="0 0 24 24" on the whole set.
Not "about 24": one grid for every glyph is the only thing that lets a new one be drawn
without being held up against its neighbours. This part the set already has, on 5,180
readings, and it is the reason the rest is fixable.2..22. Those two modules
are not emptiness, they are headroom: the glyph stands beside text, inside a button and
inside a circle, and with no headroom it touches the edge of the circle.1.1 of paint past the geometry on every side. The boundary is checked against
the PAINT, not against the curve, or the safe field is eaten by half a stroke and the
audit reads a module too kind.The same bounding box and the same weight are different things. A circle inscribed in the field a square fills looks smaller: the eye counts area, not the rectangle around it. So a set is deliberately not one size, and the four below are the argument, each measured against its paint.
The chevron is the case. It is the second most used glyph in the product and it paints a third of the cell's height. Beside a full-height glyph it does not read as a smaller icon, it reads as a mistake. That is not a drawing to enlarge, it is a decision the set has never taken: either the chevron is drawn to the field like the rest, or the set declares a class of small marks and says which glyphs are in it.
This page used to open here with a defect: two families, 52 glyphs, and four jobs drawn in both. The header carried the proof, because the same bookmark was stroked on one screen and filled on the next at two different inks, for the same meaning. The pairs are below, as they stood.
The instruction was "consolidate the stroked ones into filled", and it cannot be carried out. Not for want of a library: 33 stroked marks were read against Solar Bold and six of them have no filled form at all, because they have no interior to fill. A cross, a chevron, a plus, a tick and a hamburger are movements, not things. What a filled set offers for those is a disc or a plate with the mark knocked out of it, and a disc inside a round icon button is a disc inside a disc.
AND ON 2026-08-13 THE SIX MOVED INTO THE SPRITE, which they could not do
on the day this rule was written. `base.css` said `svg.ic:has(use){fill:currentColor
!important;stroke:none!important}`, which reads as "a mark from the one file is a FILLED mark",
and it was true only because the file held nothing else: a two-stroke drawing arriving through
that rule gets no area and no stroke and paints NOTHING. So the six stayed hand-written in the
markup of 119 documents, 983 of them, which is the exact shape of the defect the sprite
was built to end. The floor asks for #i- now and the line family is
#l-, so a family is a property of the glyph and is carried by its name,
which is the argument backlog 133 used against a class per icon size. It buys one source and
not bytes, and the honest number says so: the markup lost 4,947 and the sprite gained 2,334.
Proof it changed nothing: 238 full-page screenshots over 119 documents at two widths, 0
differing, against a control of 0.
A CELL IN THE GRID BELOW IS 3.18 TIMES LIFE SIZE, and it took a reader being misled to write that down. The cells are 70, 94 and 262px and they let the stroke scale with the box, so a 1.65px mark renders 4.67px here and looks like a heavier drawing than the one the product ships. It is not: the product's mark is the same path at 22px, and in PROPORTION it is 12% heavier than this cell, 7.5% of its box against 6.67%. Anything shown here is a magnification and its weight is constant in device pixels, never in units of the cell. On 2026-08-13 the plus was read off this page as a second, bolder drawing that the product was failing to use, and there was no second drawing.
A line is not an unfilled shape, it is a different kind of mark. Material and SF Symbols draw the same six as lines in their filled sets. An icon set that fills everything has either invented interiors or given up on the cross.
So the rule is: an object is filled, a movement is a line, and WEIGHT holds them together rather than style. 20 filled glyphs over 1,517 placements, 6 line marks over 990. The stroke was 1.6 against the solid mass of Solar Bold, and that gap is what the eye reads as "two sets": it is 2.2 now at 22px and 2.4 at 12px, chosen by standing a line mark next to a filled one at both sizes.
What did not move: the brand marks. Three sign-in providers and five social marks in the footer are logos, they keep their own drawing, and a generic glyph is not allowed to stand in for one, because a paper plane is not Telegram. Two of the four "pairs" this page named turned out to be brand marks caught by a shape census: the second cross was the X mark and the second chat bubble was Discord.
And the licence was being relied on rather than honoured. Fifteen Solar
glyphs had shipped since the Vault pass with the set named in a comment and no attribution
anywhere. Solar is by 480 Design under CC BY 4.0, which asks for exactly that.
It is in NOTICE.md now, in DESIGN.md, and in the comment above the
sprite on all 105 screens. Naming an asset is not crediting it.
the four jobs that crossed the two families, and what each one is now
| job | the stroked twin, until 2026-08-09 | the filled one | now |
|---|---|---|---|
| bookmark | M6 3h12v18l-6-4-6 4z, 88 | i-bookmark-b, 215 | 303 filled |
| bell | M6 9a6 6 0 0 1 12 0..., 35 | i-bell-b, 73 | 108 filled |
| shield | M12 3l7 3v5c0 4.5..., 106 | i-shield-b, 106 | 106 filled, and 106 against 106 was two decisions taken in two places |
| chat | not a twin. The 105 are the Discord mark in the footer | i-chat-b, 9 | 10 filled, and a logo that stays a logo |
The stroked twins are written here and not DRAWN here. This page stood all four of them as live specimens for a day after they left the tree, next to the filled ones, under a caption saying "as they stood until". A stand draws what exists: a reader who scans a grid of glyphs is reading the pictures, not the caption over them, and this one was read exactly that way. The path data is a record. The drawing was an offer.
All four now stand as the filled glyph only, and the stroked twin is gone from the tree. The rule this page proposed, filled for on and stroked for off, was considered and not taken: it is a good rule for a toggle and these four are not toggles, they are a destination, a destination, a claim and a count. A rule that would make the header's bookmark change shape when nothing about it is on or off is a rule about the wrong thing.
One ink per place, and it has to be written twice. A stroked mark takes
its ink from stroke and a filled one from fill:currentColor, so every
rule that paints an icon has to name both or half the set stops listening. Nine
:has(use) rules already existed for the old filled handful and three of them
disagreed with the stroke beside them, which is where the header's two bookmark inks came from.
That is the standing cost of holding two kinds of mark in one family, and it is written in
base.css where somebody will meet it.
THE SET HAS ONE WEIGHT NOW, and the number is the one on screen rather than
the one in the file. --stroke-mark is 1.65 in tokens.css,
paired with vector-effect:non-scaling-stroke on the shapes, so the declared value no
longer passes through the viewBox scale. Measured after: all 1,818 stroked marks in the
painted tree render at 1.65px, a ratio of 1.0, and it is verified against the PAINT, not the
formula - a straight bar screenshotted at device scale 10 and summed by pixel coverage gives
1.65px at boxes 12, 18, 22 and 40. 1.65 was chosen because 1,056 of the 1,818 already rendered
there: it is the default mark at 18, so the set keeps the weight it was drawn against and the
other 762 come to it. The four files that own a stroked mark declare the token; two of them were
hand-computing this number in user units and one had declared nothing at all. Everything below
this paragraph is the record of what it was, and it is kept because the mechanism is worth
knowing. The blow-ups in section 02 are the one exclusion: they carry no
.ic, so they still scale, because a drawing shown at four times its size wants its
stroke at four times too.
.ic declared its stroke once, in user units, inside a 24 unit
box. User units scale with the box. So the line was not that number anywhere: it was that
number multiplied by however large the icon was asked to be, and the set is drawn at six sizes.
The same glyph at all six, four of which the product actually strokes:
The constant moved from 1.6 to 2.2 on 2026-08-09 and the fault did not,
but the spread did, and this page claimed otherwise for a day. It said the ratio was
"exactly what it was, 2.98 to 1, because the fault is the unit and not the value". Re-read on
the 106 painted screens it is 1.68 to 1, and the reason is not that the unit was fixed: it is
that the set no longer strokes at the two boxes that were the extremes. 40 and 20 carry
filled glyphs only now, so the stroked range runs 12 to 22 instead of 12 to 40. An argument
whose evidence has moved is not a smaller argument, it is a different one. What the new
number does fix is the thing it was chosen for: it puts the line marks at the mass of the filled
ones beside them. The figures below are re-read at 2.2, and 12px reads 2.4 because
.ic-sm declares its own. Two of the six sizes below are a demonstration of the
mechanism and not a placement: nothing in the product strokes at 20 or at 40.
Measured 2026-08-09 on the 106 painted screens, every svg that actually paints a stroke, 998 placements of 2,399, in CSS pixels: 1.20px at 12 (266), 1.47 at 16 (17), 1.65 at 18 (528), 2.02 at 22 (178), and the remaining 9 are the market chevron in the paragraph below, at 16 and painting 1.20. Four boxes, not six, and 1.68 to 1 between the lightest and the heaviest. The old reading, kept because it is what the set was before the consolidation: 0.90 at 12, 1.07 at 16, 1.20 at 18, 1.33 at 20, 1.47 at 22, 2.67 at 40, a factor of three. What went with it was the argument's sharp end: 0.90px is under one device pixel at DPR 1 and smears, and 1.20px is not, so the lightest line in the product is no longer sub-pixel. The set still had no optical weight. It had one geometric weight that slid, less far.
AND THE THREE CONSTANTS ARE ONE, since 2026-08-10. They were:
.ic at 2.2 user units, .ic-sm at 2.4 - the smallest box declaring the
thicker number, which after the scale landed at 1.20px, the thinnest line in the product -
and a third this page had never named, .market-chevron writing its own
stroke-width:1.8 in market.css at a 16px box, which rendered 1.20px
as well: the same painted weight from a different number at a different size, in a file the
icon rules do not reach. Two declarations agreeing by accident is not one weight, it is two
that have not disagreed yet. There was a fourth that agreed with nothing:
.tr-ic on the trust strip's own stand declared no stroke
width at all and took the SVG default of 1, rendering 0.92px, under one device pixel at
DPR 1. All four read --stroke-mark now, and the value is in screen pixels because
vector-effect:non-scaling-stroke takes the viewBox scale out of it.
The smallest distance from painted edge to cell edge, for all 35, in
the distribution two paragraphs down. The line numbers all end in .9 because they
are a whole-module contour plus the 1.1 of stroke that paints outside it, which is the
arithmetic of rule 3 showing up in the data. They ended in .2 at 1.6 and this
sentence said 52 and 0.8 until 2026-08-09, one day after the set was consolidated, which is
the same lag the audit below opens by naming.
A method note, because the first pass of this got it wrong. A sweep run
over the live feed reported thirteen glyphs inside the field. It was reading the stroke off
getComputedStyle of the child, and a <use> inside
.ic inherits the stroke width, now 2.2, whether or not it paints a
stroke, so every filled glyph was credited with half of it in paint it does not have and lost a
module of field.
An inherited property is not a painted one, and the fix is to ask whether the element
actually strokes before spending half of it.
Re-measured 2026-08-09 by INK rather than by geometry, and the rule now holds on every filled glyph. Filled, 20 glyphs: field 2.0 on 17, 3.0 on one, 3.3 on two, and none inside the rule. Line, 6 glyphs: 4.9 on two, 3.9 on two, 2.9 on one and 1.9 on the menu, a tenth under, unchanged and confirmed by ink at 2.2. 18 of the 20 filled glyphs are centred at 0.0 / 0.0, the other two off by 0.2 and 0.3. Brand, 8 marks: not measured against this grid, and two of the three providers paint to the cell edge, which is what a logo does. The old reading, kept because it is what the hand-drawn set actually was: stroked 33 glyphs, field 2.2 on 15, 3.2 on 10, 4.2 on 6, 5.2 on 2; filled 15 glyphs, 2.0 on 13.
Two modules is the target and it is not a preference. An icon sits in a 36px circular button at 22px: 2 modules of headroom is 1.8px of air between the paint and the button's own border, and that is already tight. Below 2 the glyph touches. Above 4 it stops reading as the same size as its neighbours, which is the chevron's problem in section 02.
The centre had drifted on twelve glyphs, and section 06 is where that ends
This paragraph is the hand-drawn set's reading and it is kept as one. All three glyphs it sends away to be corrected are gone from the product, and 19 of the 21 filled glyphs now sit at 0.0 / 0.0: the audit below states it and this is the argument it answers. Measured against the geometric centre, in modules. Some of it is right: a shield at -0.5 and a bell at 0.0 are optically centred, which is rule 4 being obeyed. Some of it is not. sort at -2.0 across is a glyph drawn from x=4 with ragged right ends, so it is not optically centred, it is left-aligned inside a centred box. trend-up at -2.0 down and music at -1.7 / -0.8 are the same thing on the other axes. Those three are drawings to correct; the rest are within half a module and are left alone, because a half module at 16px is a third of a pixel and moving it buys nothing.
Every glyph in the product, on its own grid, with its painted extent and its smallest safe field measured in modules on 2026-08-09. Uses are counted across the 106 painted screens. It was 52 glyphs the day before, and the audit that follows is the one this page has always run, taken again after the set was consolidated.
The centre drift this page opened with is gone, and the consolidation is
what removed it. sort at -2.0 across, trend-up at -2.0 down and
music at -1.7 / -0.8 were three drawings to correct. They are not drawings any more:
18 of the 20 filled glyphs sit at 0.0 / 0.0, and the two that do not are off by 0.2 and
0.3 of a module. That is what taking a set from a grid buys, and it is worth more than three
hand corrections would have been.
AND THE WHOLE SET SHIPPED SOLID FOR A DAY, WHILE THIS PAGE DREW IT
CORRECTLY. Solar Bold draws its detail as a KNOCKOUT: the exclamation in the warning
triangle, the tick in the shield, the meridians on the globe, the lens of the magnifier are holes
cut from one path with fill-rule:evenodd. A stroke outlines every subpath including
the holes, and 2.2 units of brass around a 2 unit exclamation closes it completely.
Fifteen component rules paint an icon with stroke: at two classes, which beats
svg.ic:has(use) at (0,1,2), so the product drew blobs where the stand drew glyphs,
because no component rule reaches a specimen. It is stroke:none!important in
base.css now, one of five in the file, and it is a floor rather than a patch. The
stand could not have found this: it is the one defect a stand is structurally blind to.
THE CLOCK NEVER FILLED THE CELL. A MASK IS INVISIBLE TO
getBBox, AND THAT IS THE WHOLE OF BACKLOG 70. This page read
i-clock-circle-b at 24 x 24, field 0 and filed a row to swap the drawing for
a smaller one. Read again by ink, painting each symbol into a canvas at 20x and finding the
opaque pixels, it is 20 x 20, field 2, centre 0.0 / 0.0: the same box as
i-sort-b standing beside it in the same filter row. The glyph was delivered as a
full-cell rectangle behind a mask, <path d="M0 0h24v24H0z" mask="url(#...)"/>,
and getBBox returns the geometry of the path and never the mask. This is the same
defect as the stroke that closed a knockout, one layer up: an instrument that reads the
drawing instead of the paint. It is redrawn as one evenodd path now, Solar's two
published subpaths composed with a fill rule instead of a mask, 0.09 per cent of pixels
different and all of them the antialiased edge of the hand. What went with the mask is a
document-unique id in the sprite, which is backlog 45's family.
And the glyph that WAS breaking the rule is the one the row did not
name. i-magnifer-o painted 21.5 x 21.5 at field 1.23, the only filled
glyph inside the two-module rule, while its caption on this page claimed 19.4 and 2.3. It is
bigger than the set, not smaller: everything else paints 20. The symbol places Solar's own path
at scale(.9294) about the cell centre, which lands it at 20 x 20, field 2.0,
centre 0.0 / 0.0. The path data is unmodified; the placement is ours, and
NOTICE.md says so.
The line marks keep the arithmetic this page named in rule 3. Their
fields all end in .9 because they are a whole-module contour plus the 1.1 of stroke
that paints outside it, which is the same fact that made them end in .2 at 1.6.
The menu is the one under 2, at 1.9, and it is under by a tenth.
Filled, 20 glyphs, Solar Bold through the sprite
Line, 6 glyphs, hand-drawn at 2.2, and not one of them has an interior
.bal-addpaint 16.2 x 16.2, field 3.9FILLED since 2026-08-13, #i-plus-bThe menu left this grid on 2026-08-14, and the badge above became right
by accident. l-menu was the sixth figure here and the fifth line mark, and it
went with the control it was drawn for: the header's hamburger was a
<button> labelled "Menu (reserved for future scaling)" with no handler and no
drawer, display:none below the desk, removed from 105 painted screens, 87 grey ones
and 10 kit specimens in one pass. A symbol whose only remaining reader is the page that
documents the sprite is a glyph with no placement. The sprite is 34 symbols, 30 filled and 4
line, and the "34 glyphs" badge above had been one short against 35 for as long as it has stood,
disagreeing with the two badges beside it. Its field readings are kept in the measurement
paragraphs on this page rather than deleted, because those are a record of the set as it
stood on their own date, and the menu is the mark the 2 rule was under by a tenth.
Brand, 8 marks, drawn by their owners and not measured against this grid
52 glyphs and 45 jobs became 35 glyphs on 2026-08-09, and 34 by the end of the day. Four pairs crossed the two families, three sat inside the stroked one, and the eighth is below. None of them was a variant with a stated purpose; each was a second drawing of the same idea, made on a different day. The table is kept as it was read, and the answer is now in the last column of each row.
Two of the eight were not pairs at all, and a shape census cannot tell. The "second close" is the X brand mark in the footer and the "second chat" is Discord. A census that reads geometry and not meaning will file a logo as a duplicate of a control every time.
And the eighth is the mirror of that, which is why the census missed it entirely. The seven above were found by shapes that RESEMBLE each other. A circle and a triangle do not, so the two warning marks read as two different glyphs doing two different jobs, and they sat under the same sentence on twenty screens for weeks. A shape census can only find a duplicate that looks like one: this one was found by reading what the mark stands next to, "Couldn't load", sixteen times in a circle and four in a triangle.
M6 6l12 12M18 6L6 18 348 / M5 5l14 14M19 5L5 19 4#l-close in the sprite along with the other five movements. The pass that moved them swallowed this one first, because two drawings that look alike read as drift, and the page that had already decided is what caught itM16 16l5 5 outward / M20 20l-4-4 inwardi-magnifer-o now, one filled glyph over 6 placementsr=9 with hands 7v5l3 2, 44 / r=8 with hands 8v4l3 2, 1i-clock-circle-b, 45 placements. Two circle radii for one job was the clearest case on the page of a set with no grid disciplinei-bookmark-b, 215i-bell-b, 73i-shield-b, 106i-chat-b, 9i-danger-circle-b, 16 / i-danger-triangle-b, 4Rule. One cell, one field, one weight. viewBox="0 0 24 24"
on every glyph; paint in 2..22, checked against the paint; the stroke reads a token
and lands on a whole module. Anti-rule. An icon is never the only name of an action, and
a glyph is never redrawn because the old one looked slightly wrong in one place. That is how a
set gets seven jobs and eight drawings.
Rule, 2026-08-09. An object is filled and a movement is a line, and weight is what holds them together. A bell has an interior and a cross does not, so the split is a fact about marks rather than a preference between styles. Solar Bold for the first kind, a hand line at 2.2 for the second.
Rule, 2026-08-09. Every rule that paints an icon names both
stroke and color. The set has two kinds of mark and they read
different properties, so a rule that names one paints half of what it is pointing at.
Rule, 2026-08-09. A brand mark is not in the system. It keeps its own drawing, takes no system ink, and no generic glyph may stand in for it.
Never take a licence by naming the set. Fifteen Solar glyphs
shipped for weeks under a comment that said "Solar Bold icon sprite" and no attribution
anywhere. CC BY asks for the author, the licence and whether it was changed, and a comment
naming the vendor is none of the three. NOTICE.md.
What goes into the consolidation
.ic, 2.4 on
.ic-sm and 1.8 in market.css, are what make the line run 1.20 to
2.02 over four boxes. The fix is a stroke per rendered size, so 12, 16, 18 and 22 all paint
the same weight on screen, and the file that writes its own goes with it..ic-sm is deleted as a size. It is the smallest box
carrying the thickest user-unit stroke, which is the exact inversion of what a small glyph
needs, and it exists only because nobody had measured the rendered result. Re-read
2026-08-09 it is not even a size: of 358 placements, 354 are 12px and four are not,
all four on the toast, where toast.css rewrites the box to 18 and the stroke to
2.2, and rewrites it again to 22 and 2.4 on the error face. A class that a component
re-declares from scratch is a name the markup carries, not a size the system owns.