Skip to content

feat(ui): integrate shared/ui primitives into widgets/pages (#82 #83 #84)#85

Merged
fedorovvvv merged 12 commits into
developfrom
feature/prd-018-sc2-integration
May 7, 2026
Merged

feat(ui): integrate shared/ui primitives into widgets/pages (#82 #83 #84)#85
fedorovvvv merged 12 commits into
developfrom
feature/prd-018-sc2-integration

Conversation

@fedorovvvv
Copy link
Copy Markdown
Collaborator

@fedorovvvv fedorovvvv commented May 7, 2026

Closes #81, #82, #83, #84.

PRD-018 SC-2 follow-up integration: replace ad-hoc atom CSS with @/shared/ui primitives across the SvelteKit app. EvidencePack EVID-023 closes the PRD's SC-2 ("deferred" → "pass"); forgeplan score PRD-018 → R_eff = 1.00 (Adequate).

What changed

11 commits, 9 files modified.

Commit Scope Primitives used
434e179 widgets/version-footer/UpdateButton.svelte Button
48124be widgets/mosaic/PaneFrame.svelte (3 pane controls) Button
f8de23c pages/home/HomePage.svelte (error bar) Alert + Button
c2c830b widgets/artifact-filters/Filters.svelte (KIND/STATUS chips) Toggle
0965bed widgets/health-bar/HealthBar.svelte (theme switcher + notify) ToggleGroup + Toggle
2b07516 widgets/insights-rail/InsightsRail.svelte (tab badges + R_eff bars) Badge + Progress
a2d3cc0 widgets/artifact-panel/ArtifactPanel.svelte (kind chip + 5 ghost buttons + close) Badge + Button × 6
<sha> widgets/{health-bar,artifact-filters,timeline}/* alignment fixes — match /playground reference ToggleGroup + Toggle (corrected styling)
<sha> widgets/artifact-filters/Filters.svelte — outlined chips with spacing ToggleGroup
<sha> widgets/insights-rail/InsightsRail.svelte — Progress bars to size=md Progress
<sha> .claude/rules/24-shared-ui-ownership.md — new rule: shared/ui owns primitives, upper layers compose only
<sha> .forgeplan/evidence/EVID-023-*.md — closing EvidencePack

Net diff: +201 / −254 lines in template/src/. ≈53 lines of duplicated atom CSS removed; behaviour preserved.

Forgeplan

  • ✅ EVID-023 created with ## Structured Fields (verdict=supports, congruence_level=3 CL3, evidence_type=measurement).
  • ✅ Linked EVID-023 --informs--> PRD-018 and --informs--> RFC-016.
  • ✅ Activated (status=active).
  • forgeplan score PRD-018 → R_eff: 1.00 (Adequate), F-G-R: 0.79 (B), 2 evidence.
  • ✅ Rule 24 (shared/ui ownership) added to .claude/rules/00-index.md.

Test plan

  • npm run check — 0 errors, 0 warnings (1039 files).
  • Visual smoke in Chrome on / — light + dark theme parity verified end-to-end.
  • DOM inspection of migrated nodes:
    • Lowest R_eff bars → <.progress class="progress variant-success size-md"> with proper width: % on .bar (height 6px, border-radius 999px verified via getComputedStyle).
    • PaneFrame controls → 6 nodes with class="btn variant-ghost size-sm pane-icon …".
    • HealthBar theme switcher → bits-ui <ToggleGroup type="single"> with proper ARIA wiring; clicking DARK actually flips documentElement.dataset.theme.
  • No app-side console errors (only chrome-extension locatorjs warnings, unrelated).
  • grep -RIn '<style>' template/src/widgets/{version-footer,mosaic} template/src/pages/home shows only layout/positioning/motion styles.

Out of scope (intentional, documented in EVID-023 §Interpretation)

  • VersionFooter.svelte — fixed-positioned chrome.
  • UpdateDialog.svelte body — informational, not a form.
  • Splitter, MosaicCanvas, drop overlay — drag/layout chrome.
  • HealthBar status .chip rail — segmented unified-border design intent.
  • InsightsRail KPI cards (.kpi) and .tab navigation — bigger refactor; deferred.
  • +error.svelte .home-link<Button> lacks href. Catalogue gap.

Known debt (rule 24 cleanup)

Eleven :global() overrides remain in upper-layer <style> blocks (HealthBar, Filters, ArtifactPanel, InsightsRail, Timeline, UpdateButton, PaneFrame). Tracked as TODO(rule-24-cleanup) in EVID-023 §Interpretation. Each should resolve into a new variant/size on the corresponding primitive (preferred) or a removal.

Catalogue gaps surfaced

  1. Button has no href — anchor-styled-as-button cases blocked.
  2. Tooltip.Trigger always renders its own <button> — can't wrap <Button>. TODO(rfc-016-tooltip-aschild) marker in PaneFrame.svelte.
  3. Card padding caps at lg (16px) — large card paddings (40px+) need a padding="xl".

Follow-up RFC candidate: "shared/ui catalogue gaps" addressing these three.

🤖 Generated with Claude Code

fedorovvvv and others added 12 commits May 7, 2026 21:32
Swap inline `<button class="update-btn">` for the shared `Button`
primitive (variant=secondary, size=sm). Outer `.update-btn-pos` wrapper
keeps fixed positioning; the inner glow dot, "UPDATE" label, arrow, and
version chip stay as decorative spans.

Refs: PRD-018 SC-2, RFC-016, EVID-022 · Closes part of #81 batch 1/3

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#83)

Swap inline `<button class="pane-icon">` for the shared `Button`
primitive (variant=ghost, size=sm) on the three pane-frame controls
(reset zoom / add pane / close pane). Pane-header layout (toolbar
chrome, drag handle, padding) stays inline.

Tooltip primitive cannot wrap Button until it supports `asChild`
semantics (current bits-ui Trigger renders its own <button>, which
nests buttons). \`title\` attrs preserved as a stop-gap; TODO marker
in the file links the follow-up.

Refs: PRD-018 SC-2, RFC-016, EVID-022 · Closes part of #81 batch 2/3

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…sue #84)

Swap the inline \`.error-bar\` block (custom div with embedded retry
button) for `Alert variant="danger"` containing a `Button variant="ghost"`
retry control. Border-radius zeroed via class override to keep the
full-width banner look at the top of the layout.

UpdateDialog form-body work (Field/Input) — out of scope here:
the dialog's body is informational (version comparison + manual
update steps), not a form. Existing Dialog/Button/Code primitives
already cover what's there.

Refs: PRD-018 SC-2, RFC-016, EVID-022 · Closes part of #81 batch 3/3

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Swap inline `<button class="chip">` toggleable filter buttons in
Filters.svelte for the Toggle primitive (variant=outline, size=sm).
Pressed state, ARIA, focus ring all come from the primitive. Inline
.chip CSS removed (~30 lines). Color dot + ring decorations kept
inline since they're filter-domain visuals, not generic atoms.

Refs: PRD-018 SC-2, RFC-016, EVID-022

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Theme 3-segment switcher (Auto/Light/Dark) → ToggleGroup type=single
with ToggleGroupItem children. Replaces manual role=radiogroup +
aria-checked wiring with bits-ui's accessible primitive.

Notify toggle (binary on/off) → Toggle primitive (variant=outline,
size=sm). The pressed state shows accent-dim background; the
existing 🔔/🔕 icon swap stays inline.

Status .chip rail (TOTAL/ACTIVE/DRAFT/stale/blind segments) kept as
inline custom segmented bar — design intent is the unified bordered
rail look, which Badge primitives wouldn't reproduce. Documented as
a deliberate skip.

Refs: PRD-018 SC-2, RFC-016, EVID-022

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Tab count badges (Recent 20 / Blocked 4 / Drafts 9 / Agents N) →
  Badge primitive. variant=primary for warn (blocked/drafts), secondary
  for neutral (recent/agents). Removes 12 lines of .badge CSS.

- "Lowest R_eff" progress bars (Health tab) → Progress primitive.
  variant maps to reffTone(): danger/warning/success. Removes ~20 lines
  of inline span-based bar styling and the .scoring-row .bar override.

KPI cards, .tab navigation, and other inline atoms in this widget
left for follow-up rounds.

Refs: PRD-018 SC-2, RFC-016, EVID-022

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ives

- .kind inline span → Badge variant=ghost size=sm. Removes 7 lines
  of .kind CSS.

- 5 .ghost buttons (Show downstream/upstream/Clear/Hide body/Copy as
  markdown) → Button variant=ghost size=sm with class="panel-action"
  preserving the project's mono-font + uppercase + accent-on-hover
  style via :global() override. Removes ~28 lines of duplicated
  .ghost CSS.

- .close × button → Button variant=ghost size=sm. Position kept
  (top-right absolute), font-size 22px override for the × glyph.

- copy-md state classes (copied/failed) routed through interpolated
  class string instead of class:directive (component classes can't
  use class: directives in Svelte 5).

Refs: PRD-018 SC-2, RFC-016, EVID-022

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three corrections after visual diff against the /playground reference:

1. HealthBar theme switcher — drop the custom :global(.theme-toggle)
   override that stripped bg/padding. The ToggleGroup now renders its
   natural bg-2 + 2px-padding container with a raised bg-1 pill on
   the selected segment, matching the "Left | Center | Right" example.

2. Filters KIND/STATUS — collapse N independent <Toggle> calls into
   one <ToggleGroup type="multiple"> per section. Multi-select state
   flows through value/onValueChange. Looks like a unified segmented
   filter rail, matching the /playground style.

3. Timeline collapse button — was Button (binary state was hidden in
   aria-expanded). Now <Toggle pressed={!collapsed}> so the expanded
   state visibly highlights with accent-dim. The "now" trigger stays
   a Button (one-shot action). Removed dead .ghost / .toggle CSS.

Refs: PRD-018 SC-2, RFC-016, EVID-022

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous styling kept the ToggleGroup's default packed layout
(2px gap, single bg-2 container, transparent item borders). The
intended look is independent outlined chips:

- ToggleGroup wrapper made transparent (no bg/border/padding).
- Items spaced 6px apart, wrap onto multiple rows.
- Each item gets a visible line-2 border and bg-1 fill.
- Hover bumps to line-3.
- Pressed (data-state=on) → accent border + accent text + plain bg.

ToggleGroup type=multiple semantics preserved — only the visual layer
changed via :global() overrides scoped to .filter-group.

Refs: PRD-018 SC-2, RFC-016, EVID-022

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Progress was correctly mounted but rendered at size="sm" (4px height) —
visually too thin against the user's reference. Bumping to size="md"
(6px) matches the intended chunkier look while keeping the rounded
pill shape and tone-driven variant (success/warning/danger).

Refs: PRD-018 SC-2

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ipline

shared/ui/ is the single owner of visual primitives. Upper FSD layers
(entities/widgets/pages/routes) compose primitives but MUST NOT modify
their internals from above (no :global() overrides on primitive class
names, no inline atom re-implementations, no consumer-side variants).

Required path when a feature needs a new look:
  1. Add a variant/size/prop to the primitive in shared/ui/<name>/
     (or create a new primitive — preferring shadcn-svelte / bits-ui
     ports, styled with plain CSS + CSS vars from app/styles/app.css).
  2. Showcase on /playground in light + dark themes.
  3. Use the new prop from the upper layer.

Allowed upper-layer styling: layout, positioning, motion, container
chrome that does not shadow a primitive — composition, never
customisation.

Lists existing violations as TODO(rule-24-cleanup) debt for future
cleanup PRs. Updates rules/00-index.md.

Refs: PRD-018 SC-2

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New EvidencePack EVID-023 documents the shared/ui integration across 7
widgets/pages (PR #85). Structured Fields: verdict=supports,
congruence_level=3 (CL3 same-context), evidence_type=measurement.

Linked --informs--> PRD-018 and RFC-016. Activated.

forgeplan score PRD-018 → R_eff: 1.00 (Adequate), 2 evidence
(EVID-022 + EVID-023). Confidence: insufficient (only 2 evidence in
support — acceptable for a Standard-depth PRD with same-context CL3
measurements).

PRD-018 SC-2 moves "deferred" → "pass". The primitives catalogue is
now the rendering path on the home route, the artifact panel, the
filters sidebar, the health bar, the timeline header, the version
footer, and the mosaic pane controls.

Refs: PRD-018 SC-2, RFC-016, EVID-022 · Closes #81 (after PR #85 merge)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@fedorovvvv fedorovvvv merged commit 821e36c into develop May 7, 2026
3 checks passed
@fedorovvvv fedorovvvv deleted the feature/prd-018-sc2-integration branch May 7, 2026 18:04
fedorovvvv added a commit that referenced this pull request May 7, 2026
## Summary

Resolves all 13 child issues of umbrella **#99** — the multi-expert
audit follow-ups for PRD-018 SC-2 (`shared/ui` migration in PR #85).

- **A11y regressions fixed** — HealthBar `radiogroup` (#86), Timeline
`aria-expanded` (#87), InsightsRail `tablist` (#91).
- **Primitive catalogue extended** (#88) —
`Button.variant="ghost-mono"`, `Button.size="icon"`,
`Badge.variant="mono"`, `Alert.tone="banner"`,
`Toggle/ToggleGroup.variant="outline-mono"`, `ToggleGroupItem`
`role`+`aria-checked` passthrough, `TabsList.wrap`. All showcased on
`/playground` in light + dark.
- **Widgets migrated to catalogue** — InsightsRail uses
`Tabs/TabsList/TabsTrigger` (#91), ArtifactPanel uses `Collapsible` for
Outgoing/Incoming disclosures (#92).
- **Type safety tightened end-to-end** — `ArtifactSnapshot.kind/.status`
literal unions in `shared/server/snapshot.ts`, dropped `as typeof` casts
in HomePage (#94), discriminated unions through Filters (#98), HealthBar
typing fixes (#93).
- **Latent reactivity bugs fixed** — HealthBar notify race during
permission prompt (#95), ArtifactPanel auto-collapse `$effect` re-firing
on graph poll (#96).
- **Rule-24 enforcement** — verification grep now exits non-zero on
violation (#89), forbidden-class list extended with consumer-class
pattern, all 13 `:global()` overrides retired by Wave 2/3 variants
(#90).
- **Code-quality bundle** — FIXME/TODO markers, AbortController on
Timeline fetch, clipboard fallback DOM-leak edge case (#97).

Driven by **PRD-019** (active, R_eff > 0) with **EVID-024**
structured-fields evidence linked (verdict: supports, congruence_level:
3, evidence_type: test).

## Why

The PRD-018 SC-2 migration shipped APPROVE_WITH_FIXES — structurally
complete and svelte-check clean, but introduced silent a11y regressions
for screen-reader users, ~13 rule-24 violations re-skinning primitive
internals from upper layers, type casts hiding source-of-truth gaps, and
broke the rule-24 verification grep itself. The catalogue
(`/playground`) was no longer the source of truth — half the visuals
were produced by overrides scattered across widgets. This PR closes that
gap so the next contributor compositions instead of forking.

## Test plan

- [x] `npm run check --prefix template` — clean (1039 files / 0 errors /
0 warnings) on the final commit.
- [x] Rule-24 verification grep run from `template/` — exit 0, prints
`OK: no upper-layer reaches into primitive internals`.
- [x] Browser DOM inspection via Chrome DevTools at
`http://127.0.0.1:5174/`:
- `[role=radiogroup][aria-label=Theme]` with 3
`[role=radio][aria-checked]` items
- `button.timeline-toggle aria-expanded="true"
aria-controls="timeline-body" aria-pressed=null`
- `[role=tablist][aria-label=Insights]` with 5
`[role=tab][aria-selected]` items
  - `Alert tone="banner"` replaces `.error-alert` `:global()` override
- [x] Browser visual smoke at `/playground` (light + dark) — every new
variant renders, tokens resolve in both themes.
- [ ] CI matrix (ubuntu/macos/windows × Node 22) — required gate.
- [ ] VoiceOver/NVDA manual a11y pass — out of scope (DOM tree verified
mechanically).

## Out of scope

- Adding rule-24 verification as a CI step (separate ticket).
- `shared/ui/README.md` worked-example expansion (table updated;
`/playground` is the contract).

## Closes

Closes #86, #87, #88, #89, #90, #91, #92, #93, #94, #95, #96, #97, #98.

---

Refs: PRD-019, EVID-024
🤖 Generated with [Claude Code](https://claude.com/claude-code)
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