Repository navigation
spec: Tooltip and HoverCard pin on press; InfoTip is the info preset - #9175
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
/approve-spec 3f754ca |
|
AI review status for this pull request.
|
|
/approve-spec d6f2043 |
|
/approve-spec 2b5215d |
|
/approve-spec d6ea116 |
There was a problem hiding this comment.
Requesting changes: the press-to-pin direction is approved for this head, but four record issues need fixing before it can merge. Please rebase onto main and fix the points below; the rebased head will need a fresh /approve-spec.
FieldLabel.spec.mdFR9 and DEC-1 reuse the IDs that #9089 just made current onmainwith the opposite mouse-click rule, and the branch no longer merges cleanly.docs/families/overlay-dismissal.mdFR8 still says a Tooltip never wins or blocks Escape, which contradicts the new AST-003 FR4 and DEC-6 for a pinned Tooltip.- The Tooltip and HoverCard compatibility sections, and CLN-0024, describe the default as unchanged, but FR5 changes what a tap does under
touchTrigger="auto". - The new
HoverCard.doc.mjsguidance describes the innermost-control and disabled-control tap rule, which the shipped component does not follow yet.
[Automated review]
| ## Decision log | ||
|
|
||
| No component-local decision. The label-element and ref typing is OQ1. | ||
| ### DEC-1 — The info trigger is an InfoTip beside the label |
There was a problem hiding this comment.
main now has a different FieldLabel FR9 and DEC-1 (from #9089) that keep native label activation for a mouse click. After the rebase, replace those entries with this rule, or renumber these (FR10, DEC-2) and mark main's mouse-click clause superseded, so the record states one rule. Current-state impact (line 175) and the FR9 verification row need the same update.
| - **FR4 — Passive dependents never shield owners.** Tooltip is passive. It does | ||
| not win or block dismissal and disappears as a consequence when its associated | ||
| owner or trigger interaction closes. | ||
| - **FR4 — Passive dependents never shield owners.** An unpinned Tooltip is |
There was a problem hiding this comment.
docs/families/overlay-dismissal.md FR8 on main still says "A Tooltip never wins or blocks an Escape press against another member (spec:AST-003 FR4)", and its matrix rows give Escape to the Dialog over a Tooltip. Please amend FR8 and those rows to except a Tooltip pinned by a press.
| targets, and public API remain unchanged | ||
| - Released default preserved: `yes`; `pressTrigger` defaults to `none`, which | ||
| keeps every shipped behavior | ||
| - Compatibility class: additive `pressTrigger`; `touchTrigger="tap"` is |
There was a problem hiding this comment.
FR5 and DEC-1 change the default touchTrigger="auto": a trigger inside a link or clickable row stops opening on the tap, and a disabled control's reason starts opening on one. Without pressTrigger the runtime does change, so this line and HoverCard's should say so with its spec:AST-017 class. CLN-0024's delta (line 100) also still describes the trigger-only rule.
| guidance: true, | ||
| description: | ||
| 'Leave touchTrigger on auto so a tap opens the card on triggers that do nothing else, and stays out of the way on triggers that perform an action.', | ||
| 'Leave touchTrigger on auto, the fallback: a tap that already acts, as the innermost control around the tapped element decides, stays with that control, and a disabled control is not an action, so its reason opens on a tap. A clickable element without a role needs one.', |
There was a problem hiding this comment.
The shipped isActionTrigger (packages/core/src/Layer/useTouchTrigger.ts) reads only the trigger and treats a disabled <button> as an action, so this guidance describes behaviour HoverCard does not have yet (FR5 lists it as an implementation gap). Keep the current wording here and in the two locale copies until the walk ships.
d6ea116 to
c2e5c15
Compare
|
/approve-spec c2e5c15 |
PR Analysis Report🔎 Preview ·
|
| Component | Size | LoC |
|---|---|---|
| Field | 45.8 kB (0) · 8.3 kB CSS (0) | — |
| FieldLabel | 39.6 kB (0) · 5.9 kB CSS (0) | — |
| HoverCard | 17.7 kB (0) · 5.0 kB CSS (0) | — |
2 more components
| Component | Size | LoC |
|---|---|---|
| StatusDot | 19.6 kB (0) · 6.3 kB CSS (0) | — |
| Tooltip | 17.4 kB (0) · 5.0 kB CSS (0) | — |
Package size: @astryxdesign/core 6.54 MB packed (+17.1 kB); no new dependencies.
Not run on this PR: add ci:run-visual, ci:run-a11y or ci:run-rtl to run them.
Generated by PR Enrichment workflow | View full report
There was a problem hiding this comment.
Requesting changes: the press-to-pin direction is approved for this head and the earlier record issues are fixed, but the default touchTrigger="auto" change needs a different release class. Please address the points below; the new head will need a fresh /approve-spec.
- FR5's walk is filed as an AST-017 FR53 patch fix in the Tooltip and HoverCard Compatibility sections, but FR53 excludes changes that need app code, and a hint-only trigger inside a clickable row, card, link or label would need
pressTrigger="toggle"to stay reachable on touch; classify that part under FR1 with CLN-0024's minor. - Core StatusDot uses the default
auto, so its FR5 promise that the tooltip opens on touch stops holding inside a control; adopt the toggle there no later than the walk ships and amend StatusDot FR5. - The description's user impact names status dots and timestamps, which these records do not change; narrow it to InfoTip and FieldLabel's info button.
[Automated review]
| - Compatibility class: records shipped behavior; runtime, DOM, styling, | ||
| targets, and public API remain unchanged | ||
| - Released default preserved: the defaults stay `pressTrigger="none"` and | ||
| `touchTrigger="auto"`, but FR5 changes what a tap does under `touchTrigger="auto"`: a tap that already acts does not open the layer, where the released `auto` opens it on any trigger that does not itself act, and a disabled control's reason opens on a tap. That change ships in a patch as `[fix]` under `spec:AST-017` FR53, because it restores `architecture:interaction-modality` INV4 (a tap on an action trigger performs the action, and every modality reaches the reason); a tap destination INV4 does not cover would ship as a `[feat]` patch |
There was a problem hiding this comment.
AST-017 FR53 covers only a fix that "requires no app-layer code change"; a change that needs app edits "is classified under FR1". The released auto opens on a tap for any trigger that does not itself act, so a hint-only trigger inside a clickable row, card, link or label opens today. Under FR5 it stays shut, and only a button trigger with pressTrigger="toggle" brings the touch path back. Core StatusDot (default auto) is one such caller, and its FR5 says its tooltip opens on touch. Please classify the inside-a-control walk under FR1 with CLN-0024's minor, keeping the disabled-reason part as the FR53 patch.
| - Released default preserved: `yes` | ||
| - Compatibility class: records shipped behavior; no public change | ||
| - Released default preserved: the defaults stay `pressTrigger="none"` and | ||
| `touchTrigger="auto"`, but FR5 changes what a tap does under `touchTrigger="auto"`: a tap that already acts does not open the layer, where the released `auto` opens it on any trigger that does not itself act, and a disabled control's reason opens on a tap. That change ships in a patch as `[fix]` under `spec:AST-017` FR53, because it restores `architecture:interaction-modality` INV4 (a tap on an action trigger performs the action, and every modality reaches the reason); a tap destination INV4 does not cover would ship as a `[feat]` patch |
There was a problem hiding this comment.
Same release-class issue as Tooltip's Compatibility line: a hint-only card trigger inside a clickable row or card loses its tap under FR5, and only app code (pressTrigger="toggle" on a button trigger) restores it, which AST-017 FR53 excludes from a patch fix.
| | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | id | `CLN-0024` | | ||
| | lifecycle | `DEP-0016` | | ||
| | delta | `touchTrigger` is removed from `Tooltip`, `HoverCard`, `useTooltip`, and `useHoverCard`. `pressTrigger` takes `auto`, `toggle`, and `none`, default `auto`: `auto` is the released `touchTrigger="auto"` (a tap opens the layer unless the trigger performs its own action, and no press pins it), `none` is the released `touchTrigger="none"` (no tap or press opens it), and `toggle` is unchanged. `auto` keeps FR5's rule that a tap that already acts does not open the layer, which ships earlier as a patch (Compatibility and migration). One prop decides what a press does | |
There was a problem hiding this comment.
This defines auto as "a tap opens the layer unless the trigger performs its own action", the released rule FR5 replaces, and then says auto keeps FR5's rule. Please describe the rule auto will actually carry after the fold.
|
/approve-spec 7e15ea5 |
…ay-dismissal FR8; FR5 auto change is a patch fix; revert HoverCard docs
…eclares the toggle
…ontrol hands its text to the host
… a control speaks through the host's name
…hosts carry the status; pinned layers in overlay-dismissal rows
…ot hit area; InfoTip cites DEC-1
… hit area where neighbors leave room; unescape INV4 row
… lines); InfoTip hit area where neighbors leave room
…atusDot FR5 opens with the placement split
…xt sites are reported
…mpact stops claiming a blanket rewrite
e36deb8 to
b67fa3a
Compare
|
/approve-spec b67fa3a |
There was a problem hiding this comment.
Requesting changes: the press-to-pin direction is owner-approved for this head and the previous round's CLN-0024 and Builder impact points are fixed, but the new button rule takes away the touch path this pull request sets out to add. CLN-0024 drops touchTrigger="tap" from every button or link trigger at the next minor (Tooltip.spec.md:109-110), yet tap is how a button whose only job is its tooltip opens on a tap today (the Lab InfoTip and the Tooltip story do this), and released auto treats any button as an action, so that help stops opening on touch, which architecture:interaction-modality INV4 rules out. Please name pressTrigger="toggle" as the replacement for such buttons, have the minor's codemod report button and link tap sites instead of removing tap, and fix the codemod verification row (:302), which fails the minor's own rewrites; once fixed, an engineering owner records the decision on the new head with /approve-spec <full head SHA>, which the author can post as an engineering owner.
[Automated review]
| | id | `CLN-0024` | | ||
| | lifecycle | `DEP-0016` | | ||
| | delta | `touchTrigger` is removed from `Tooltip`, `HoverCard`, `useTooltip`, and `useHoverCard`. `pressTrigger` takes `auto`, `toggle`, and `none`, default `auto`: `auto` is the released `touchTrigger="auto"` (a tap opens the layer unless the trigger performs its own action, and no press pins it), `none` is the released `touchTrigger="none"` (no tap or press opens it), and `toggle` is unchanged. `auto` gains FR5's walk: a tap that already acts on a control around the trigger does not open the layer. A hint-only trigger inside a control migrates by declaring `pressTrigger="toggle"`, which no codemod can decide; Astryx's own info-only triggers (InfoTip, FieldLabel's info button, and a standalone StatusDot) declare it before this minor; a StatusDot inside a button, link, menu item, or option stays an image whose status the host's accessible name carries. One prop decides what a press does | | ||
| | replacement | Each remaining `touchTrigger="tap"` site has a named replacement (`spec:AST-017` FR8): a button or link trigger drops it and takes `auto`, so a tap performs the action and opens no tooltip (`architecture:interaction-modality` INV4); a trigger whose tooltip repeats its own text takes `pressTrigger="toggle"` (FR9's own-text exception); an unknown element is reported for a manual choice between those two | |
There was a problem hiding this comment.
A button whose only job is its tooltip (the Lab InfoTip's trigger, the Tooltip story's "?" button) carries tap because released auto treats a <button> as an action. Dropping tap there leaves its help with no touch path, against architecture:interaction-modality INV4, DEP-0016's replacement (:92) and this record's own delta (:108). Please name pressTrigger="toggle" for a button whose only job is the layer, keep "drop it" for a button that acts and whose name carries the text, and have the codemod (:110) report button and link sites instead of removing tap.
| | AR5 | `Tooltip.a11y.test.tsx`; `Tooltip.a11y.chromium.spec.ts` | Wrapped, text, anchor, and hook triggers | A lost role, description, keyboard-focus show, or Escape dismissal fails; focus-leave hide, hover show, and leave hide run as advisory rows (Current-state impact). | `audit:Tooltip/accessibility` | | ||
| | FR5 | `Tooltip.test.tsx` touch suite; `TouchTrigger.a11y.chromium.spec.ts`; `useTouchTrigger.test.ts`, with tap-destination cases an evidence gap until built | Tap open, scroll, second tap, tap outside; a trigger inside a link, a clickable row, or a clickable card; a trigger in running text; a disabled control inside a clickable row | A tap that fails to open or close as stated, opens the tooltip on a tap that already acts, or leaves a disabled control's reason shut fails. | `audit:Tooltip/behavior` | | ||
| | FR9, FR10, AR6 | Evidence gap until built: a press-toggle suite in `Tooltip.test.tsx` and `useTooltip.test.tsx`, and a real-touch and keyboard case in `TouchTrigger.a11y.chromium.spec.ts` | Click, Enter, Space, and tap pin; second press, press outside, focus leaving, and Escape close; pointer leave keeps the pin; controlled; non-button trigger; a truncated Text trigger whose tooltip repeats its text; a toggle trigger inside a ClickableCard, an Item row, and a `<label>` | A press that does not pin or close as stated, a pin lost on pointer leave, a missing or stale `aria-expanded` or `aria-controls`, a second announcement, a non-button trigger without the warning or its tap fallback, an own-text trigger that warns, gets `aria-expanded`, or is announced, or a pinning press that also activates the card, row, or labelled control around the trigger, fails. | `audit:Tooltip/behavior` | | ||
| | DEP-0016, CLN-0024 | Evidence gap until built: the codemod's tests and fixtures beside its transform, and the codemod verify workflow | `touchTrigger="tap"` on an icon and a span, and on an own-text trigger, a button, a link, and an unknown element; `"auto"` and `"none"`; `pressTrigger` beside `touchTrigger`; spreads and dynamic values | A rewrite that changes behavior, a rewritten own-text, button, link, or unknown-element site, a reported site without its manual-migration note, a missed static use, or a silently skipped dynamic one fails. | `audit:Tooltip/api` | |
There was a problem hiding this comment.
This row covers both codemods, but CLN-0024's codemod rewrites button, link and own-text sites by design (:110), so "a rewritten own-text, button, link, or unknown-element site ... fails" fails it. Please split the row by release, and add a failure for a remaining tap the minor neither rewrites nor reports.
…emod reports button and link tap sites
|
/approve-spec 63115d1 |
There was a problem hiding this comment.
The press-to-pin direction is owner-approved for this head, and the info-only button rule and codemod verification row now read correctly; one record rule still needs a change. CLN-0024 (Tooltip.spec.md:109-110) tells every button or link with its own action to drop touchTrigger="tap" for auto, but the INV4 sentence this pull request adds exempts only triggers whose accessible name already carries the layer's text, so a tooltip hint or hover card on any other action trigger loses its only touch path. Please limit that replacement to INV4's case and name one that keeps a touch path for the rest (for example an adjacent InfoTip) in both the replacement and the minor's codemod note, then post a fresh /approve-spec for the new head; smaller wording fixes are listed in the review.
[Automated review]
| | id | `CLN-0024` | | ||
| | lifecycle | `DEP-0016` | | ||
| | delta | `touchTrigger` is removed from `Tooltip`, `HoverCard`, `useTooltip`, and `useHoverCard`. `pressTrigger` takes `auto`, `toggle`, and `none`, default `auto`: `auto` is the released `touchTrigger="auto"` (a tap opens the layer unless the trigger performs its own action, and no press pins it), `none` is the released `touchTrigger="none"` (no tap or press opens it), and `toggle` is unchanged. `auto` gains FR5's walk: a tap that already acts on a control around the trigger does not open the layer. A hint-only trigger inside a control migrates by declaring `pressTrigger="toggle"`, which no codemod can decide; Astryx's own info-only triggers (InfoTip, FieldLabel's info button, and a standalone StatusDot) declare it before this minor; a StatusDot inside a button, link, menu item, or option stays an image whose status the host's accessible name carries. One prop decides what a press does | | ||
| | replacement | Each remaining `touchTrigger="tap"` site has a named replacement (`spec:AST-017` FR8): a button or link whose only job is its tooltip takes `pressTrigger="toggle"`, so its tooltip keeps a touch path; a button or link with its own action drops `tap` and takes `auto`, so a tap performs the action and opens no tooltip; a trigger whose tooltip repeats its own text takes `pressTrigger="toggle"` (FR9's own-text exception); an unknown element is reported for a manual choice (`architecture:interaction-modality` INV4) | |
There was a problem hiding this comment.
"A button or link with its own action drops tap and takes auto" also covers action triggers whose tooltip or hover card holds content their accessible name does not carry (an icon button named "Sync" whose tooltip says "Last synced 5 minutes ago", a name link with a profile card). The released tap (:91) is their only touch path, and the INV4 sentence this pull request adds exempts only "a tooltip that labels an action trigger whose accessible name already carries its text". Could this row limit the drop to that case and name a replacement that keeps a touch path for the rest (for example, move the content to an adjacent InfoTip), with the codemod note on :110 offering it?
…oves it to an InfoTip; review wording fixes
|
/approve-spec 4d77b9f |
There was a problem hiding this comment.
The press-to-pin direction is owner-approved for this head, and CLN-0024 now gives every remaining touchTrigger="tap" site a replacement that keeps a touch path, so these records are ready to merge. A few small wording follow-ups can ride the implementation pull requests:
- CLN-0024's codemod note offers HoverCard sites an adjacent InfoTip, which only holds short, noninteractive help; the replacement row's "adjacent info trigger that declares
pressTrigger="toggle"" fits both layers. component:Field's theming anatomy still delegates the label tooltip trigger toIcon/icon, which changes once FieldLabel's InfoTip button is built.
[Automated review]
| | lifecycle | `DEP-0016` | | ||
| | delta | `touchTrigger` is removed from `Tooltip`, `HoverCard`, `useTooltip`, and `useHoverCard`. `pressTrigger` takes `auto`, `toggle`, and `none`, default `auto`: `auto` is the released `touchTrigger="auto"` (a tap opens the layer unless the trigger performs its own action, and no press pins it), `none` is the released `touchTrigger="none"` (no tap or press opens it), and `toggle` is unchanged. `auto` gains FR5's walk: a tap that already acts on a control around the trigger does not open the layer. A hint-only trigger inside a control migrates by declaring `pressTrigger="toggle"`, which no codemod can decide; Astryx's own info-only triggers (InfoTip, FieldLabel's info button, and a standalone StatusDot) declare it before this minor; a StatusDot inside a button, link, menu item, or option stays an image whose status the host's accessible name carries. One prop decides what a press does | | ||
| | replacement | Each remaining `touchTrigger="tap"` site has a named replacement (`spec:AST-017` FR8): a button or link whose only job is its tooltip takes `pressTrigger="toggle"`, so its tooltip keeps a touch path; a button or link with its own action whose accessible name already carries the layer's text drops `tap` and takes `auto`, so a tap performs the action and opens no layer (`architecture:interaction-modality` INV4's case); any other button or link with its own action moves the layer's information to an adjacent info trigger that declares `pressTrigger="toggle"`, such as an InfoTip, or into its visible label, so that information keeps a touch path, and then drops `tap`; a trigger whose tooltip repeats its own text takes `pressTrigger="toggle"` (FR9's own-text exception); an unknown element is reported for a manual choice (`architecture:interaction-modality` INV4) | | ||
| | codemod | The minor's `astryx upgrade` codemod, with tests and fixtures: `touchTrigger="none"` becomes `pressTrigger="none"`; `touchTrigger="auto"` and the released `pressTrigger="none"` are removed, since `auto` reproduces them; `pressTrigger="toggle"` is kept and any `touchTrigger` beside it removed; a remaining `touchTrigger="tap"` becomes `pressTrigger="toggle"` on an own-text trigger the codemod can identify; every button and link `tap` site is reported with the options in its note (`pressTrigger="toggle"` when the trigger's only job is its layer; remove `tap` for `auto` when the trigger's accessible name already carries the layer's text; otherwise move the information to an adjacent InfoTip or the visible label, then remove `tap`), since no static check tells them apart, and every other remaining `tap` site is reported; spreads and dynamic values are reported | |
There was a problem hiding this comment.
Minor: HoverCard DEC-1 applies CLN-0024 unchanged, so a HoverCard tap site on an action link gets this note too, but "an adjacent InfoTip" cannot hold a card, since InfoTip takes only short, noninteractive help (InfoTip FR3). The replacement row's wording, "an adjacent info trigger that declares pressTrigger="toggle"", covers both layers; could the note use it?
Exact decision needed
How a person on touch, keyboard, or a screen reader keeps a tooltip open long enough to read it, and how an info icon offers that help. Today the help behind an info icon, a field label's info icon, or a status dot opens on hover, and a tap opens it when the trigger performs no action (the default
touchTrigger="auto") or always withtouchTrigger="tap"; but a scroll or a tap elsewhere closes it, no mouse or keyboard press keeps it open, and neither the field label's icon nor the status dot can be reached with Tab.Existing authority searched
component:Tooltip,component:HoverCard: hover, focus, and tap opening (touchTriggerauto|tap|none); no press-to-pin for a mouse or keyboard.spec:AST-003FR4: every Tooltip is passive in Escape order, so a tooltip someone chose to keep open could not take Escape before a Dialog beneath it.component:FieldLabelFR8, AR5: the label tooltip must be keyboard-reachable through a named trigger; the shipped icon is neither.component:StatusDotFR5: an info-only trigger whose information lives only in its tooltip, with no keyboard path.InfoTip: a named button around Tooltip withtouchTrigger="tap"; no Core record.architecture:public-component-apiINV9, INV21;spec:AST-017FR28–FR29d (deprecation, codemods);spec:AST-094FR42 (touch hit area);component:MoreMenu(a component built entirely on another).architecture:interaction-modalityINV4: every modality has an operable path; it said nothing about a tap on an action trigger that also carries a tooltip.No current record lets a mouse or keyboard press keep a tooltip or hover card open, or says what an info icon is.
Decision
User impact: people on a phone, a keyboard, or a screen reader can open the help behind an InfoTip, a field label's info button, or a standalone StatusDot, keep it open while they read, and close it; a StatusDot inside a button, link, menu item, or option tells screen-reader users its status through the host's accessible name; and asking what a setting does never changes the setting.
pressTrigger: 'none' | 'toggle'(default'none'), onTooltip,HoverCard,useTooltip, anduseHoverCard. With'toggle', hover and keyboard focus still preview the layer; a click, tap, Enter, or Space on a button trigger pins it open; Escape, a second press, a press outside, or focus moving away closes it; a pinned layer stays open when the pointer leaves. The trigger carriesaria-expanded(true only while pinned) andaria-controls, and the layer is announced once through a polite live region when it pins. A trigger whose role is notbuttongets a development warning and keeps tap-only opening. A controlled layer reports the toggled value throughonOpenChange(component:TooltipFR9, FR10, AR6, DEC-1;component:HoverCardFR9, FR10, AR3, DEC-1).spec:AST-003FR4 and DEC-6: an unpinned Tooltip stays passive and never wins or blocks Escape; a Tooltip or HoverCard pinned by a press is an active owner, so Escape closes it before the Dialog beneath it.spec:AST-090FR5: a leaving unpinned Tooltip stays passive; a pinned Tooltip or HoverCard that is closing plays its exit and leaves the dismissal stack when the exit starts, so the member beneath takes the next Escape. Its layer-runtime and overlay-dismissal impact lines carry the same exception.component:InfoTipin Core: a named button with the registry's info icon, wrapped in Tooltip withpressTrigger="toggle", with no layer, listener, or dismissal rule of its own. Props:content,label(a translated "More information" by default), andsize(sm). It is one API with Tooltip under INV21, as MoreMenu is with DropdownMenu.pressTrigger="toggle"where the layer is the only home of their information. UndertouchTrigger="auto", the fallback until the fold, a tap that already acts does not open the layer: walking out from the tapped element, the innermost control decides (a button, link, label, form control, option, menu item, or clickable row or card, the trigger included), and a disabled oraria-disabledcontrol is not an action, so its reason opens on a tap. A role-lessonClickelement needs a role (component:TooltipFR5, DEC-1;component:HoverCardFR5, DEC-1).architecture:interaction-modalityINV4: a tooltip that labels an action trigger whose accessible name already carries its text is not a separate interaction, so a tap performs the action; hint-only information is reachable on touch throughpressTrigger="toggle".pressTrigger="toggle"with no development warning, noaria-expandedoraria-controls, and no announcement (component:TooltipFR9).labelTooltiprenders an InfoTip after the label element, outside it, named with the label text ("About Auto-save"). FR9 and DEC-1 are amended in place: a tap, click, Enter, or Space on it toggles the help and never activates, focuses, or toggles the control, reversing the mouse-click activation FR9 kept. With alabelTooltip, one inline-flex row wraps the label element and the InfoTip, as FieldLabel already wraps the label and description; callers see that extra wrapper only whenlabelTooltipis set, and their class,xstyle, and ref stay on the label element. WithisLabelHidden, the InfoTip is visually hidden with the label, takes no layout space, and becomes visible while it has keyboard focus. FR8, AR5, and the mouse-click part of FR9 stay implementation gaps until this is built (component:FieldLabelFR8, FR9, AR5, DEC-1).pressTrigger="toggle", adopted no later than FR5's walk ships, so its tooltip is reachable on touch and by keyboard; under a coarse pointer it reaches 44 px through the shared invisible hit area where its neighbors leave room (AST-094 FR42,component:StatusDotAR2). A dot inside a button, link, menu item, or option stays an image whose tooltip previews on hover only; sighted touch users lose its visible tooltip text there, and its status reaches screen-reader users through the host's accessible name, since a control cannot nest another control; a host named byaria-labelincludes the status in that label (component:StatusDotFR5, AR1, AR2).family:overlay-dismissalFR8 and its Escape table: an unpinned Tooltip never wins or blocks Escape; a pinned Tooltip or HoverCard takes it first.Migration notes
FR5's walk (under the default
touchTrigger="auto", a tap that already acts on a control around the trigger does not open the layer) needs app code where a hint-only trigger sits inside a control, so it is breaking under AST-017 FR1 and ships with CLN-0024 in the next minor. No caller's behavior changes in a patch: until thenautokeeps its released behavior, except that a disabled control's reason opens on a tap, which ships separately as fix(Tooltip, HoverCard): a disabled control's reason opens on a tap #8207's patch[fix]. Hint-only triggers inside controls migrate by declaringpressTrigger="toggle"; InfoTip, FieldLabel's info button, and StatusDot do so before the minor.touchTrigger="tap"is deprecated in the same release that addspressTrigger(component:TooltipDEP-0016), with static@deprecatedmetadata and no runtime warning. When both are given,pressTrigger="toggle"wins.A codemod ships through the existing
astryx upgradepath, staged inpackages/cli/assets/codemods/transforms/next/with tests and fixtures and checked by the codemod verify workflow, in the release that deprecatestap: it rewritestouchTrigger="tap"topressTrigger="toggle"only where the trigger is statically a non-interactive icon or span, where the result is observably the same because Tooltip FR9 keeps a non-button toggle trigger on tap-only opening; it reports every othertapsite (an own-text trigger, a button, a link, or an unknown element) with a manual-migration note and leaves it unchanged, since the toggle changes what a click, Enter, or Space does there (and on a button addsaria-expanded); it removestouchTrigger="auto"and reports spreads and dynamic values. The deprecatedtapkeeps working until the minor.At the next minor,
touchTriggerfolds intopressTriggerand is removed (CLN-0024):pressTriggertakesauto|toggle|none, defaultauto, whereautois today'stouchTrigger="auto"andnoneis today'stouchTrigger="none". Each remainingtouchTrigger="tap"site gets a named replacement there: a button or link whose only job is its tooltip takespressTrigger="toggle"(it keeps its touch path); a button or link with its own action whose accessible name already carries the layer's text dropstapand takesauto(a tap performs the action and opens no layer); any other button or link with its own action moves the layer's information to an adjacent InfoTip (or another info trigger withpressTrigger="toggle") or into its visible label, then dropstap; a trigger whose tooltip repeats its own text takespressTrigger="toggle"; an unknown element is reported for a manual choice. That minor's codemod rewritestouchTrigger="none"topressTrigger="none", removestouchTrigger="auto"and today's explicitpressTrigger="none", keepspressTrigger="toggle", rewrites own-text sites it can identify, reports every button and linktapsite with all three options in its note (no static check tells them apart), and reports every other remainingtapsite and dynamic value.Lab
InfoTipcallers change the import to@astryxdesign/core; the Lab export stays one release as a deprecated re-export.Rejected alternatives
touchTrigger="tap"to also pin on a mouse click and keys: a touch-named prop would then govern mouse and keyboard.Claim scope and non-goals
pressTrigger="toggle"is a follow-up under its own record.Evidence and implementation relationship
Writing rubric results
Run over
component:Tooltip,component:HoverCard,component:FieldLabel,component:StatusDot, the newcomponent:InfoTip,spec:AST-003,architecture:interaction-modality,family:overlay-dismissal, andspec:AST-090, andspec:AST-082(FR3's example now namespressTrigger).auto, which the template asks for. DEP-0016'sstate: proposedis the lifecycle field value, not narration.Scope
Validation
pnpm check:knowledge: "Knowledge templates and records are aligned."pnpm check:repo: passes.git diff --check: clean.