feat(icons): Sync the icon set with Figma - #2161
Merged
Merged
Conversation
🦋 Changeset detectedLatest commit: 93b898e The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
ivans-netto
force-pushed
the
feat/icons-figma-sync
branch
5 times, most recently
from
August 19, 2026 16:59
6db5737 to
478c0fd
Compare
juliajforesti
previously requested changes
Aug 19, 2026
ivans-netto
force-pushed
the
feat/icons-figma-sync
branch
from
August 19, 2026 18:12
478c0fd to
e20118e
Compare
`writeJson` returned the data without awaiting the underlying write, so `await writeJson(...)` in `nextCharactersFor` resolved before the file was actually on disk and the next reader could still observe the previous content. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`createSvgBuffer` calls `nextCharactersFor` for every icon in parallel, and each call is a read-modify-write over `glyphsMapping.json`: read the mapping, derive the next free character from the current maximum, write the mapping back. Concurrent calls read the same state, derive the same "next" character, and the last write clobbers the entries of the others - adding N icons at once persisted only one of them and assigned duplicate codepoints. `stacked-meatballs` and `rocketchat` share U+E147 in `main` because of this. Chain the assignments on a module-level promise so they run one at a time and each observes the previous one's result. The chain swallows rejections so one failed assignment cannot wedge the ones that follow, while the caller of the failed assignment still receives the original rejection. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Glyphs were written into the font stream from inside a `Promise.all`, so the write order followed I/O completion order and varied between builds. For the duplicated codepoint U+E147 (`stacked-meatballs`/`rocketchat`) that made the font itself non-deterministic - which glyph won the codepoint changed from build to build, flipping both grid cells in the Icon story and making the visual-regression check bistable (fail with exactly 398 differing pixels, or pass, on identical input). Read the sources in parallel but write the glyphs sequentially in the already-sorted icons order. The generated SVG font is now byte-identical across builds; U+E147 consistently renders `stacked-meatballs`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`buildFont` (which assigns characters to new icons) ran in the same `Promise.all` as the module builders (which read the mapping), so the first build after adding an icon silently left it out of the generated `index.js`/`index.mjs`/`index.d.ts` while still emitting it into the SVG sprite; only a second build picked it up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All bench icons that were not already published, plus the icons documented on the "Fuselage" frame that never landed in the package (`arrow-left`, `arrow-right`, `arrow-up-down`, the chevron `-big` family, `jump-backward`/`jump-forward` and `attachment-file-filled`), after deduplicating against the existing set: - `pop-out-window`, `close-pop-out-window`, `drag-handle` and `circle-unfilled-medium` were not added - they render identically to the published `arrow-to-square-box`, `arrow-from-cross-box`, `stacked-meatballs` and `circle-unfilled`. - `hd-stroke`/`hd-fill` landed as `hd`/`hd-filled`, following the `star`/`star-filled` naming convention. - `tag-horizontal-plus-2` was dropped by design. - The Figma components were renamed/removed accordingly. `folder-plus`, `note`, `sidebar`, `arrow-left`, `arrow-right`, `chevron-left-big` and `chevron-right-big` are `.dir` (RTL-mirrored) to match their existing counterparts. `jump-backward`/`jump-forward` are not mirrored so their `15` numeral keeps its reading direction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Geometry only - no icon is renamed or removed. The previous versions were drawn on an older grid and carried off-grid coordinates (`hash` sat on half units, `video` on 0.444 units); the redraws are pixel-aligned on the 32x32 box. `hashtag-lock` takes the same redrawn geometry as the new `hash-lock`, and `attachment-file` the same redraw as the new `attachment-file-filled`, so each concept renders in a single style. The phone family and `pin` are marked `[redraw]` in Figma too, but their redraws already shipped in #1735/#1882 - the exports differ from the package only by sub-0.001px float rounding, so they are untouched. The corresponding components on the Iconography Figma file's "Fuselage" frame were replaced with the same redrawn geometry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Júlia Jaeger Foresti <60678893+juliajforesti@users.noreply.github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Icon grids pick up the added and redrawn glyphs, and the Tag, SidebarV2, Sidepanel and Sidebar stories render redrawn icons. Regenerated in the pinned playwright:v1.62.0-noble image so the output matches CI; 506/506 pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ivans-netto
force-pushed
the
feat/icons-figma-sync
branch
from
August 19, 2026 18:33
2a91a4d to
eebbe78
Compare
juliajforesti
approved these changes
Aug 19, 2026
|
Tick the box to add this pull request to the merge queue (same as
|
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Task
Brings the
@rocket.chat/iconsset in sync with the Iconography Figma file, which is the design source of truth:arrow-left,arrow-right,arrow-up-down, the chevron-bigfamily,jump-backward/jump-forwardandattachment-file-filled).[redraw]-prefixed) versions from the same frame — in the package and on the Figma "Fuselage" frame, whose corresponding components were replaced with the same geometry.build-iconsthat made adding more than one icon at a time unsafe.All geometry is taken straight from Figma and reduced to the package's convention (
viewBox+xmlnsonly,<path>elements keeping justd/fill-rule/clip-rule). Nothing that exists inmainis renamed or removed, and no codepoint changes — for consumers this release is purely additive plus visual refreshes.Visual overview
Added (36)
archivearrow-leftarrow-rightarrow-up-downattachment-file-filledbell-dotchevron-down-bigchevron-left-bigchevron-right-bigchevron-up-bigclosed-captionsfolder-plushandhash-lockhdhd-filledimage-resizejump-backwardjump-forwardmovenotepruneresizesearch-sparklesidebarsquare-arrow-down-leftsquare-arrow-up-rightsquare-arrows-contained-inwardssquare-arrows-contained-outwardssquare-slashstar-plustag-horizontaltag-horizontal-filledtag-horizontal-plusvoicemailvolume-question-markfolder-plus,note,sidebar,arrow-left,arrow-right,chevron-left-bigandchevron-right-bigare.dir(RTL-mirrored) to match their existing counterparts.jump-backward/jump-forwardare not mirrored so their15numeral keeps its reading direction.Updated (18)
arrow-expandattachment-filefolderhashhashtag-lockmicmic-offrecteamteam-lockvideovideo-disabledvideo-messagevideo-offvolumevolume-disabledvolume-lockvolume-offGeometry only. The previous versions were drawn on an older grid and carried off-grid coordinates; the redraws are pixel-aligned on the 32×32 box. For example
hashmoved off half units (10.5→10) andvideooff0.444units.The phone family (
phone,phone-disabled,phone-in,phone-issue,phone-off,phone-out) andpinare marked[redraw]in Figma too, but their redraws already shipped in #1735/#1882 — the bench exports differ from the package only by sub-0.001px float rounding (or not at all), so they are untouched. Verified numerically: same coordinate count, max deviation 0.0003px.Figma updates applied alongside this PR
[redraw]bench icon were replaced with the redrawn geometry (phone×6,team×2,hash,hashtag-locked,arrow-expand,mic×2,volume×4,pin,video×4,folder), so the library and the package stay in sync.stacked-meatballs,circle-unfilled,arrow-to-square-box,arrow-from-cross-box,rec,hd,hd-filled);tag-horizontal-plus-2was deleted.Deduplication against the published set
A pairwise shape-similarity sweep across all 356 SVGs (exact pixel IoU + a shift-tolerant pass), reviewed with design, caught seven bench icons that duplicate already-published ones. Resolution, validated by design:
pop-out-windowarrow-to-square-box(#1958)close-pop-out-windowarrow-from-cross-box(#1958)drag-handlestacked-meatballs(#1626)circle-unfilled-mediumcircle-unfilledrec-unfilledrecrecupdated to the redrawn geometry; Figma component renamedvideo-arrow-up-right/video-arrow-down-lefthash-lockhashtag-lockhash-lockadded as the canonical name andhashtag-lockupdated to the same geometry, so both names render identicallyOther renames applied for convention (
star/star-filledpattern):hd-stroke/hd-fill→hd/hd-filled, in Figma too.tag-horizontal-plus-2was dropped by design and its Figma component deleted.Pre-existing near-duplicates that are not touched by this PR, flagged for a future pass:
list-bullets×medium-view(~97% overlap) anderror-circle×circle-cross(same concept, filled/outline pair with non-standard names).The
build-iconsfixesAdding more than one icon at a time was unsafe. Three separate commits, one per cause:
Await the
glyphsMapping.jsonwrite —writeJsonreturned its data without awaiting the underlying write, soawait writeJson(...)resolved before the file hit disk and the next reader could still observe the previous content.Serialize glyph character assignments —
createSvgBuffercallsnextCharactersForfor every icon in parallel, and each call is a read-modify-write overglyphsMapping.json: read the mapping, derive the next free character from the current maximum, write it back. Concurrent calls read the same state, derived the same "next" character, and the last write clobbered the others' entries — adding N icons at once persisted only one of them and assigned duplicate codepoints. I hit this directly: the first build of this branch persisted 1 of 34 entries. The fix chains the assignments on a module-level promise, so they run strictly one at a time and each observes the previous one's result:The
catchkeeps the chain alive when an assignment fails — otherwise one rejection would wedge every later assignment — while the failed call's own caller still receives the original rejection, sinceassignmentis returned before thecatch.Write font glyphs in deterministic order — glyphs were written into the font stream from inside a
Promise.all, so write order followed I/O completion order and varied between builds. For the pre-existing duplicated codepoint U+E147 (stacked-meatballs/rocketchat) that made the font itself non-deterministic: which glyph won the codepoint changed from build to build, flipping both cells in the Icon grid story — this is why the visual-regression check was bistable on this PR (failing with exactly 398 differing pixels, or passing, on identical input). Sources are still read in parallel, but glyphs are now written sequentially in the already-sorted icons order; the generated SVG font is byte-identical across builds and U+E147 consistently rendersstacked-meatballs.Assign characters before generating the modules —
buildFont(which assigns characters to new icons) ran in the samePromise.allas the module builders (which read the mapping), so the first build after adding an icon silently left it out ofindex.js/index.mjs/index.d.tswhile still emitting it into the SVG sprite; only a second build picked it up.A single
yarn buildnow assigns everything and is idempotent.Note
This bug already bit
main:stacked-meatballsandrocketchatboth map to U+E147. I left that alone here because changing either codepoint is a breaking change for anyone rendering the font directly — happy to fix it in a follow-up, just needs a call on which one moves.Verification
yarn buildinpackages/iconssucceeds; all 36 new icons resolve inindex.js,index.mjs,index.d.ts,dist/icons.svganddist/svg/.mainwas removed and no codepoint that exists inmainchanged (verified by diffingglyphsMapping.jsonagainstmain).glyphsMapping.json(idempotent).hash-lockandhashtag-lockrender byte-identically in the build output..diricons and the 2 modified.diricons carryrcx-svg--directionalin the build output.dist/svg/output and visually checked; the redraws were also compared against the current versions pixel-by-pixel to confirm each difference is a real design change rather than export noise.Icongrids,Tag,SidebarV2/Item,SidepanelandSidebarstories). Regenerated in the pinnedplaywright:v1.62.0-nobleimage so they match CI; the full suite is 506/506 green.yarn lintpasses forpackages/icons—eslint,stylelint, andprettier(which does check SVGs here, via@prettier/plugin-xml).🤖 Generated with Claude Code