feat(lint): ban native title tooltips and migrate to styled Tooltip - #7209
Conversation
Add t3code/no-native-title-tooltip, an oxlint rule that reports title attributes on intrinsic HTML elements and directs authors to the styled Tooltip component. The pin button regression in pingdotgg#6371 happened partly because nothing enforced the preference, so warn now and flip to error once existing usages are migrated.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Reviewed the title → Tooltip migration for consistency with the shared tooltip primitive. Most conversions match the existing Tooltip / TooltipTrigger render={…} / TooltipPopup idiom and preserve aria-labels and layout (the Tooltip root emits no DOM, so flex parents are unaffected).
Four call sites changed interaction ownership rather than just the tooltip mechanism: three now put the trigger around an interactive child that owns its own hover surface (tooltip positioner is z-[140], popover/combobox popups are z-[130]), and one attaches the trigger to an element the design system explicitly makes non-hoverable.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
Reviewed the title → Tooltip migration for consistency with the shared tooltip primitive. Most conversions match the existing Tooltip / TooltipTrigger render={…} / TooltipPopup idiom and preserve aria-labels and layout (the Tooltip root emits no DOM, so flex parents are unaffected).
Four call sites changed interaction ownership rather than just the tooltip mechanism: three now put the trigger around an interactive child that owns its own hover surface (tooltip positioner is z-[140], popover/combobox popups are z-[130]), and one attaches the trigger to an element the design system explicitly makes non-hoverable.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 9c2f68d. Configure here.
ApprovabilityVerdict: Approved 774d434 Mechanical migration replacing native You can customize Macroscope's approvability policy. Learn more. |
There was a problem hiding this comment.
UI Consistency
The four issues flagged on the previous head are addressed (DiffPanel trigger now scopes to the head-ref text, the PR header tooltip no longer swallows PullRequestBaseFreshnessWarning, the pairing-expiry trigger no longer wraps AccessScopeSummary, and the compact filter menu anchors on a hoverable wrapper instead of the disabled MenuRadioItem).
One new finding, inline: the "Remote only" indicator in DiffPanel.tsx loses its accessible name in the migration (the title was the only text, and the icon is aria-hidden).
Also worth noting (no inline comment possible — the lines are outside the diff): the new t3code/no-native-title-tooltip rule only matches lowercase intrinsic elements, so title props that shared primitives forward straight to the DOM still render native tooltips. Two remain next to the surfaces this PR migrated: PullRequestDetailPanel.tsx:1414 (<Badge title="The host will merge this on its own…">, which useRender spreads onto a span, in the same header row that was converted) and PullRequestListFilters.tsx:166,383 (<MenuRadioItem title={option.unavailable}> — the twin of the CompactFilterMenu case converted in _chat.pull-requests.tsx). Converting those keeps hover behavior uniform; otherwise the rule silently leaves the pattern it was added to eliminate.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
One consistency finding on the disabled-menu-item tooltip in _chat.pull-requests.tsx. The rest of the migration matches the existing tooltip contract (Tooltip > TooltipTrigger render={...} > TooltipPopup), keeps aria-labels, and the earlier hover-surface overlaps (DiffPanel combobox, PullRequestBaseFreshnessWarning, AccessScopeSummary) are resolved.
Posted via Macroscope — UI Consistency
Dismissing prior approval to re-evaluate 620d91d
Dismissing prior approval to re-evaluate 2805cc9
There was a problem hiding this comment.
One finding: the base-freshness warning changed position in the PR detail toolbar as a side effect of the tooltip migration. Everything else in this pass looks consistent with the existing tooltip contracts (disabled menu items now use the data-disabled:pointer-events-auto pattern from RightPanelTabs, icon-only triggers keep an accessible name, and hover popovers are no longer nested inside tooltip triggers).
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
One finding: PullRequestActorLabel now owns a tooltip, which stacks a second popup on the reviewer avatar stack that already wraps it in its own tooltip trigger. Everything else in this migration (disabled menu items re-enabling pointer events per the RightPanelTabs contract, render={<Button/>}/render={<PopoverTrigger/>} composition, preserved aria-labels, scoped triggers next to the freshness popover and the base-ref combobox) matches existing conventions, and no wrapped control sits under a [data-slot="button"]/[data-slot="toggle"] themed container or a Group, so the trigger's data-slot takeover has no styling fallout here.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
One finding: PullRequestActorLabel now owns a tooltip, which stacks a second popup on the reviewer avatar stack that already wraps it in its own tooltip trigger. Everything else in this migration (disabled menu items re-enabling pointer events per the RightPanelTabs contract, render={<Button/>}/render={<PopoverTrigger/>} composition, preserved aria-labels, scoped triggers next to the freshness popover and the base-ref combobox) matches existing conventions, and no wrapped control sits under a [data-slot="button"]/[data-slot="toggle"] themed container or a Group, so the trigger's data-slot takeover has no styling fallout here.
Posted via Macroscope — UI Consistency
Dismissing prior approval to re-evaluate 4b89f77
There was a problem hiding this comment.
Reviewed the tooltip migration against the shared Tooltip contract and the affected call sites. The earlier findings (double trigger on the reviewer avatars, the reordered base-freshness warning, the combobox/popover hover collisions, the unreachable disabled-menu-item reasons, and the unlabeled remote-only icon) are all addressed in this revision, and the disabled-item pattern now matches the RightPanelTabs precedent (data-disabled:pointer-events-auto + trigger rendered onto the item).
One remaining item: the mini-player resize handle now carries hover chrome.
Posted via Macroscope — UI Consistency
Dismissing prior approval to re-evaluate 774d434
## What's Changed * feat(lint): ban native title tooltips and migrate to styled Tooltip by @UtkarshUsername in pingdotgg/t3code#7209 **Full Changelog**: pingdotgg/t3code@v0.0.34-nightly.20260816.1110...v0.0.34-nightly.20260816.1112 Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.34-nightly.20260816.1112
Re-applies the deltas that mid-stack blob reverts discarded, and merges main's round-9 work into the v2-owned surfaces: - settings: main's Integrations page (#7082) coexists with the branch's Scheduled Tasks page in the path union, section labels, icons, and search catalog - contracts: main's preview appearance/zoom/viewport settings imports restored beside the branch's modelSelection home for ModelSelection - mobile: main's built-in themes (#6619) re-applied to the v2 thread screens and work log (useThemeColor over hand-rolled color-scheme ternaries) - MessagesTimeline: main's #7157 cleanup adopted (toolCallExpandedBody class name unexported, implementation-detail test dropped) - ChangedFilesTree: main's styled tooltip (#7209) carrying the v2 runId - pullRequestDetail tests: branch's row-action coverage renamed onto main's buildAddSelectionToAgentHandoff (#6597) - lint: migrated the six branch-owned native title tooltips that main's new no-native-title-tooltip rule (#7209) flags to styled Tooltips (GitActionsControl, QueuedRunsControl, TimelineSystemDivider, MessagesTimeline intent badge and MCP tool logo) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Upstream pingdotgg#7209 added t3code(no-native-title-tooltip), which is an error-level rule banning the native title attribute on intrinsic elements. The composer activity row was the only carried change still using one. The row keeps its hover hint, now as a Tooltip popup, and gains an aria-label that the native attribute was previously supplying. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Upstream added the `no-native-title-tooltip` oxlint rule in fee10de (pingdotgg#7209) and migrated its own call sites in the same commit. The fork's UpdateToast.tsx was not in that sweep -- it is fork-owned -- so the new rule turned the fork's lint red on a file upstream has never seen. Migrated to the same Tooltip/TooltipTrigger/TooltipPopup idiom upstream used, so the build-provenance affordance keeps its explanation and lint goes green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Upstream pingdotgg#7209 banned native `title` tooltips and migrated call sites to the styled Tooltip, which collided with this branch's consistency pass on nine files. Each conflict is resolved by taking upstream's Tooltip structure and re-applying the consistency fix that had been on the replaced element: - Sidebar, ChangedFilesTree, DraftHeroHeadline, PullRequestDetailPanel, ProjectSettingsPanel, ThemeColorPicker: keep upstream's Tooltip wrapper, restore `cursor-pointer` / `transition-colors` / focus ring-offset. - ThreadPreviewMiniPlayer: upstream dropped the redundant `title`; the focus-visible ring this branch added is kept. - GitActionsControl: take upstream's `min-w-0` (replaces `truncate` so StartTruncatedPath can truncate itself) plus this branch's `cursor-pointer`. - scroll-area: keep the shared SCROLL_FADE_SIZE_CLASS constant and fold in upstream's new `scroll-p-[var(--fade-size)]`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Upstream added the `no-native-title-tooltip` oxlint rule in fee10de (pingdotgg#7209) and migrated its own call sites in the same commit. The fork's UpdateToast.tsx was not in that sweep -- it is fork-owned -- so the new rule turned the fork's lint red on a file upstream has never seen. Migrated to the same Tooltip/TooltipTrigger/TooltipPopup idiom upstream used, so the build-provenance affordance keeps its explanation and lint goes green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

What Changed
Migrated Tooltips:
Sidebar / global chrome
Settings
<code>(truncated, full URL on hover)<p>Pull requests
<code>; "Copy pull request branch" / "Copied" (copy-branch button)Chat / composer
Preview / files
Diff
Host picker / search
Browser device toolbar
Why
AI models keep using the native tooltip instead of the app's styled tooltip component. This has regressed a few times. Native title tooltips don't match the app's look and can't be styled. The rule is scope-tight so it catches real native tooltips without false-positiving on accessibility uses or component props, and it's wired in as an error so future regressions fail CI.
UI Changes
Checklist
Note
Low Risk
Broad but mechanical UI-only changes with no auth, data, or API behavior; main risk is minor tooltip/hover regressions on disabled controls or truncated text.
Overview
Adds
t3code/no-native-title-tooltipas an oxlint error so intrinsic JSX elements cannot usetitlefor hover hints (with carve-outs for accessibility names likeiframeand for custom components). Existing web UItitletooltips are replaced withTooltip/TooltipTrigger/TooltipPopupacross sidebar, chat, diff, PR views, settings, search, file preview, and browser toolbar.Disabled menu items that used
titlefor “unavailable” reasons now wrap the item in a tooltip and usedata-disabled:pointer-events-autoso hover still works.PullRequestActorLabelgains an optionaltooltip={false}to avoid double tooltips where callers wrap avatars themselves. A few redundant native titles are dropped (e.g. scroll-to-end label); the mini-player resize handle keepsaria-labelonly.Reviewed by Cursor Bugbot for commit 774d434. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Ban native
titletooltips across the web app and migrate to styledTooltipcomponentst3code/no-native-title-tooltipoxlint rule (no-native-title-tooltip.ts) that errors ontitleattributes used as tooltips on intrinsic JSX elements, registered in vite.config.ts.titletooltip usages across pull request, chat, sidebar, settings, search, file preview, and browser toolbar components to useTooltip+TooltipTrigger+TooltipPopup.titleto show unavailability reasons now applydata-disabled:pointer-events-autoso custom tooltips can still trigger.Tooltipcomponent behavior rather than the browser's native behavior.Macroscope summarized 774d434.