Skip to content

feat(lab): open InfoTip on tap - #4408

Open
AKnassa wants to merge 1 commit into
facebook:mainfrom
AKnassa:ak-3349-infotip-touch
Open

feat(lab): open InfoTip on tap#4408
AKnassa wants to merge 1 commit into
facebook:mainfrom
AKnassa:ak-3349-infotip-touch

Conversation

@AKnassa

@AKnassa AKnassa commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

What this does

InfoTip can now be opened by tapping it. Before this, on a touch device it could not be opened at all.

Why

The RFC's own accessibility section requires "tap toggles the tooltip". The component merged without it, wiring only keyboard, blur and mouse-leave.

Core doesn't fill the gap either — and it's worse than "hover just doesn't fire". useTooltip deliberately bails out of hover-open under (hover: none), and focus-open is gated on :focus-visible, which a tap does not satisfy. So on a phone the info icon was inert.

What changed

The two-state dismissed flag becomes auto / pinned / dismissed, driving the controlled isOpen channel the component already owns.

  • Clicking pins unconditionally rather than toggling on what's currently visible. With a mouse the pointer is by definition over the trigger at click time, so a toggle would almost always read "open" and close it — and since mouseenter has already fired it wouldn't re-open. Pinning also makes the gesture independent of whether the 200ms hover delay had elapsed.
  • A pinned tip survives pointer-leave (that's the whole value of pinning) and closes on a second click, Escape, or focus leaving.
  • Closing routes through dismissed, never back to autoauto only stops overriding Tooltip and cannot close an open layer, so returning to it would leave the tip stuck open. That's the sharpest edge in the change and is commented in-file.

Scope question for a reviewer

A reviewer arguing this belongs in Tooltip instead has a strong case, and I'd rather say so than not. Core already owns the touch policy — it's what suppresses hover under (hover: none) — and it left no escape hatch, so every future icon-button-plus-Tooltip pairing inherits the same dead end and will re-invent this state machine.

Against that: Tooltip is core and consumed everywhere, a click path there could collide with triggers that have their own click semantics, and it needs an API decision adjacent to this RFC's unresolved Option A/B question. So the lab-scoped version is the right first move and is cheap to delete if redirected — but treat it as a candidate for graduation to Tooltip, not the final home.

Deliberately not included

Outside-click dismissal. An outside tap fires both pointerdown and blur; if those batch, the net state lands on auto — stuck open, which is the worst possible outcome on exactly the devices this fixes. Tap-to-toggle alone satisfies the RFC and keeps one handler per event.

The RFC's open questions (Option A vs B, toggletip semantics, lab→core graduation) are untouched.

How to check it

pnpm vitest run packages/lab/src/InfoTip/ packages/core/src/Tooltip/ — 29 tests, green, no collateral on Tooltip. Full repo suite run alone matches baseline exactly.

The tests were mutation-tested, and that caught a fake: the original "keeps pinned" test passed even when handleMouseLeave was mutated to unconditionally reset, because auto maps to isOpen: undefined and core no-ops on that. An extra test now kills that mutant; all four mutants die.

Not verified on a real device — jsdom does no layout and dispatches no real touch events, so fireEvent.click is synthetic. Every touch claim here is reasoned from core's source, not measured. One known limit: while pinned, core's document-level Escape listener is disabled, so Escape relies on the trigger having focus — Safari and Firefox on macOS don't focus buttons on click, so there a second click closes it but Escape won't. A document-level listener was deliberately not added, since one keypress would then close both the tip and any enclosing dialog.

No changeset: packages/lab is private, and the changeset check rejects private packages outright.

Refs #3349

InfoTip's own RFC accessibility section requires "tap toggles the tooltip",
but the shipped component wired only keydown, blur and mouseleave. Core does
not fill the gap either — `useTooltip` deliberately bails out of hover-open
under `(hover: none)` and gates focus-open on `:focus-visible`, which a tap
does not satisfy — so on a touch device the InfoTip could not be opened at
all.

Replace the two-state dismissed flag with `auto` / `pinned` / `dismissed`,
driving the controlled `isOpen` channel the component already owns. Clicking
pins unconditionally rather than toggling on perceived visibility, so the
gesture does not depend on whether the 200ms hover delay has elapsed; a
pinned tip survives pointer-leave and closes on a second click, Escape, or
focus leaving. Closing must route through `dismissed` rather than back to
`auto`, since `auto` only stops overriding Tooltip and cannot close an open
layer.

Outside-click dismissal is deliberately not included: an outside tap fires
both pointerdown and blur, and if those batch the net state is `auto` —
stuck open, the worst outcome on the very devices this fixes.

Refs facebook#3349
@vercel

vercel Bot commented Jul 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
astryx Ready Ready Preview, Comment Jul 26, 2026 5:09pm

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Jul 26, 2026
@github-actions github-actions Bot added community Authored by a community contributor (not on the eng/design team) needs:code-review High-risk change (new package/component/API) — needs human code review before merge labels Jul 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

PR Analysis Report

No new or modified components detected.

Bundle Size Summary

Package Size (ESM) Size (CJS) Gzipped
@astryxdesign/core N/A 4.7KB 0B

Accessibility Audit

Status: No accessibility violations detected.


Generated by PR Enrichment workflow | View full report

@AKnassa
AKnassa marked this pull request as ready for review July 30, 2026 05:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot. community Authored by a community contributor (not on the eng/design team) needs:code-review High-risk change (new package/component/API) — needs human code review before merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant