You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/audit/components/tooltip.md

55 lines
4.8 KiB

# Audit: tooltip
audit-version: 1
audited-at: 2026-06-26
scope: ['soma', 'eidos', 'sema']
method: adversarially-verified workflow (analyze → refute); HIGH/CRITICAL personally re-verified against cited code by the lead.
provider: src/uix/soma/components/tooltip/tooltip-provider.svelte.ts
## Summary
Counts (post-verification): CRITICAL 0 · HIGH 0 · MEDIUM 1 · LOW 1.
systemic hits: SYS-6.
## Findings
### MEDIUM: Test Coverage — Keyboard/Dismissal — tooltip-003 <!-- id: tooltip-003 -->
- dimension: F
- rule: Test Coverage — Keyboard/Dismissal
- location: src/uix/soma/components/tooltip/tooltip-provider.svelte.test.ts:1-322
- evidence: Test env is @vitest-environment jsdom (line 1). 4 test cases (describe lines 116-322) cover: timer delay/close/skip-delay, focus/blur/disabled state, click/Enter/Space on trigger, parts registration/linking. MISSING: Escape key close, dismissal flow, content pointer enter (which cancels close).
- impact: Keyboard routes (Enter/Space) and Escape dismissal behavior are not verified. jsdom-only environment cannot catch DOM interaction edge cases. High-risk behavior untested on the difficult path (keyboard/escape).
- proposed-fix: Add test case for onkeydown with Escape key triggering handleClose via Dismissal layer. Add test for content onpointerenter calling cancelClose(). Consider promoting at least the Escape path to a client/Playwright test to verify in real browser.
- verify: [confirmed] Confirmed. Test env is jsdom (test file line 1 `// @vitest-environment jsdom`). The 4 `it` blocks (lines 122,178,218,265) cover: delay/close/skip-delay timers, focus/blur/disabled, click + Enter/Space close (lines 241-259), parts registration/aria-describedby/role linking. There is NO test exercising (a) the Escape -> Dismissal.onEscapeKeydown -> handleClose path (provider lines 456-465) or (b) content onpointerenter -> cancelClose() (provider lines 482-486), which is the hoverable-tooltip cancel-close behavior. Both are real provider behaviors on a moderate-risk path left unexercised. MEDIUM is appropriate; matches SYS-3 (interaction-heavy + jsdom-only + dismissal/cancel paths untested).
- fix-status: open
### LOW: E-bis MAGIC NUMBERS — tooltip-002 <!-- id: tooltip-002 -->
- dimension: E-bis
- rule: E-bis MAGIC NUMBERS
- location: src/uix/eidos/components/tooltip/tooltip.css:86
- evidence: stroke-width: 1px; in outline variant arrow selector. Recipe base.ts declares tooltip.'border-width': 'var(--border-width)' (line ~1), but CSS uses literal 1px instead of that token.
- impact: Design token inconsistency. Outline variant arrow stroke width is hardcoded; if border-width is ever updated at the design system level, outline arrow won't scale with it. Token discipline breach.
- proposed-fix: Replace 'stroke-width: 1px;' with 'stroke-width: var(--tooltip-border-width);' or confirm 1px is intentionally different from the canonical border-width scale (unlikely).
- verify: [downgraded] Confirmed the literal exists but downgraded MEDIUM->LOW. tooltip.css:86 reads `stroke-width: 1px;` in the outline-variant arrow rule. The recipe DOES declare base.ts:3908 `'border-width': 'var(--border-width)'`, and the box border at tooltip.css:31 uses `border: var(--tooltip-border-width) solid ...`. So the 1px on the SVG arrow stroke is a real but trivial token-discipline nit on a 1px hairline that conceptually mirrors the box border (`--border-width` itself resolves to 1px). No user-visible, behavioral, or theming consequence — token hygiene only. LOW, not MEDIUM.
- fix-status: open
## No-findings dimensions
A, B, C, D
## Theming facts (E-bis)
- magic z-index: none
- magic literals: stroke-width: 1px (line 86, should use --tooltip-border-width)
- undeclared parts: data-size (content part) | data-variant (content part)
- roles clean: true · variants clean: true
## Tests (F)
- exists: true · env: jsdom
- covers: timer-based open/close delay; group skip-delay coordination; focus/blur instant open/close; disabled state respect; click/Enter/Space trigger close; part registration and id linking; aria-describedby linking
- untested: Escape key dismissal; Content pointer enter (cancelClose); Dismissal onEscapeKeydown flow
## Style observations (non-blocking)
- Morfo contract is clean and well-structured; trigger + content both use correct 'instant-open' state derivation
- A30 id registration pattern is correct (constructor-time assignment via opts.id)
- SafePolygon integration for hoverable content is properly configured with transitIntentTimeout
- Timer disposal in $effect root is correct
- Two-moments sequence 'pre' is correct for all three events (open/close/close-dismiss)
- Eidos recipe properly declares all component tokens including content-z, padding variants, and font-size scale
- Keyboard handling on trigger (Enter/Space to close when open) matches the pattern from baseline dropdown-menu

Powered by TurnKey Linux.