Add the provided label/tag icon to the #465 icon mechanism and place it on the Labels tab, the issues-list labels filter, and the labels page #485

Closed
opened 2026-09-13 19:26:49 +00:00 by crueber · 5 comments
Owner

Add the provided label/tag icon to the #465 icon mechanism and place it on the Labels tab, the issues-list labels filter, and the labels page

What's requested

Add the user-provided label.svg (delivered at /tmp/svg-icons/label.svg: 1em × 1em, stroke="currentColor", viewBox 0 0 24 24) to the embedded ICONS map in web/src/lib/icons.jsx under the name label, embedded verbatim per the #465 rules (path/viewBox unmodified, currentColor paint preserved, inline JSX, no runtime fetches — same treatment as the existing 11 entries including the #466 plus).

Then render it on three label surfaces:

Surface File Current rendering Change
Issues-list labels filter trigger web/src/pages/Issues.jsx — LabelsFilter trigger <button> (~line 79, class="input flex w-full items-center justify-between gap-2 …") text-only: summary ("All labels" or the joined selection) + a bare ▾ caret label icon at the LEFT of the summary text, inside the existing flex row (shrink-0); keep the caret and aria-label as-is
Labels page heading web/src/pages/Labels.jsx (route /:owner/:name/labels) — the page heading block text-only heading label icon beside the heading text, composed into the existing heading flow (inline row with gap — do NOT drop it in as its own row; same composition rule as the #481-style additions)
Labels tab web/src/pages/Repo.jsx — the TABS array (~line 162) + the <For each={TABS}> tab-strip render (~line 688) Note: there is no Labels tab today — /labels renders as a tab-less sibling route. Decision point for the implementer (pick one, note it): (a) the labels affordance is reached by adding a Labels entry to TABS with the label icon — but then decide whether icon-bearing tabs generalize (does the icon render for every tab or only ones with an icon defined?) — or (b) leave the tab strip alone and place the icon on the Labels PAGE heading only, treating the "Labels tab" as the labels page's own header. Preference: (a) with the icon?: field optional on the TABS entries so only tabs with icons render one, and the #319 tabBadge + #274 scroll-into-view behavior untouched. If (a) would make Labels the only tab with an icon and look inconsistent, say so and fall back to (b).

Architecture notes

  • The mechanism contract is fixed by #465 (web/src/lib/icons.jsx, landed): mixed viewBoxes scale through width="1em" height="1em"; the .icon utility in web/src/ui.css gives inline-block + no-shrink + baseline alignment; no color literals; size/spacing belongs to the caller's classes. The new label entry follows exactly.
  • The filter trigger is an input-class button: the icon is decorative (aria-hidden="true", which the shared Icon component already sets); the button's existing aria-label/title remain the accessible signal. The min-w-0 flex-1 truncate span stays the truncating element — the icon must not be clipped and must not break the summary truncation.
  • The labels filter dropdown rows already carry a color dot + check per label; this ticket does NOT touch those rows — only the closed trigger.
  • No server changes, no new wire data, no ETag concerns — the icon rides data already in hand (labels:{o}/{r} is already fetched by both pages).

Acceptance criteria

  • ICONS map gains label, embedded verbatim from the provided file (path + viewBox byte-identical in intent, currentColor preserved)
  • The issues-list labels filter trigger shows the label icon left of the summary text, with the existing caret, aria-label, and summary truncation intact
  • The labels page (/:owner/:name/labels) heading shows the label icon composed into the heading flow
  • The Labels tab surface shows the icon per the chosen option (a) or (b), with tabBadge (#319) and active-tab scroll (#274) behavior intact if (a)
  • All decorative icons carry aria-hidden="true"; no accessible name changes text
  • No new network requests; no color literals in the icon layer
# Add the provided label/tag icon to the #465 icon mechanism and place it on the Labels tab, the issues-list labels filter, and the labels page ## What's requested Add the user-provided `label.svg` (delivered at `/tmp/svg-icons/label.svg`: 1em × 1em, `stroke="currentColor"`, viewBox `0 0 24 24`) to the embedded `ICONS` map in `web/src/lib/icons.jsx` under the name `label`, embedded verbatim per the #465 rules (path/viewBox unmodified, currentColor paint preserved, inline JSX, no runtime fetches — same treatment as the existing 11 entries including the #466 `plus`). Then render it on three label surfaces: | Surface | File | Current rendering | Change | |---|---|---|---| | Issues-list labels filter trigger | `web/src/pages/Issues.jsx` — `LabelsFilter` trigger `<button>` (~line 79, `class="input flex w-full items-center justify-between gap-2 …"`) | text-only: summary ("All labels" or the joined selection) + a bare `▾` caret | `label` icon at the LEFT of the summary text, inside the existing flex row (`shrink-0`); keep the caret and aria-label as-is | | Labels page heading | `web/src/pages/Labels.jsx` (route `/:owner/:name/labels`) — the page heading block | text-only heading | `label` icon beside the heading text, composed into the existing heading flow (inline row with gap — do NOT drop it in as its own row; same composition rule as the #481-style additions) | | Labels tab | `web/src/pages/Repo.jsx` — the `TABS` array (~line 162) + the `<For each={TABS}>` tab-strip render (~line 688) | **Note: there is no Labels tab today** — /labels renders as a tab-less sibling route. | Decision point for the implementer (pick one, note it): (a) the labels affordance is reached by adding a `Labels` entry to `TABS` with the `label` icon — but then decide whether icon-bearing tabs generalize (does the icon render for every tab or only ones with an icon defined?) — or (b) leave the tab strip alone and place the icon on the Labels PAGE heading only, treating the "Labels tab" as the labels page's own header. Preference: (a) with the `icon?:` field optional on the `TABS` entries so only tabs with icons render one, and the #319 `tabBadge` + #274 scroll-into-view behavior untouched. If (a) would make Labels the only tab with an icon and look inconsistent, say so and fall back to (b). | ## Architecture notes - The mechanism contract is fixed by #465 (`web/src/lib/icons.jsx`, landed): mixed viewBoxes scale through `width="1em" height="1em"`; the `.icon` utility in `web/src/ui.css` gives `inline-block` + no-shrink + baseline alignment; no color literals; size/spacing belongs to the caller's classes. The new `label` entry follows exactly. - The filter trigger is an `input`-class button: the icon is decorative (`aria-hidden="true"`, which the shared `Icon` component already sets); the button's existing `aria-label`/`title` remain the accessible signal. The `min-w-0 flex-1 truncate` span stays the truncating element — the icon must not be clipped and must not break the summary truncation. - The labels filter dropdown rows already carry a color dot + check per label; this ticket does NOT touch those rows — only the closed trigger. - No server changes, no new wire data, no ETag concerns — the icon rides data already in hand (`labels:{o}/{r}` is already fetched by both pages). ## Acceptance criteria - [ ] `ICONS` map gains `label`, embedded verbatim from the provided file (path + viewBox byte-identical in intent, `currentColor` preserved) - [ ] The issues-list labels filter trigger shows the label icon left of the summary text, with the existing caret, aria-label, and summary truncation intact - [ ] The labels page (`/:owner/:name/labels`) heading shows the label icon composed into the heading flow - [ ] The Labels tab surface shows the icon per the chosen option (a) or (b), with `tabBadge` (#319) and active-tab scroll (#274) behavior intact if (a) - [ ] All decorative icons carry `aria-hidden="true"`; no accessible name changes text - [ ] No new network requests; no color literals in the icon layer
crueber added this to the v1 milestone 2026-09-13 19:26:49 +00:00
Author
Owner

Icon file label.svg (verbatim from the user; 1em, currentColor — missing from the filed body, posted here as the source of truth):

<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
	<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19.293 9.951l-2.333-2.8c-.353-.423-.53-.635-.746-.787a2 2 0 0 0-.632-.295C15.327 6 15.052 6 14.502 6H7.2c-1.12 0-1.68 0-2.108.218a2 2 0 0 0-.874.874C4 7.52 4 8.08 4 9.2v5.6c0 1.12 0 1.68.218 2.108a2 2 0 0 0 .874.874c.427.218.987.218 2.105.218H14.5c.551 0 .826 0 1.081-.069c.226-.06.44-.16.632-.296c.216-.152.393-.363.746-.786l2.333-2.8c.608-.729.91-1.093 1.027-1.5c.102-.359.102-.74 0-1.098c-.116-.407-.42-.77-1.027-1.5" />
</svg>
Icon file `label.svg` (verbatim from the user; `1em`, `currentColor` — missing from the filed body, posted here as the source of truth): ```svg <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"> <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19.293 9.951l-2.333-2.8c-.353-.423-.53-.635-.746-.787a2 2 0 0 0-.632-.295C15.327 6 15.052 6 14.502 6H7.2c-1.12 0-1.68 0-2.108.218a2 2 0 0 0-.874.874C4 7.52 4 8.08 4 9.2v5.6c0 1.12 0 1.68.218 2.108a2 2 0 0 0 .874.874c.427.218.987.218 2.105.218H14.5c.551 0 .826 0 1.081-.069c.226-.06.44-.16.632-.296c.216-.152.393-.363.746-.786l2.333-2.8c.608-.729.91-1.093 1.027-1.5c.102-.359.102-.74 0-1.098c-.116-.407-.42-.77-1.027-1.5" /> </svg> ```
Author
Owner

Note: the icon source files (label) are posted as comments below in ```svg blocks — copy verbatim into web/src/lib/icons.jsx. The body does not contain the SVG markup.

Note: the icon source files (label) are posted as comments below in ```svg blocks — copy verbatim into web/src/lib/icons.jsx. The body does not contain the SVG markup.
Author
Owner

Fix ready for review: PR #492 (#492) — branch fix/issue-485. Tab-icon decision: option (a) in the #484 sense (conditional label icon inside the existing Issues tab under /labels); the literal new-TABS-entry variant was rejected per the issue's own fallback trigger (sole permanent-icon tab + highlight/navigation split). Headless 1073 total / 1071 pass / 2 fail (pre-existing smoke); vite build green.

Fix ready for review: PR #492 (https://git.packden.us/crueber/walhub/pulls/492) — branch fix/issue-485. Tab-icon decision: option (a) in the #484 sense (conditional label icon inside the existing Issues tab under /labels); the literal new-TABS-entry variant was rejected per the issue's own fallback trigger (sole permanent-icon tab + highlight/navigation split). Headless 1073 total / 1071 pass / 2 fail (pre-existing smoke); vite build green.
Author
Owner

Review of PR #492 (fix/issue-485) — verified in scratch worktree /tmp/pr492 (removed afterward); main worktree untouched (still clean). No browser drive per instructions — node tests + compiled-bundle reasoning only, noted explicitly below.

  1. VERBATIM TRANSCRIPTION — PASS (programmatic). /tmp/svg-icons/label.svg vs web/src/lib/icons.jsx label entry: viewBox '0 0 24 24' identical; d string byte-identical (420 chars, m19.293 9.951l-2.333-2.8...); fill=none, stroke=currentColor, stroke-linecap/linejoin=round, stroke-width=2 all preserved; no hex/rgb literal in the entry.

  2. THREE SURFACES — PASS. (a) Issues.jsx:100 left of the summary span inside the existing flex trigger row. (b) Labels.jsx:80 heading 'flex items-center gap-2 ... Labels' — composed into heading flow, not its own row. (c) Repo.jsx:810 conditional inside the existing Issues tab via new isLabelsPath(), only under /labels.

  3. TAB DECISION — option (a) in the #484 sense, JUSTIFIED. A new Labels TABS entry would split highlight from navigation (web/src/lib/tabs.js:21 maps labels→issues, so activeTab never returns a labels id) and would be the sole permanent-icon tab — the issue's own fall-back-to-(b) trigger. Conditional matches the established #484 strip idiom (Issues tab already carries milestone-open under /milestones); the two conditions are mutually exclusive (segs[2] can't be both), TABS stays 7 entries, #319 badge + #274 scroll-into-view byte-identical.

  4. TRIGGER INTEGRITY — PASS. Caret span (aria-hidden shrink-0), button aria-label/title, and the 'min-w-0 flex-1 truncate' summary span all intact; icon is shrink-0 via shared .icon so truncation unchanged. Dropdown rows untouched (menuitemcheckbox roles, color-dot+check idiom, no Icon in menu block).

  5. DECORATIVE — PASS. Shared svg carries aria-hidden=true (icons.jsx:186); no accessible-name changes (tab label text, heading text, button aria-label all unchanged); no per-icon class= on any of the three call sites.

  6. DEPS/DOCS — PASS. No dep-file changes (package.json/pnpm files identical to main; runtime stays solid-js+router+marked+dompurify per law 1). Decision appended to docs/go/12_web_ui.md in the same change (law 12). No core-package or seam changes (law 8); no long-work/task surface (law 7 n/a).

TESTS (scratch, node_modules symlinked from main): full node --test web/test/unit/*.test.js = 1073 total / 1071 pass / 2 fail; the 2 failures are smoke.test.js live-server subtests (needs live Go server + built dist), unrelated to this PR. New label-icon-485.test.js: 9/9 pass. vite build (local pinned binary, from web/): exit 0, label path fragment present in dist/assets/index-BV54Ibmu.js. (Note: repo-rule browser proof stays open — shared-daemon loopback guard; no private daemon used.)

No fixes needed — pushed nothing. MERGE RECOMMENDATION: ready to merge.

Review of PR #492 (fix/issue-485) — verified in scratch worktree /tmp/pr492 (removed afterward); main worktree untouched (still clean). No browser drive per instructions — node tests + compiled-bundle reasoning only, noted explicitly below. 1) VERBATIM TRANSCRIPTION — PASS (programmatic). /tmp/svg-icons/label.svg vs web/src/lib/icons.jsx label entry: viewBox '0 0 24 24' identical; d string byte-identical (420 chars, m19.293 9.951l-2.333-2.8...); fill=none, stroke=currentColor, stroke-linecap/linejoin=round, stroke-width=2 all preserved; no hex/rgb literal in the entry. 2) THREE SURFACES — PASS. (a) Issues.jsx:100 <Icon name="label" /> left of the summary span inside the existing flex trigger row. (b) Labels.jsx:80 heading 'flex items-center gap-2 ... <Icon name="label" />Labels' — composed into heading flow, not its own row. (c) Repo.jsx:810 conditional <Icon name="label" /> inside the existing Issues tab via new isLabelsPath(), only under /labels. 3) TAB DECISION — option (a) in the #484 sense, JUSTIFIED. A new Labels TABS entry would split highlight from navigation (web/src/lib/tabs.js:21 maps labels→issues, so activeTab never returns a labels id) and would be the sole permanent-icon tab — the issue's own fall-back-to-(b) trigger. Conditional matches the established #484 strip idiom (Issues tab already carries milestone-open under /milestones); the two conditions are mutually exclusive (segs[2] can't be both), TABS stays 7 entries, #319 badge + #274 scroll-into-view byte-identical. 4) TRIGGER INTEGRITY — PASS. Caret span (aria-hidden shrink-0), button aria-label/title, and the 'min-w-0 flex-1 truncate' summary span all intact; icon is shrink-0 via shared .icon so truncation unchanged. Dropdown rows untouched (menuitemcheckbox roles, color-dot+check idiom, no Icon in menu block). 5) DECORATIVE — PASS. Shared svg carries aria-hidden=true (icons.jsx:186); no accessible-name changes (tab label text, heading text, button aria-label all unchanged); no per-icon class= on any of the three <Icon name="label"> call sites. 6) DEPS/DOCS — PASS. No dep-file changes (package.json/pnpm files identical to main; runtime stays solid-js+router+marked+dompurify per law 1). Decision appended to docs/go/12_web_ui.md in the same change (law 12). No core-package or seam changes (law 8); no long-work/task surface (law 7 n/a). TESTS (scratch, node_modules symlinked from main): full node --test web/test/unit/*.test.js = 1073 total / 1071 pass / 2 fail; the 2 failures are smoke.test.js live-server subtests (needs live Go server + built dist), unrelated to this PR. New label-icon-485.test.js: 9/9 pass. vite build (local pinned binary, from web/): exit 0, label path fragment present in dist/assets/index-BV54Ibmu.js. (Note: repo-rule browser proof stays open — shared-daemon loopback guard; no private daemon used.) No fixes needed — pushed nothing. MERGE RECOMMENDATION: ready to merge.
Author
Owner

Fixed by PR #492 (review clean — byte-identical transcription, all 3 surfaces, tab decision justified), merged. Closing.

Fixed by PR #492 (review clean — byte-identical transcription, all 3 surfaces, tab decision justified), merged. Closing.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
crueber/walhub#485
No description provided.