Skip to content

fix(ui): ui kit harmonisation after naive-ui (tokens, sizes and touch, tooltips, field states, toggles, one overlay system) - #592

Merged
jplacht merged 17 commits into
mainfrom
fix/ui-kit-harmonisation
Oct 13, 2026
Merged

jplacht merged 17 commits into
mainfrom
fix/ui-kit-harmonisation

Conversation

@jplacht

@jplacht jplacht commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

What and why

naive-ui is gone, but src/ui/ still looked and behaved like the library it replaced: seven floating-panel looks, literal colours, buttons off the tokens, props that don't exist (three "secondary" buttons rendered blue), tooltips on mouse hover only, no field error state and three overlay systems. This makes it one kit on the tokens and the colour-blind palette. It's usable by keyboard and touch (44 px targets on touch screens, tooltips on focus and tap) and has the options the app kept working around, so the chart specs can build on it.

Changes

  • Guard and cleanup: stray kit props are removed, and pnpm tsc now fails on unknown props or events (checkUnknownProps/checkUnknownEvents). There's a kit page at /dev/kit (dev builds, or VITE_KIT_PAGE=true).
  • Tokens: one panel, border, shadow and radius look for every overlay and table. Buttons and tags use primary/negative/warning, and the app's raw red/orange/green and text-white/60 now use tokens.
  • Sizes and touch: sm/md heights are equal at 24/28 px. A touch-target hit area gives 24 px everywhere and 44 px on coarse pointers, and fields are 44 px tall on touch. PCheckbox gains size and indeterminate.
  • Tooltips: they open on focus and tap, close with Esc and are announced via aria-describedby. Field states: status on fields, PFormItem error, PNotice (11 notices migrated), PModal width and a PInput #prefix slot.
  • Toggles and tags: PButton pressed sets aria-pressed (27 toggles); PTag closable is a real "Remove {label}" button.
  • One overlay system: select lists move onto the layer stack (useSelectList, listbox ARIA, Esc closes only the list); stateCurrentOpen and the data-pselect-dropdown hatch are removed.

Outside the kit, getMaterialExchangeOverview now rejects instead of returning a half-filled overview, which made the MaterialTile tooltip throw. The branch keeps one commit per part plus the QA fix commits.

How it was verified

  • QA: PASS after five rounds: checks.sh, the qa code review, and the qa-e2e walk in the running app. Details are in the spec's QA report.

  • Checks:

    • pnpm test passed: 142 files, 3870 tests. It ran with --maxWorkers=3 because, at load average 20, main.test.ts hits its 30 s timeout with the default workers, before and after the merge alike.
    • pnpm test:components: 950 tests passed.
    • pnpm tsc, pnpm lint and pnpm knip pass.
  • UI audit: all pages at 8 widths, against a baseline taken on main. No regressions versus baseline.

    Check main this branch
    smallTargets (24 px) 225 133
    smallTargetsTouch (44 px, touch) 1782 639 (no kit controls left; the app shell is deferred)
    smallTargetsTouchTable 1041 756
    smallTargetsTouchCovered 0 64 (accepted: neighbouring buttons)
    consoleErrors 120 120
  • Bench (select): open with 330 options takes 3.5 → 3.7 ms. ArrowDown with 2000 options takes 0.7 → 0.8 ms, with 3 updates per key on both.

  • Visual sign-off: 35 main | branch | diff pairs, signed off row by row. They're in specs/assets/ui-kit-harmonisation/ in the workspace repo.

  • Merge of main: brought in fix(account): registration form keeps the security planet after an error and shows every field error at its field #591 (signup-02). RegistrationComponent.vue conflicted: the new top error (alertText + alertMessages) now renders inside a PNotice, and the field errors are unchanged. Its tests now find the PNotice (separate commit). The registration flow was retested in the app after the merge: PASS. The username error shows under its field with the PNotice above, the security planet and answer are kept, and the notice clears once the field is fixed. The UI audit of landing, login-form and onboarding shows no regressions.

Screenshots

Desktop (1440):

Kit page: every control, size and state (1440) Plan: pressed toggles, tokens (1440) Management: kit checkboxes (1440)
Kit page: every control, size and state at 1440 Plan: pressed toggles, tokens at 1440 Management: kit checkboxes at 1440
Exchanges: field warning ring (1440) Login form (1440) Registration: PNotice and field error (1440)
Exchanges: field warning ring at 1440 Login form at 1440 Registration: PNotice and field error at 1440

Mobile (375, touch):

Kit page: every control, size and state (375) Plan: pressed toggles, tokens (375) Management: kit checkboxes (375)
Kit page: every control, size and state at 375 Plan: pressed toggles, tokens at 375 Management: kit checkboxes at 375
Exchanges: field warning ring (375) Login form (375) Registration: PNotice and field error (375)
Exchanges: field warning ring at 375 Login form at 375 Registration: PNotice and field error at 375

Main | branch | diff comparisons for 35 states are in the workspace repo, specs/assets/ui-kit-harmonisation/.

Deploy order

Frontend only; no backend or API change.

🤖 Generated with Claude Code

jplacht and others added 17 commits October 13, 2026 20:41
… page

- 'secondary' on PButton becomes type="secondary" (COGM and the two
  Material I/O toggles were rendering primary blue); bordered, size,
  multiple, clearable and status that the kit never had are removed
- vue-tsc checkUnknownProps/checkUnknownEvents, with role, title, id,
  aria-* and data-* allowed to fall through (ui.types.ts)
- remove buttonConfig defaults, the unused v-click-outside directive and
  PSelectElement/PPagination from the barrel
- rename PFormSeperator to PFormSeparator
- /dev/kit shows every kit component, in dev and VITE_KIT_PAGE builds

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- style.css tokens: surface, surface-sunken, line, line-strong, hover,
  selected, ink, ink-strong, primary, link, radius-control (4px),
  radius-panel (6px), shadow-panel; gray-dark, pp-border and link-primary
  alias them
- popover, menu, select list, tooltip and toast share one panel look;
  modal and drawer the same surface and shadow; one hover highlight
- no hex or rgb literals left in the kit (PDataTable, PTable, PPagination,
  PPopover, PModal, PDrawer, PDropdown)
- error buttons are filled negative with dark text and follow the
  colour-blind palette; warning buttons and tags are amber
- PSpin colours from a fixed map
- app: raw red/orange on negative/warning, text-white/60 and /55 on muted
- kitTokens.test.ts guards literals and raw red/orange/green

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- size (sm | md) on PSelect, PSelectMultiple and PCheckbox; every control
  is 28px at md and 24px at sm (PInput sm gets a height, a textarea none);
  MaterialTile and CXTickerPreference get their sm back
- touch-target utility: an invisible 44x44 hit area under
  pointer: coarse, on PButton, PCheckbox, PInputNumber steppers,
  PLayerClose, pagination, table sort and expand buttons, PTag close,
  PDropdown items and the raw buttons in Planet Search, Management and
  the plan analytics box
- text fields and select triggers are 44px tall on touch screens
- PCheckbox indeterminate; Management's native checkboxes move to it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- useTooltipTrigger holds the open rules (hover with a 150ms close delay,
  focus in the trigger, Esc caught in the capture phase, a tap on a
  non-interactive trigger toggles, a tap on a control inside runs it),
  for PTooltip and the coming chart tooltips
- PTooltip: role=tooltip panel, aria-describedby on the trigger's first
  focusable element, a trigger without one becomes a tab stop; disabled
  tooltips add nothing
- the clickable planet resource div in PlanProduction is a button

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- status (error | warning) on PInput, PInputNumber, PSelect and
  PSelectMultiple: an inset ring, aria-invalid for an error
- PFormItem error shows the message under the field, puts the control in
  the error state and links it and #info with aria-describedby
- PNotice (info, warning, error; #action), role alert or status; the 11
  hand-styled notices move to it
- the construction cart's red inputs and the Exchanges name warning use
  status; the warning shows again
- PModal width; the 9 call sites drop their ! width classes
- PInput #prefix (Planet Search drops its padding override);
  PSelectMultiple placeholder, clearable off by default

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- PButton pressed: sets aria-pressed, primary while pressed, its type
  (default secondary) when not; the 27 buttons that switched type to
  show a selection or an on/off state use it
- PTag closable renders a real 'Remove {label}' button that emits close;
  the clickable #icon slot is gone and PSelectMultiple chips use it
- the select triggers lose their <label> wrapper, which forwarded every
  click on the trigger to the first chip's remove button

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- useSelectList: search, filtering, highlight (still one option per arrow
  key) and keys, shared by PSelect and PSelectMultiple, which keep only
  value handling and their trigger
- select lists are non-modal layers in useOverlayLayer, positioned with
  createFloating: Esc closes only the list, a pick in a select inside a
  popover keeps the popover open; currentlyOpenId, the select's own
  popper and PPopover's data-pselect-dropdown check are gone
- combobox/listbox semantics: aria-controls, role=option with
  aria-selected, groups, aria-activedescendant; a keyboard-opened
  searchable select focuses its search, Esc and Enter return to the
  combobox
- z-indexes from ui/zIndex.ts; tooltips render (and describe their
  trigger) only while open, so closed ones add no nodes
- PDataTable's expand column header gets a screen-reader label
- tests: listbox roles, Esc in a select inside a modal, drawer and
  popover, axe over the kit page

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lectElement and PConfirmHost

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- a chip's remove button keeps Enter and Space (the combobox opened or
  picked instead), so chips can be removed by keyboard
- touch-target is at least 24px for every pointer (44px on touch); tooltip
  triggers that become tab stops get it
- PForm rows don't clip the hit areas on touch screens
- PSelectMultiple is 28/24px tall again (less padding)
- a tooltip adds its id to a field's own aria-describedby instead of
  replacing it
- more built than planned is a warning, not an invalid value
- architecture.md no longer mentions v-click-outside

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…chips

- PInputNumber steppers and a tag's remove button are visibly 44px on
  touch screens instead of an invisible hit area that covered their
  neighbour (a tap on the right of − incremented)
- a tooltip on a disabled control: the trigger takes focus, hover and
  taps (the COGM tooltip explains why it's disabled)
- PSelectMultiple chips follow its size, so sm is 24px with chips
- removing a chip by keyboard keeps focus in the select
- PForm content keeps min-width 0, so its grid sizes as before
- docs: hit areas next to each other, the table-row limit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eways only

- PInputNumber's steppers are mouse-only (as with the keyboard) and
  hidden on touch screens, where 44px steppers left no room for the
  number; the field opens the numeric keypad
- PForm rows clip sideways only (the ellipsis), so a checkbox's hit
  area isn't cut to the row on a mouse either
- docs: steppers on touch, buttons close together share the space

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- a tag's remove button is a target on a mouse too (touch-target, 24px);
  on touch screens it stays visibly 44px inside its chip
- docs: PForm rows only clip sideways

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…filled data

A missing exchange ticker left MaterialTile with a partial overview, and
its tooltip (now also opened by keyboard focus) threw on render.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…plete the password

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p only

The kit's button min width no longer overrides the app's min-w-30, so at
768 the 120px button squeezed the plan title until it wrapped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	src/features/account/components/RegistrationComponent.vue
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@netlify

netlify Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for prunplanner-preview ready!

Name Link
🔨 Latest commit 3ea037b
🔍 Latest deploy log https://app.netlify.com/projects/prunplanner-preview/deploys/6ace8f45b6915a0af1799586
😎 Deploy Preview https://deploy-preview-592--prunplanner-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

jplacht added a commit that referenced this pull request Oct 13, 2026
@codacy-production

codacy-production Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics -25 complexity · 0 duplication

Metric Results
Complexity -25
Duplication 0

View in Codacy

🟢 Coverage 100.00% diff coverage · +0.02% coverage variation

Metric Results
Coverage variation ✅ +0.02% coverage variation (-1.00%)
Diff coverage ✅ 100.00% diff coverage

View coverage diff in Codacy

Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (7cc168d) 4152 4061 97.81%
Head commit (3ea037b) 4153 (+1) 4063 (+2) 97.83% (+0.02%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#592) 1 1 100.00%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@jplacht
jplacht merged commit 2e82c6a into main Oct 13, 2026
12 checks passed
@jplacht
jplacht deleted the fix/ui-kit-harmonisation branch October 13, 2026 20:38
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