* fix(design-system): DS::Tooltip a11y — focusable trigger, keyboard parity, Esc dismiss
Closes#1747. Five fixes on the tooltip primitive.
1. **Tooltip anchor not in a11y tree.** The trigger was a bare
Lucide icon, which Lucide renders with `aria-hidden="true"`.
The tooltip target had `role="tooltip"` but nothing referenced
it, so AT users had no way to discover the description. Wrap
the icon in a focusable `<button type="button">` with
`aria-describedby="<tooltip-id>"` so the underlying icon stays
`aria-hidden` and the button picks up the description binding.
2. **Stable per-instance id.** Each DS::Tooltip now mints a
`tooltip-<8-char hex>` id wired between the trigger's
`aria-describedby` and the tooltip's `id`.
3. **Keyboard parity.** Hover-only triggers locked keyboard-only
users out. Add `focusin` / `focusout` listeners on the
controller element so Tab onto the trigger reveals the
tooltip, Tab away dismisses it.
4. **Esc-to-dismiss.** Matches the WAI-ARIA tooltip pattern.
`Escape` while the tooltip is open closes it without removing
focus from the trigger.
5. **Resize-safe width cap.** Replace the hard-coded
`max-w-[200px]` with `max-w-[20rem]` so the tooltip scales
with the user's root font-size setting (large-text accessibility
pref). Slightly wider visual cap (320px @ default) but no longer
clips on text-zoom.
Plus: docstring note that tooltip content must be non-interactive
(no buttons / links / form controls inside) — `aria-describedby`
exposes content as a description, not as an interactive subtree.
Callers needing actions should reach for a popover/menu primitive.
API unchanged. Existing 30+ DS::Tooltip callsites work without
modification — they all pass `text:`-only payloads, which still
render correctly under the new markup.
* fix(review): as: option + alpha focus-ring on DS::Tooltip
Addresses two AI review findings on #1845:
1. **Button-inside-summary spec violation.** Wrapping the icon in
`<button>` regressed keyboard/AT behavior at 13 callsites where
DS::Tooltip lives inside a `<summary>` (8 provider items, lunchflow
disclosure, activity_date, 4 simplefin badges). HTML's content
model forbids interactive content inside `<summary>`; browsers
and AT can drop focus or conflate activation with the disclosure
toggle. Add `as:` parameter — default `:button` preserves the
standalone a11y wrap; `:span` renders a non-focusable wrapper for
summary-nested usage. `focusin` bubbles up to the controller from
the ancestor `<summary>`, so keyboard tooltips still appear on
tab. Migrate the 13 in-summary callsites to `as: :span`.
2. **Raw palette focus ring → alpha tokens.** Swap
`outline-gray-900 theme-dark:focus-visible:outline-white` to the
established focus-ring pattern `focus-visible:ring-2
focus-visible:ring-alpha-black-300
theme-dark:focus-visible:ring-alpha-white-300` — matches the
DS::Toggle fix landed in #1843 review and provider_card /
form-field tokens.
* fix(review): bind tooltip focus on ancestor <summary>
Codex P2 follow-up on #1845: \`as: :span\` renders a non-focusable
trigger inside the disclosure \`<summary>\`. Keyboard users hit Tab
and focus lands on the summary itself; \`focusin\` fires on the
summary and bubbles UP — never down to a descendant span — so the
existing listener on \`this.element\` never fires and the tooltip
stays hidden for keyboard-only users on every in-summary row
(provider _item partials, lunchflow disclosure, activity_date,
simplefin badges). My earlier reply that the focusin "bubbles up to
the Stimulus controller on the outer span" was wrong about the
direction; \`focusin\` only bubbles upward.
In \`addEventListeners\`, resolve \`this.element.closest("summary")\`
and bind \`focusin\` / \`focusout\` / \`keydown\` on it too. Track the
ancestor on the controller and undo the bindings in
\`removeEventListeners\` so reconnect-on-Turbo cycles don't leak.
Update the template comment to reflect the actual mechanism.
* docs(ds-tooltip): correct as=:span comment to match controller mechanism
---------
Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
* Initial data objects
* Remove trend calculator
* Fill in balance reconciliation for entry group
* Initial tooltip component
* Balance trends in activity view
* Lint fixes
* trade partial alignment fix
* Tweaks to balance calculation to acknowledge holdings value better
* More lint fixes
* Bump brakeman dep
* Test fixes
* Remove unused class