Skip to content

refactor: introduce AppTooltip, replace native title tooltips across docs + playground chrome — #495#621

Merged
johnleider merged 14 commits into
masterfrom
refactor/title-to-tooltip-495
Jul 19, 2026
Merged

refactor: introduce AppTooltip, replace native title tooltips across docs + playground chrome — #495#621
johnleider merged 14 commits into
masterfrom
refactor/title-to-tooltip-495

Conversation

@johnleider

@johnleider johnleider commented Jul 15, 2026

Copy link
Copy Markdown
Member

Part of #495 — replacing native title browser tooltips with the v0 Tooltip compound across docs and playground.

What this PR does

1. AppTooltip wrapper (both apps)

An app-local AppTooltip (apps/docs/…/app/AppTooltip.vue, apps/playground/…/app/AppTooltip.vue), built directly on the v0 Tooltip.Root/Activator/Content compound. text prop + default-slot trigger, as/positionArea/disabled props, forwards $attrs (class/aria/listeners) to Tooltip.Activator, surface-card styling matching the BenchmarkRow/DocsMaturity convention.

Why app-local, not GnTooltip in genesis: docs depends on @paper/genesis but playground depends only on @vuetify/v0, so one genesis wrapper can't serve both; and app chrome shouldn't couple to a released DS package. Matches the repo's existing per-app chrome duplication (AppCloseButton exists in both apps).

2. Conversions

  • App bars — docs AppBar (Discord/GitHub), playground PlaygroundAppBar (share-link + settings).
  • Docs icon-button sweep (~37 sites) — releases/ask panels, example + mermaid toolbars, question/skill/palette-preview controls, app-shell buttons.
  • Page meta/action wrappersDocsMetaItem (coverage breakdown, commit hash, …), DocsActionChip (page actions), DocsBadge (skill/mode badges). DocsMetaItem's · separator moved from an adjacent-sibling (+) to a general-sibling (~) selector and un-scoped, since AppTooltip renders a multi-root fragment (see feat(genesis): add GnActionButton action-button primitive #359).
  • Non-button elementsDocsSearch result actions (9), skillz badges, release/commit anchors, home color swatch.
  • The Ask form's Send button (native type="submit") — converted via Tooltip renderless mode, binding attrs + the anchor styles onto the submit button so it keeps its type and native form submission. This relies on the Tooltip.Activator renderless anchor-styles fix (fix(Tooltip): expose anchor styles on Tooltip.Activator renderless slot #648), merged and included here.

Accessibility (preserved, not just moved)

Tooltip.Activator wires aria-describedby — a description, not a name. So every icon-only trigger that relied on title for its accessible name gained an explicit aria-label; state-conveying toggles keep a reactive aria-label; copy/status buttons use a stable name with only the tooltip text flipping to "Copied!". Tooltip.Content renders via the native popover API (top layer), so tooltips stack above dialogs/overlays.

Deliberately left native (correct, not tooltips / other tranches)

  • Not tooltips: heading-text titles (DocsApi, DocsApiHover, api pages, settings-section headers) and filename data props (DocsCodeActions & friends).
  • Correct as native: truncation-mirror titles (home copy/install command inputs).
  • Other tranches: genesis GnActionButton (versioned package + changeset), Popover.Activator triggers (activator-on-activator), the palette-browse swatch grid (hundreds of Tooltip.Root instances), the playground editor chrome, and docs teaching examples.

Verification

  • vue-tsc + eslint clean on changed files.
  • Independent code-review pass (a11y names, submit/disabled handling, scoped-CSS, template balance, double-fire) — clean.
  • Browser-verified across app bars, the search overlay, the settings dialog, the meta row (separators + coverage tooltip), and the renderless Send button (tooltip positions against the submit button; accessible name + submit preserved). Zero console errors.

Introduce a docs-local AppTooltip built on the v0 Tooltip compound
(Root/Activator/Content) and convert the Discord and GitHub app-bar
links from native title attributes to it. The links keep their
aria-label, so the accessible name is unchanged; the tooltip supplies
the description via aria-describedby.

Part of #495.
Add an app-local AppTooltip built on the v0 Tooltip compound and convert
the share-link and settings buttons from native title attributes. These
buttons previously relied on title for their accessible name, so an
explicit aria-label is added on each; the tooltip text stays reactive so
the share button still shows "Link copied!" feedback. The playground does
not install createTooltipPlugin, so this exercises the useTooltip fallback
context.

Part of #495.
@johnleider johnleider added this to the v1.0.0 milestone Jul 15, 2026
@johnleider johnleider added documentation Improvements or additions to documentation E: useTooltip C: Tooltip labels Jul 15, 2026
@johnleider johnleider self-assigned this Jul 15, 2026
Declare a `disabled` prop on AppTooltip and forward it to Tooltip.Root.
Tooltip.Activator merges its own attr bundle (which includes
`disabled: undefined` when the root isn't disabled) over the caller's via
mergeProps, so a caller's `:disabled` would otherwise be clobbered. Keeps
the playground copy in sync with the docs one.

Part of #495.
Replace native `title` browser tooltips on raw icon `<button>` elements
across the docs app chrome with the AppTooltip wrapper (~37 sites over 20
files): releases/ask panels, example + mermaid toolbars, question/skill/
palette-preview controls, and app-shell buttons (search, settings,
account, footer theme toggle, close button).

Accessibility is preserved, not just moved: Tooltip wires aria-describedby
(a description), not a name, so every icon-only button that relied on
`title` for its accessible name gains an explicit aria-label. State-
conveying toggles keep a reactive aria-label; copy/status buttons use a
stable name with only the tooltip text flipping to "Copied!".

AppTooltip also gains a `disabled` prop (forwarded to Tooltip.Root) so a
caller's `:disabled` survives Tooltip.Activator's attr merge.
AppCloseButton's style is un-scoped because AppTooltip renders a
multi-root fragment and the parent scope-id is not forwarded to its
button (see #359).

Deliberately left native: submit buttons (Tooltip.Activator forces
type=button, which would break form submit), buttons nested inside other
activators (Popover/Discovery), non-button elements (span/div/a), and the
high-cardinality palette-browse swatch grid (hundreds of Tooltip.Root
instances). These are follow-up tranches.

Part of #495.
@johnleider johnleider changed the title refactor: introduce AppTooltip, replace native title attrs (app bars) — #495 refactor: introduce AppTooltip, replace native title tooltips across docs + playground chrome — #495 Jul 16, 2026
Convert the wrapper components behind the page-features row to AppTooltip:

- DocsMetaItem (phase, renderless, skill level, coverage, benchmark, last
  updated) — these titles add info beyond the visible label (coverage
  breakdown, commit hash), so they become real tooltips. The `·` separator
  moves from an adjacent-sibling (+) to a general-sibling (~) selector and
  is un-scoped, because AppTooltip renders a multi-root fragment: the
  tooltip content node sits between items and the parent scope-id is not
  forwarded (see #359).
- DocsBadge (skill-level / mode badges) — as="span" tooltip.
- DocsActionChip — the href chips (Edit page, Report a Bug, Open issues,
  View on GitHub) become as="a" tooltips; the Copy Markdown chip's title
  was identical to its visible label, so it is simply dropped rather than
  turned into a redundant tooltip.

Part of #495.
Sweep the remaining native `title` tooltips on span/div/a elements:

- DocsSearch — 9 result-row action controls (favorite / Ask AI / dismiss);
  they are role="button" spans that already carry aria-labels, so the name
  is preserved and only the tooltip description moves to AppTooltip.
- DocsQuestion / DocsDiscoveryStep — the SKILLZ level badge span; the
  `.skillz-badge` style is un-scoped in both (AppTooltip renders a
  multi-root fragment, so the parent scope-id no longer reaches the
  activator — see #359).
- DocsReleases — the two social <a> links.
- DocsLastCommit — the commit <a> (tooltip shows the full sha; the visible
  text is the short slice, so it is genuine info, not a truncation mirror).
- SkillMasteredBadge — the completed-skill <span>.
- HomeDx — the theme color-swatch <div> (label on hover).

Left native (correct): truncation-mirror titles on the Home copy/install
command inputs (title echoes the truncated command), the DocsQuestion
hint-reveal spoiler control, the Popover.Activator trigger, and the
DocsReleases GnActionButton (genesis, separate tranche).

Part of #495.
The Send button is a native type="submit", so the AppTooltip wrapper can't
cover it — Tooltip.Activator forces type="button". Use Tooltip in renderless
mode instead: bind attrs + the anchor styles onto the submit button so it
keeps its type and native form submission while gaining the styled tooltip.
This was the one docs-chrome site the icon-button sweep left on a native
title.

Relies on Tooltip.Activator exposing anchor styles on its renderless slot
(#648).

Part of #495.
The skill filter and theme selector are Popover.Activator triggers — a
tooltip on the same element would collide with the popover's own CSS
anchor-name. Wrap the activator in an AppTooltip as="span" instead: the
tooltip anchors to the span, the popover to the inner button, and a click
closes the tooltip while opening the popover. The account-settings avatar
button (a plain button inside Discovery.Activator) converts directly.

All three titles were redundant with the existing aria-label (or, for the
theme selector, the current theme name), so accessible names are unchanged.

Part of #495.
Same Popover.Activator pattern as the skill filter / theme selector: wrap
the activator in AppTooltip as="span" so the tooltip and the popover anchor
to separate elements. The wrapper div's :title="command" is left native — it
is a truncation-mirror revealing the full command.

Part of #495.
@johnleider
johnleider marked this pull request as ready for review July 19, 2026 19:08
…nav)

A final onceover of native `title` in the docs app surfaced four sites the
earlier sweeps missed:

- AppFooter last-commit <a> (commit datetime) -> AppTooltip as="a".
- AppBrowserIcon (:title -> AppIcon <i> fallthrough) -> AppTooltip as="span".
- AppNavFooter freshness router-link -> AppTooltip as="span" wrap.
- AppBar search button -- the title was fully redundant (aria-label + a
  visible "Search the docs..." label + a visible Ctrl+K kbd), so it is
  removed rather than turned into a redundant tooltip.

Every other remaining `title=` in the docs app is a non-tooltip: heading
text (API / settings sections), filename data props for code panes, or props
feeding the already-converted meta/badge/chip wrappers -- plus the
intentional exclusions (truncation-mirror inputs, the interactive hint
spoiler, and the GnActionButton / Dialog.Close component tranches).

Part of #495.
AppVersionChip's release link used Vue's same-name shorthand `:title` (the
"Last Released: <datetime>" hover), which the earlier sweep's `:title=` grep
missed. Wrap the AppLink in AppTooltip as="span" (AppLink is a routing
component, so it can't be the activator directly). The aria-label
("Latest release <tag>") is unchanged.

Part of #495.
Source openDelay/closeDelay/positionArea/disabled from
Pick<TooltipRootProps, ...> and `as` from AtomProps['as'] instead of
hand-retyping them, so the wrapper can't drift from the v0 Tooltip surface.
Also widens `as` (was `string`) to accept a component, matching what
Tooltip.Activator's `as` allows. Type-only; no runtime change.

Deliberately NOT `extends TooltipRootProps`: that would surface props the
wrapper doesn't forward (interactive, positionTry, namespace, open v-model,
renderless) and inherit Root's `as` semantics (the Root element, not the
activator).

Part of #495.
Keep the playground AppTooltip copy in sync: forwarded prop types come from
Pick<TooltipRootProps, ...> and `as` from AtomProps['as']. Type-only.

Part of #495.
@johnleider
johnleider merged commit f7940e8 into master Jul 19, 2026
20 checks passed
@johnleider
johnleider deleted the refactor/title-to-tooltip-495 branch July 19, 2026 19:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C: Tooltip documentation Improvements or additions to documentation E: useTooltip

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant