Skip to content

feat: six icon glyphs + nuri mark at 22/32 — implements nuri-com/nuri-design-system#230 - #236

Merged
darioaschero merged 1 commit into
mainfrom
feat/icons-230
Oct 29, 2026
Merged

darioaschero merged 1 commit into
mainfrom
feat/icons-230

Conversation

@darioaschero

Copy link
Copy Markdown
Collaborator

Implements #230: six icon glyphs (address-book, id-card, selfie, lock, lock-open, phone) and the nuri mark redrawn at 22/32.

  • Admission: decision, Extension, sole-architect exception, with the independent review and the amendment after it.
  • Ready: Project Ready was confirmed by the operator in chat on 2026-10-29. The gh token has no read:project scope, so this PR does not verify it.
  • Base: origin/main at f28e14c.

Contract delivered

  • IconName gains exactly six names. The RN union and the web registry key set both go from 39 to 45, and they are identical to each other:

    RN IconName: 39 -> 45      + address-book  + id-card  + lock  + lock-open  + phone  + selfie
    web ICONS keys: 39 -> 45   + address-book  + id-card  + lock  + lock-open  + phone  + selfie
    RN key set ≡ web key set: identical
    values changed among the 39 existing keys (RN and web): nuri
    RN value ≡ web value for all 45: true
    
  • nuri is the operator's 22/32 file: the letters are unchanged and the gap between them is 2px instead of 4. The old mark spanned 4–28; the new one spans 5–27.

  • The seven files are the operator's exports from prompts/icons-230/ (2026-10-29), copied unchanged. The sha256 of each committed file equals its source.

  • Anti-scope respected:

    • no user, camera, globe, iban-globe, tourism or users; no operator or brand marks;
    • no component, avatar, schema, token or parser change; no doc-page change; no card board; no nuri-expo change.

Intake check (acceptance criterion 1)

The check parses every d attribute into absolute coordinates, counting endpoints and Bézier control points. A Bézier curve lies inside the hull of its control points, so this box bounds the drawn glyph. It also lists every element in the file and every stroke* attribute. The sources use the commands M, L, H, V, C and Z only, with no arcs.

File viewBox stroke= Elements Box (x · y) 4–28
address-book.svg 0 0 32 32 none svg + 3 path 2–28 · 2–30 no (known deviation, see below)
id-card.svg 0 0 32 32 none svg + 6 path 4–28 · 4–28 yes
selfie.svg 0 0 32 32 none svg + 3 path 4–28 · 4–28 yes
lock.svg 0 0 32 32 none svg + 3 path 6–26 · 4–28 yes
lock-open.svg 0 0 32 32 none svg + 3 path 6–26 · 4–28 yes
phone.svg 0 0 32 32 none svg + 3 path 8–24 · 4–28 yes
nuri.svg (new) 0 0 32 32 none svg + 4 path 5–27 · 5–27 yes

A grep cross-check agrees: grep -c stroke gives 0 for all seven files, and the only tags are 7 <svg and 25 <path.

Known deviation: address-book is drawn at 2–30. The operator decided on 2026-10-29 that it goes in as drawn and is not rescaled here. This conflicts with acceptance criterion 1 and with the accepted contract's line "the glyph inside the 4–28 box". At 24px it reads slightly larger than its neighbours on the sheet. The correction is follow-up 1.

Changes

Area File(s)
Spec packages/spec/icons/: 6 new SVGs, and nuri.svg replaced
Generated packages/rn/generated/data/icons.ts · packages/prototype/generated/icons.js. No other generated file changed: packages/doc/generated/, packages/playground/generated/ and packages/prototype/styles/ have no diff.
RN tests render-smoke.test.tsx: the six names join the targeted NuriIcon mount list (the #131 / #186 pattern). The two snapshots that embed the mark are updated.
Playground New pages/component/icon.html, the name ↔ drawing sheet, plus its card in component/index.html
Boards send.html: All contacts list-bullets → address-book (three boards); Mobile money wallet → phone. send-wip.html: bank · activate copy → id-card and eye → selfie; bank · recipient · locked: the outline bank avatar → lock, still outline.
MISFIT logs send.html: the Mobile money line is rewritten as resolved, and a resolved All contacts line is added. send-wip.html: the locked-row line and the ID-document / selfie line are rewritten, the latter replacing the old camera (or user) suggestion. No other line in either log mentioned the dropped portrait or user glyph.

Snapshots. Exactly two snapshots changed, 5 lines each, and only in the mark:

  • IconAvatar — the inverse accessory equals the neutral accent solid…
  • ListAction — leadingAvatar forwards initials + accessory + accessorySurface…

In each, the 4 changed lines are the mark's four RNSVGPath d values and the fifth is the SvgXml xml string that carries the same markup. In that string, the source puts fill-rule / clip-rule after d on the R path; the parser keeps source attribute order. The parsed fillRule props did not change.

The icon sheet (component/icon.html):

  • Boards: three boards, 24px (<nuri-icon> md), 18px (size="sm") and 15px. All 45 names are in registry order, with each glyph next to its name, and theme and accent come from the board controls.
  • 15px: the DS has no 15px icon size. size.2xs is the IconAvatar accessory leaf from DS admission: IconAvatar initials + corner accessory (+ ListAction forwarding) #231, so the 15px board shows each glyph where the DS draws it at 15px: as the accessory over the same initials="EB" avatar. An avatar with no content was tried first and warns ("expects exactly one of icon, source or initials").
  • Pure DS markup: no page-local CSS, no script.
  • Hand-written list: a check script compared each board with the registry: 45 glyphs per board, identical to the registry keys in order, and each caption equals its glyph name. No gate runs that check (follow-up 2).

Verification (commands and output)

All commands were run in a fresh worktree (origin/main at f28e14c plus npm ci) on the source tree committed as 7d69f3b.

$ node --test scripts/*.test.js
# tests 128 · pass 128 · fail 0

$ npm test -w @nuri/prototype
# tests 130 · pass 130 · fail 0

$ npm test -w @nuri/doc
# tests 3 · pass 3 · fail 0

$ npm test -w @nuri/rn
Test Suites: 21 passed, 21 total
Tests:       308 passed, 308 total
Snapshots:   26 passed, 26 total        # after the 2 nuri snapshots were updated (-u); none written

$ npm run typecheck -w @nuri/rn          # tsc → exit 0
$ npm run typecheck -w @nuri/expo-demo   # tsc → exit 0

Before the snapshot update, npm test -w @nuri/rn failed exactly 2 snapshots (Tests: 2 failed, 306 passed). The new six-name mount test passed on the first run.

Deterministic re-emit. I ran the full build chain twice: node scripts/tokens-parser.js, then npm run build, then the @nuri/prototype, @nuri/playground and @nuri/doc builds. After each run I hashed every file under packages/{rn,prototype,doc,playground}/generated, packages/prototype/styles and packages/spec (169 files):

build 1 manifest sha256: ee0afd1f7aaa4cc03593a8c0deaa38c3df4171a63d392a277b66fc98a7fd8fec
build 2 manifest sha256: ee0afd1f7aaa4cc03593a8c0deaa38c3df4171a63d392a277b66fc98a7fd8fec
→ IDENTICAL across two builds

git diff --exit-code packages/prototype/styles/       → clean
git diff --exit-code packages/playground/generated/   → clean
git diff --exit-code packages/doc/generated/          → clean
git diff packages/rn/generated packages/prototype/generated → only data/icons.ts and generated/icons.js

Rendered proof (web playground, light and dark)

The pages were served statically from the worktree and captured with headless Chrome at device scale 2. Each board's theme was switched with its own toolbar control. No console warnings or errors were logged on any captured page.

Name ↔ drawing check: every glyph renders its own drawing. I compared each of the six new names and nuri with its source file at 24, 18 and 15px, in light and dark. The 15px check used 3× crops of the accessory cells, which are not committed.

Name Drawing
address-book a ring-bound book with a person
id-card a card with a person and two lines, a bar above it and a notched bottom edge, as in the source paths
selfie a person inside viewfinder corners
lock a closed padlock
lock-open the padlock with its shackle open
phone a handset with a speaker and a home bar
nuri NU over RI, with a narrower gap

The other 38 names show the same drawings as before; no other value changed in the registry.

Evidence (docs/evidence/icons-230/, at 7d69f3b):

Notes

  • The locked row keeps variant="outline". Only the glyph was swapped, bank to lock. Whether the locked row should also change variant is the operator's call.
  • Registry order puts lock-open before lock. The filename sort places - before .. The sheet follows registry order, and so do the union and the key set.
  • One caption wraps. On the 15px board, "transfer-horizontal" wraps to two lines at the default iPhone 17e width, because the 48px avatar narrows the cell. This is cosmetic.
  • How the captures were made. The existing preview configs serve the main checkout, which I did not touch. The worktree was served with a temporary http-server on port 8791, and both helper processes were stopped afterwards.

Not verified

  • RN-web, native device or simulator. There is no RN-web or native render of the six names or of the new nuri mark: expo-demo Home.tsx (topbar mark), the toast, and gen-ui were not rendered. On RN the proof is jest only: the six names mount through NuriIcon and their register markup reaches SvgXml, and the two nuri snapshots were updated. The issue's plan lists "the six names mounted on RN-web, light and dark". That step was not run.
  • Toast and gen-ui placements of the mark. These were not screenshotted. The registry value is shared, so they show the same drawing, but no capture was made.
  • Optical review. I checked name ↔ drawing, not weight or balance. The address-book size deviation is visible on the sheet and is left to follow-up 1.
  • Project Ready. It is not verifiable with this gh token (no read:project scope).
  • Independent verification. This is the implementer's run. Independent verification by another session is still required before Verified/Done, and this PR does not self-certify it.

Follow-ups

  1. address-book into the 4–28 box. The operator re-exports it (phone's ratio, same height). That restores the accepted contract's "inside the 4–28 box", so it is a Correction under the policy. It is one SVG plus a regen, with no snapshot change because no snapshot embeds address-book.
  2. Parser intake assertion (internal maintenance item, not this admission). scripts/parsers/icons.js should fail loud on stroke= and on any element other than <path>. Today both pass silently: fill="none" is rewritten to currentColor, and other elements are dropped. It could also check the 4–28 box and that component/icon.html lists every registry name.
  3. Release. Add the release-note line "six new glyphs: address-book, id-card, selfie, lock, lock-open, phone; the nuri mark redrawn at 22/32" in the next rn/v0.x.y tag. Tagging is the operator's.
  4. nuri-expo adoption. That comes after the vendoring sync, as a separate consumer PR. It covers the card freeze with lock / lock-open, which is unverified from DS inputs, and the send boards' glyphs.

🤖 Generated with Claude Code

Implements the contract admitted in
#230 (comment) (sole-architect
exception · Project Ready confirmed by the operator). The seven drawings are the operator's exports
of 2026-10-29, copied unchanged.

Spec
- icons: address-book, id-card, selfie, lock, lock-open, phone added; nuri.svg replaced by the
  22/32 redraw (letters unchanged, 2px gap). Intake per file: viewBox 0 0 32 32, no stroke=,
  <path> only, coordinates inside 4–28. address-book is drawn 2–30 by the operator's decision: a
  known deviation from acceptance criterion 1, with a follow-up.

Codegen
- regenerated: packages/rn/generated/data/icons.ts (IconName 39 → 45) and
  packages/prototype/generated/icons.js (45 keys). No other generated file changed; nothing under
  packages/doc/generated/.

RN tests
- render-smoke: the six names join the targeted NuriIcon mount list (the #131 / #186 pattern).
- the two snapshots that embed the mark (IconAvatar inverse accessory, ListAction leadingAvatar)
  are updated; only the mark's four path `d` values and the SvgXml `xml` string changed.

Playground
- new pages/component/icon.html: every registry name at 24px (md), 18px (sm) and 15px (the
  IconAvatar accessory, the only 15px placement), one board per size; theme and accent from the
  board controls. Card added to component/index.html.
- send.html: All contacts (three boards) list-bullets → address-book; Mobile money wallet → phone.
- send-wip.html: bank · activate copy → id-card, eye → selfie; bank · recipient · locked: the
  outline bank avatar → lock (outline). MISFIT lines rewritten.

Evidence: docs/evidence/icons-230/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@darioaschero
darioaschero merged commit e1dbb18 into main Oct 29, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant