Skip to content

docs(spec): Tokenizer records the experimental touch sheet presentation (DEC-3) - #7976

Merged
imdreamrunner merged 1 commit into
mainfrom
spec/tokenizer-experimental-presentation
Oct 26, 2026
Merged

imdreamrunner merged 1 commit into
mainfrom
spec/tokenizer-experimental-presentation

Conversation

@imdreamrunner

@imdreamrunner imdreamrunner commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Based on main; spec:AST-086 FR14, which AR6 cites, is current there.

Exact decision needed

Whether Tokenizer offers its touch surface — a trigger that opens one searchable modal bottom sheet in place of the inline combobox — and through which public boundary. The record is component:Tokenizer (packages/core/src/Tokenizer/Tokenizer.spec.md, current), so a new public surface needs a record amendment before the implementation lands, following the shape component:Drawer DEC-4 set for experimental.isFullWidthOnMobile.

Existing authority searched

Decision

component:Tokenizer DEC-3 (decider imdreamrunner, 2026-10-23). The record's frontmatter carries authority: current, approved_by: imdreamrunner, approved_at: 2026-10-23 from the owner's decision; the current head still needs the engineering owner's /approve-spec <full head SHA> comment, which records the decision for this head.

Two decision points settled by the owner and recorded in DEC-3:

  • Value set: adaptive, not adaptive-bottom-sheet. Tokenizer picks several items from a searchable list, so its nearest siblings are MultiSelector and Selector, which use AdaptivePresentation ('popover' | 'bottom-sheet' | 'adaptive'). The date and time fields use PickerPresentation (utils/inputPresentation.ts) because they have a native-picker axis that Tokenizer lacks. DEC-3 carries this as a one-line rejected alternative.

  • No new theming targets while experimental. The record says it twice, in Theming and in DEC-3: the sheet presentation exposes the field's existing tokenizer target on the trigger; the sheet's own parts carry no Tokenizer theming targets while the option is experimental (spec:AST-017 FR27) and are themed through the targets of the components they compose (BottomSheet, Button, CheckboxIndicator, EmptyState, the panel search field); sheet targets follow the option's promotion.

  • Surface: experimental.presentation, 'popover' | 'bottom-sheet' | 'adaptive', inlined on the prop; no experimental hook or type is exported. popover — the value when experimental or the option is omitted — is today's inline combobox with its anchored suggestion menu, unchanged. adaptive resolves through the same shared compact coarse-pointer policy the selection controls use, so a Tokenizer and a Selector on one page switch together; a resolution change while the sheet is open applies after it closes and focus has returned to the trigger; whenever the presentation changes while its control holds focus — then, or while the sheet is closed, as when a phone rotates across the breakpoint after Done — focus moves to the new presentation's control, the combobox input or the trigger, never to the page body (FR16, spec:AST-085 FR4).

  • Scope: FR1–FR15, AR1–AR5, and the first six design rows describe the popover presentation; FR16–FR24, AR6, PR6, and the sheet rows describe the sheet presentation, which has no inline combobox, suggestion menu, or end lane — so FR17/FR19 and FR1/FR13 no longer read as contradicting one another.

  • Trigger (FR17): one button named by the field label with aria-haspopup="dialog" / aria-expanded, showing the selected tokens without remove controls or the placeholder, wrapping tokens, at the inline field's height for its size step (as Selector's bottom-sheet trigger); opens one tall modal BottomSheet named by the label. Disabled: focusable, aria-disabled, does not open, shows disabledMessage; a field that becomes disabled while its sheet is open closes the sheet and returns focus to the trigger (spec:AST-085 FR2), as FR15 closes the inline popup.

  • Base props (Public concepts): in the sheet presentation the trigger is the control — ref, id, data-*, aria-*, and the other base props reach it (family:input-fields FR10, architecture:public-component-api INV8), handleRef acts on it, onFocus/onBlur/onKeyDown report its events. The ref type follows the opt-in, and the record now says how: the exported TokenizerProps remains an interface with ref?: Ref<HTMLInputElement> as released (so interface X extends TokenizerProps<T> and Omit<TokenizerProps<T>, …> keep compiling), and the component's own parameter type is where the widening lives — with experimental passed, ref also accepts Ref<HTMLInputElement | HTMLButtonElement>. The summary table's Public contract row says the same. The props with no sheet surface (renderItem, renderToken, endContent, hasClear, hasEntriesOnFocus, menuWidth, tokenOverflowBehavior, hasAutoFocus) emit one development-mode warning naming the prop rather than being dropped silently (INV3), matching Selector's warning for an option that carries action.

  • One sheet (FR18): the shared panel search field on top, a scrolling list of rows, and — while the query is empty — a footer with Clear all and Done. No second sheet. Done closes and clears the query; swipe/scrim/Escape follow purpose="info"; focus returns to the trigger.

  • Rows (FR19, FR23): checkbox rows named by item label; toggling commits add/remove immediately and announces with the inline field's words through the sheet's own live region; a row renders an item without its action and warns once in development, as the inline panel does (spec:AST-058 FR9); order is fixed while open (selected-at-open in value order, then selected items absent from results, then results) and a toggle never moves a row; the next open re-snapshots. At maxEntries, unchecked rows are aria-disabled and inert, checked rows still toggle.

  • Sheet surface (FR18): on open, focus moves to the sheet's panel, not the search field, so the reading order starts at the top of the sheet and the on-screen keyboard rises only when the person reaches for the search (WCAG 2.2 SC 2.4.3 Focus Order and 3.2.1 On Focus through spec:AST-020 FR1; Selector's AR2 is the precedent for naming the destination); the representative states carry both FR18 rules — "open" says focus is on the panel, a new "dismissed" state says the query is empty after a swipe, scrim, Escape or Done close with focus back on the trigger — and the sheet verification row lists them; every close — Done, swipe, scrim, Escape — clears the query, so the next open lists the bootstrap set (the Lab prototype kept the query; one rule for every close is simpler to state and to test, and "Done clears it" alone never applied, since Done shows only while the query is empty).

  • adaptive on the server and at hydration (FR16, aligned with PowerSearch's FR10): adaptive server-renders and hydrates as the inline field — the resolver answers popover where there is no matchMedia and in the hydrating render — and on a compact coarse-pointer client swaps to the trigger in the first render after hydration; DEC-3 records the architecture:component-style-authoring INV7 / spec:AST-017 FR60 exception in two parts — the closed adaptive field, which renders a different element (the trigger button with the selected tokens) in place of the inline input, its token remove actions and overflow handling on a compact coarse-pointer client, removing elements rather than adding them (154 fine / 120 coarse at rest, under the fine count); and the open sheet — with measured counts (Chromium, Experimental: adaptive story: at rest 154 fine / 120 coarse; open at the query "e" with no footer: 157 fine (the inline field with its menu on the one matching item, plus the two page live regions) / 165 coarse, reconciled exactly as 120 at rest − 2 (the app-global surfaces host the open modal re-hosts) + the dialog of 47, broken down by the record's own parts — BottomSheet dialog/panel/handle/edge tint 7, sheet body 2, search row 19, list 16, live region 1, re-hosted surfaces host 2 — with a pending Clear all confirmation adding 20 and the empty-query footer 9; forced bottom-sheet 114/165 under either pointer) and the reason — a different surface replacing an anchored listbox, not the same one restyled; at rest the coarse pointer never has more elements. AV7's proviso states INV7's at-rest rule (same elements) and quotes its open-layer budget verbatim, allowance clause included; the closed adaptive trigger (a different element, which no allowance covers) and the open sheet (more than FR60's five extra elements) are named as the owner exceptions DEC-3 records beyond that allowance, with before/after counts — the same wording in the verification row. The compact-touch resolution rule is cited to its owner spec:AST-094/FR13 (compact: spec:AST-012/FR9) in the ownership bullet and FR16, and the toast outlet the open modal re-hosts to spec:AST-003/FR18; those owners join system_specs. FR20 states the empty-query bootstrap set is capped at maxMenuItems, as the inline field caps its focus bootstrap (parity; the code already did). FR16 records an architecture:layer-runtime INV14 gap: the presentation hold belongs to the layer runtime, which does not yet provide one, so the field keeps its own until it does. Earlier clause kept: AV7's proviso ("…beyond that, only by the open-layer allowance spec:AST-017 FR60 and DEC-29 set"), so DEC-3's exception reads as that allowance for the closed adaptive trigger and the open sheet; the sheet verification row lists the counts. The owner's /approve-spec covers the exception. FR20: with an empty query and nothing to list — nothing selected and an empty bootstrap set, as a search-only source has — the sheet shows no list, no message, and announces nothing, only the search field above the footer, as the inline field opens no menu and reports nothing for a bootstrap that returned nothing; "No items available" is gone from the record (the verification row lists the search-only case and the list once a query finds items). The trigger's accessible description is everything the inline input's is, in its order — the caller's aria-describedby ids, the field description, the status message, the disabledMessage reason when disabled (spec:AST-085 FR5, now in system_specs) — followed by the selected count and the selected labels; the sheet verification row lists the disabled-trigger case. The count and labels: the labels being the rendered tokens themselves referenced by id after a hidden count, so no label renders twice (AR6, with architecture:component-style-authoring INV7). The record names spec:AST-099 as the owner of the sheet's on-screen keyboard behavior in the ownership bullet and the Content boundary. family:input-fields FR7 is amended in this pull request (knowledge-contracts DEC-1: a conflict between current records is resolved at the canonical owner): busy semantics go on the combobox — or, in a presentation without a combobox, such as Tokenizer's sheet (DEC-3), on the result list the search updates; FR20 cites that clause. Also: nothing is focused before interaction, so focus is undisturbed (document.activeElement stays the body) — except with hasAutoFocus, whose server-rendered autofocus focuses the inline input on load: the record says the swap then moves that focus to the trigger, the same element and visit for the caller, so no onBlur/onFocus pair is reported, and the verification row lists the case (the SSR test covers it). The exact layout effect is stated: the trigger keeps the field's box — same width, top, label and size block size — except the inline input's own row: when the tokens leave the input no room on their row the inline field wraps the input onto a row of its own, and the trigger, which has no input, drops that row (one md row, 27px, measured on a 390 and a 414px phone with five tokens); with room for the input beside the tokens nothing shifts. The "adaptive resolution" state row says the same; the verification row lists the server-render-then-hydrate test (Tokenizer.ssr.test.tsx, in the implementation pull request and its verified_by) and the 390/414px box measurement. Evidence (Chromium 149, iPhone profile, same coarse CSS media in both runs, the resolver forced to popover for the hydrating render): 390px — hydrating field 358×59 (five tokens on two rows, the input on the second), trigger 358×32 (one row), same top and label; 414px — 382×59 → 382×32, same; activeElement body in both.

  • Batches reach assistive technology (FR21): the rows sit in a list whose listitem wrappers carry aria-setsize (the total) and aria-posinset — the checkbox role admits neither, and axe 4.12.1 reports a critical aria-allowed-attr when they sit on the checkbox — so a person moving through the rows can learn more exist before the list has scrolled; the record says that whether a screen reader speaks the position ("n of total") from the focused checkbox depends on the screen reader and names the accessibility tree as the proof of the list/listitem/aria-setsize/aria-posinset exposure (spec:AST-009 FR8, listed under system_specs); focus reaching the last rendered row renders the next batch (WCAG 2.2 SC 1.3.1 and 4.1.2 through spec:AST-020 FR1). Clear all (FR24): confirming moves focus to the search field, the destination this record names (spec:AST-085 FR4; SC 2.4.3): the clear is spent and the search field is where the next selection starts; a disabled Clear all stays focusable per AST-085 FR1, so the record no longer says otherwise; cancelling returns focus to Clear all. AR6: cites spec:AST-086/FR14 exactly as it stands on main; its description claim now says the selection is exposed as the trigger's description, that whether a screen reader reads it with the button depends on the screen reader, and that the DOM and accessibility tree prove the aria-describedby exposure (spec:AST-009 FR8). Closes (FR18): Done, swipe, scrim and Escape clear the query and report it through onChangeQuery; a close the field forces by becoming disabled keeps the query, since nothing the person did changed it (the "dismissed" state says so).

  • FR20 timing and focus: the bootstrap set is requested at once on open and on clearing the query, so an instant source shows no busy feedback and announces no busy start; an instant source with an empty bootstrap set and nothing selected shows only the search field and announces nothing (the search-only case above); only a typed query waits debounceMs; when a settled search removes the row that holds focus, focus moves to the search field (spec:AST-085 FR4), as FR16, FR18, FR22 and FR24 name their destinations; both are in the verification row.

  • Search (FR20), batches (FR21), create (FR22), Clear all (FR24): a query shorter than minQueryLength is not searched — it lists only the selected rows it matches and, with hasCreate, the create row, with no busy feedback, empty state or announcement, as the inline field shows nothing for such a query; a rejecting source settles the search to no results, as the inline field does; activating the create row commits, clears the query, and moves focus to the sheet's search field, since the row it was on is gone; from open and from each query change until that search settles the list is pending — named busy feedback above any rows still listed from the previous search, with the list element carrying the busy semantics (aria-busy) since the sheet has no combobox (family:input-fields FR7), never "No items" or "No results" during the debounce, as the lab prototype held its empty state back; then bootstrap on an empty query, minQueryLength / debounceMs / maxMenuItems / onChangeQuery / emptySearchText keep their meaning; 50-row batches advanced within 200px of the list end, reset on open and on query change; with hasCreate a Create "{text}" row with a trailing Add {text} action commits a create change and clears the query, absent at the limit; Clear all is disabled when empty — aria-disabled, still in the focus order, a tooltip saying nothing is selected, a click opens nothing (spec:AST-085 FR1) — otherwise confirms through an AlertDialog before committing []; the confirmed clear is announced from inside the sheet ("All items removed", AR6).

  • Inert props in the sheet presentation (Public concepts): renderItem, renderToken, endContent, hasClear, hasEntriesOnFocus, menuWidth, tokenOverflowBehavior, hasAutoFocus, and the input ref; handleRef.focus() focuses the trigger.

  • AR6: sheet semantics: the trigger is a button named by the field label with aria-haspopup="dialog" and aria-expanded, and its accessible description lists the selected token labels — exposed as the trigger's description; whether a screen reader reads it with the button depends on the screen reader, and the DOM and accessibility tree prove the aria-describedby exposure (spec:AST-009 FR8; WCAG 2.2 SC 1.3.1 and 4.1.2 through spec:AST-020 FR1); the sheet is a dialog named by the label with a Search {label} field whose clear is named; rows are checkbox controls toggled by tap or click and by Space and Enter (spec:AST-020 FR15; WCAG 2.2 SC 2.1.1 and 4.1.2); a disabled row and a disabled trigger use aria-disabled and keep their focus order; while the sheet is open, additions, removals, a confirmed Clear all ("All items removed", made once the confirmation has closed, since a message sent while the confirmation covers the sheet is not heard — spec:AST-086 FR14; FR24 states the same moment and the verification row lists it), the search's busy start, and how each search ended (the result count for a non-empty query, or the empty state's text when it shows — spec:AST-056 FR3) are announced once per occasion from inside the sheet through useAnnounce, which spec:AST-086 FR14 (current on main) routes into the persistent polite container of the sheet's own dialog while it is open (WCAG 2.2 SC 4.1.3); after the sheet closes focus returns to the trigger and the field's announcements follow the same FR14 route from wherever the field renders. The Sheet trigger and Sheet footer design rows name Token and Button plainly (they are not record ids). PR6: the sheet surface is opt-in cost — not loaded for the popover presentation, loaded on demand for the sheet presentation, nothing rendered on the server or in the hydrating render.

  • Supporting rows: Compatibility bullet, Ownership bullets (owns the touch surface; does not own the adaptive rule or BottomSheet's mechanics), AV7, six representative states, four Design-relationship anatomy rows, a verification-map row, the Compatibility bullet reads "the stable public API … unchanged"; system_specs gains spec:AST-017/FR21, spec:AST-020/FR1, spec:AST-056/FR3, spec:AST-058/FR9, spec:AST-085/FR1, spec:AST-085/FR4, and spec:AST-086/FR14 (current on main), and one open question (whether rows and trigger should honor renderItem / renderToken).

Every row main does not meet carries a present-tense "Implementation gap" note, as the BottomSheet promotion did (including the ref type statement, which main does not meet); the implementation pull request deletes those notes and adds its test file to verified_by.

Rejected alternatives

  • Switching surfaces automatically on pointer: coarse with no opt-in: changes stable behavior for every existing caller, which spec:AST-017 FR21 forbids outside an experimental boundary.
  • A top-level stable presentation prop, matching the selectors today: the surface has no consumer evidence yet, and a stable prop cannot be changed or removed in a patch.
  • Separate manage and add sheets (the prototype's first flow): two near-duplicate lists, and the person loses their place when switching.
  • Hosting the one sheet in a BottomSheetSwitcher: there is only one sheet, and the switcher is headed for deprecation; a standalone BottomSheet driven by isOpen is the same dialog with less machinery.
  • The date and time fields' PickerPresentation value set (adaptive-bottom-sheet): its native-picker axis has no counterpart in a list picker; the selection controls' AdaptivePresentation is the right family.

Each is one Rejected: line inside DEC-3.

Claim scope and non-goals

The record governs the experimental option, its resolution, and the observable behavior of the trigger and sheet listed above. It does not decide the stable default (whether adaptive ever becomes Tokenizer's default follows the selection controls' own lifecycle, outside this surface), the sheet's visual tuning, BottomSheet's dialog and gesture contract, the shared compact-touch query, or renderItem / renderToken support in the sheet (left open).

Evidence and implementation relationship

  • Touch-flow evidence: the Lab prototype's single-sheet flow, verified in real browsers at a 390×844 touch viewport — stable rows while open, selected-first regrouping on reopen, immediate toggles, create with a trailing Add action, 50-row batches, guarded Clear all, focusable disabled trigger and rows.
  • Shared policy evidence: the selection controls' adaptive tests stub the compact-touch query both ways; the implementation's presentation tests mirror them.
  • Implementation: the stacked feature pull request (feat/tokenizer-experimental-bottom-sheet) adds experimental.presentation, the sheet surface, i18n keys, docs, a story, tests, and the [experimental] Changeset, and deletes the gap notes; it adds no theming targets.

Writing rubric results

  • R1 narration: the summary table, Intent, Compatibility, Public concepts, the three scoping sentences, FR16–FR24, AV7, the representative states, PR6, AR6, the design rows, the verification row, and DEC-3 are written as present-tense statements about the system; the gap notes say what the shipped Tokenizer does today, with no fix tracking. "Lab touch-flow evidence" in the Basis column names the source of the facts, not a change. The reasoning behind the two owner decisions (value set, theming targets) lives in this description; the record keeps the conclusions as one sentence each.
  • R2 research in the record: none; the prototype history and the selectors' comparison stay in this description.
  • R3 evidence naming a change: none added; the new verification row names test layers and real-engine evidence. (The pre-existing PowerSearch pull-request link in Intent is untouched; it belongs to DEC-1's owner.)
  • R4 rejected alternatives over one line or outside a decision: none; DEC-3 carries five one-line Rejected: entries.
  • R5 pull request or issue references remaining: none added (searched # + digits, /pull/, /issues/). The summary table's former #6360 reference is gone with the rewritten Compatibility/readiness row; the pre-existing #5804 link in Intent and Family relationships is unchanged.
  • R6 ruled open questions still listing options: none; the new open question is unanswered and names the two options.

Scope

  • This PR contains specification records only.
  • It records observable behavior and preserves equivalent internal implementations (AV7).
  • Any named internal mechanism is intentionally public, names its dependent caller/system, and explains why an equivalent implementation would not satisfy the contract: the native modal dialog is named because component:BottomSheet and family:overlay-dismissal depend on it; the shared panel search field is named so the sheet reads as part of the panel, as the selection controls' panels do.
  • Verification states the evidence layer and failure signal without prescribing CI job/workflow topology.
  • It records one intentional durable decision, not a rescue for a separable tagalong.
  • The canonical knowledge owner, affected current claims, and overlapping open work are named; no ownership collision.
  • Adjacent API, visual, accessibility, composition, and implementation facts remain outside scope unless this exact decision depends on them.
  • Public text and artifacts contain no internal Meta context.

Validation

  • pnpm check:knowledge — "Knowledge templates and records are aligned."
  • Prettier — packages/core/src/Tokenizer/Tokenizer.spec.md formatted.
  • git diff --check — clean.

@vercel

vercel Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
astryx Ready Ready Preview Oct 26, 2026 7:31pm UTC

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 23, 2026
@astracat-bot

astracat-bot Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
❓ Needs maintainer input (for maintainers only) Oct 26, 2026, 7:57 PM UTC

@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

Previews ready: Storybook and Sandbox were built with this PR's docsite. CI evidence will be added when available.

View Storybook for this PR · View Sandbox for this PR

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This spec change is on hold for a maintainer decision. component:Tokenizer DEC-3 adds requirements the current record does not have (FR16–FR24, AR6, PR6), so an engineering owner needs to approve them at this head, including the choice of the selection controls' adaptive over the adaptive-bottom-sheet value the other input fields use. Whatever that decision, the record needs these fixes before it merges:

  • Scope FR1–FR15 and the existing design rows to the popover presentation: FR17 removes the input FR1 keeps mounted, and FR19 lists the committed items FR13 excludes.
  • In the sheet presentation, send ref and the other base props to the trigger (family:input-fields FR10), and make the suppression of the eight inert props visible rather than silent.
  • Give the sheet a pending-search state, so FR20 does not show "no results" before results arrive.
  • Say what happens when the field becomes disabled with the sheet open, and update approved_by/approved_at when DEC-3 is approved.

[Automated review]

| FR14 | With `hasEntriesOnFocus` enabled, committing a choice from the current settled empty-query bootstrap cohort MUST keep the input focused and the popup open when another eligible loaded choice remains. The active option advances to the next remaining choice in displayed order; when the committed choice was last, the nearest preceding choice becomes active. `aria-activedescendant` MUST reference that rendered option. | Approved rapid multi-select lifecycle | Current contract; implementation and verification pending |
| FR15 | The retained popup MUST close and clear its active descendant when no eligible loaded choice remains, `maxEntries` is reached, the control becomes disabled, focus leaves, or the person dismisses it. Disabled Tokenizer remains non-interactive. Pending or stale source work MUST NOT reopen a terminal popup or expose stale choices; loading has no selectable placeholder and its busy state clears when work is cancelled or settles. | Approved terminal-state safety boundary | Current contract; implementation and verification pending |
| FR16 | `experimental.presentation` selects the surface. `popover`, the value when `experimental` or the option is omitted, is the inline combobox with its anchored suggestion menu, unchanged. `bottom-sheet` is the trigger and modal sheet of FR17–FR24 on every device. `adaptive` resolves to `bottom-sheet` on compact coarse-pointer screens and to `popover` otherwise, through the same shared resolution the selection controls use (`component:Selector/FR4`). A resolution change while the sheet is open takes effect after it closes. | DEC-3; touch-flow evidence from the Lab prototype | Current contract. Implementation gap: the shipped Tokenizer has no `experimental` prop and renders the inline combobox on every device. |
| FR17 | In the sheet presentation the control surface is one button, named by the field label, with `aria-haspopup="dialog"` and `aria-expanded`. It shows the selected tokens without remove controls, or the placeholder when empty, wraps tokens onto further lines, and keeps a block size of at least 44px. Activating it opens one tall modal BottomSheet named by the field label; the suggestion popover, inline input, and end lane do not render. While disabled the trigger stays focusable with `aria-disabled="true"`, does not open, and shows `disabledMessage` as the inline field does (`spec:AST-085/FR1`). | DEC-3; Lab touch-flow evidence; `spec:AST-085` | Current contract. Implementation gap: no trigger exists. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This removes the inline input, but FR1 (line 161) still says Tokenizer MUST keep one native combobox input mounted, and FR13 (line 173) still excludes committed items that FR19 lists as checked rows. One sentence scoping FR1–FR15, AR1–AR5 and the existing design rows to the popover presentation would remove the contradiction.

surface to act on and are inert: `renderItem`, `renderToken`, `endContent`,
`hasClear` (the sheet's own Clear all replaces it), `hasEntriesOnFocus` (the
sheet always lists the bootstrap set), `menuWidth`, `tokenOverflowBehavior`,
`hasAutoFocus`, and the input `ref`. `handleRef.focus()` focuses the trigger.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

family:input-fields FR10 sends ref, id, data-* and aria-* to the "combobox/trigger element that carries the field's accessible name and value", and architecture:public-component-api INV8 connects ref to the element the contract promises. In the sheet presentation that element is the trigger, so an inert ref contradicts both. The eight dropped props are also suppressed silently (INV3).

| FR17 | In the sheet presentation the control surface is one button, named by the field label, with `aria-haspopup="dialog"` and `aria-expanded`. It shows the selected tokens without remove controls, or the placeholder when empty, wraps tokens onto further lines, and keeps a block size of at least 44px. Activating it opens one tall modal BottomSheet named by the field label; the suggestion popover, inline input, and end lane do not render. While disabled the trigger stays focusable with `aria-disabled="true"`, does not open, and shows `disabledMessage` as the inline field does (`spec:AST-085/FR1`). | DEC-3; Lab touch-flow evidence; `spec:AST-085` | Current contract. Implementation gap: no trigger exists. |
| FR18 | The sheet is a single surface: a search row at the top using the panel search field the selection controls share, a scrolling list of rows, and, while the query is empty, a footer with Clear all and Done. There is no second sheet. Done closes the sheet and clears the query; swipe, scrim, and Escape dismissal follow BottomSheet `purpose="info"`; focus returns to the trigger after the sheet closes. | DEC-3; Lab touch-flow evidence; `component:BottomSheet` | Current contract. Implementation gap: no sheet exists. |
| FR19 | Each row is a `checkbox` named by the item label whose `aria-checked` mirrors membership in `value`. Activating an unchecked row commits an `add` change; a checked row commits a `remove` change; both apply immediately and are announced as the inline field announces them. Row order is stable while the sheet is open: the items selected when it opened, in `value` order, then selected items absent from the results, then the results in source order; a toggle never moves an existing row. The next open takes a fresh snapshot. The query filters the selected rows by case-insensitive label substring. | DEC-3; Lab touch-flow evidence | Current contract. Implementation gap: no rows exist. |
| FR20 | An empty query lists the source's bootstrap set; a query shorter than `minQueryLength` lists no source results; otherwise the source is searched after `debounceMs` and the results are capped at `maxMenuItems`. `onChangeQuery` fires on every query change. With no rows and no create row, an empty query shows a "No items available" empty state and a non-empty query shows the `emptySearchText` empty state (its default message when omitted). | DEC-3; Lab touch-flow evidence | Current contract. Implementation gap: no sheet search exists. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As written, a pending search with no rows shows the emptySearchText empty state before results arrive. The Lab prototype holds the empty state back while searching, and family:input-fields FR7 requires named source-busy feedback, so this row needs a pending state.

| FR15 | The retained popup MUST close and clear its active descendant when no eligible loaded choice remains, `maxEntries` is reached, the control becomes disabled, focus leaves, or the person dismisses it. Disabled Tokenizer remains non-interactive. Pending or stale source work MUST NOT reopen a terminal popup or expose stale choices; loading has no selectable placeholder and its busy state clears when work is cancelled or settles. | Approved terminal-state safety boundary | Current contract; implementation and verification pending |
| FR16 | `experimental.presentation` selects the surface. `popover`, the value when `experimental` or the option is omitted, is the inline combobox with its anchored suggestion menu, unchanged. `bottom-sheet` is the trigger and modal sheet of FR17–FR24 on every device. `adaptive` resolves to `bottom-sheet` on compact coarse-pointer screens and to `popover` otherwise, through the same shared resolution the selection controls use (`component:Selector/FR4`). A resolution change while the sheet is open takes effect after it closes. | DEC-3; touch-flow evidence from the Lab prototype | Current contract. Implementation gap: the shipped Tokenizer has no `experimental` prop and renders the inline combobox on every device. |
| FR17 | In the sheet presentation the control surface is one button, named by the field label, with `aria-haspopup="dialog"` and `aria-expanded`. It shows the selected tokens without remove controls, or the placeholder when empty, wraps tokens onto further lines, and keeps a block size of at least 44px. Activating it opens one tall modal BottomSheet named by the field label; the suggestion popover, inline input, and end lane do not render. While disabled the trigger stays focusable with `aria-disabled="true"`, does not open, and shows `disabledMessage` as the inline field does (`spec:AST-085/FR1`). | DEC-3; Lab touch-flow evidence; `spec:AST-085` | Current contract. Implementation gap: no trigger exists. |
| FR18 | The sheet is a single surface: a search row at the top using the panel search field the selection controls share, a scrolling list of rows, and, while the query is empty, a footer with Clear all and Done. There is no second sheet. Done closes the sheet and clears the query; swipe, scrim, and Escape dismissal follow BottomSheet `purpose="info"`; focus returns to the trigger after the sheet closes. | DEC-3; Lab touch-flow evidence; `component:BottomSheet` | Current contract. Implementation gap: no sheet exists. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing says what happens if the field becomes disabled while the sheet is open. The rows (FR19), the create row (FR22) and Clear all (FR24) would still commit, which spec:AST-085 FR2 forbids. Saying the sheet closes and focus returns to the trigger, as FR15 does for the inline popup, would cover it.

@imdreamrunner

Copy link
Copy Markdown
Contributor Author

/approve-spec 1edddb0

@github-actions github-actions Bot added spec-auto-merge Auto-merge was enabled by the spec owner gate and removed needs:spec-owner-review Current knowledge records await owner approval labels Oct 23, 2026

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An engineering owner approved component:Tokenizer DEC-3 at this head, so the direction is settled. Four record fixes are still needed before it merges:

  • Say what element and type ref has in the sheet presentation. It is released as Ref<HTMLInputElement>, and the record keeps stable types unchanged, but it now goes to the trigger button.
  • Say what a sheet row does with an item's action, which spec:AST-058 FR9 expects Tokenizer to handle through its one panel.
  • Update the Compatibility bullet that still says the public API is unchanged.
  • In AR6, name the WCAG 2.2 success criterion and the row activation keys that spec:AST-020 FR1 and FR15 ask for.

[Automated review]

Comment on lines +142 to +145
In the sheet presentation the trigger is the control: `ref`, `id`, `data-*`,
`aria-*`, and the other base props reach it (`family:input-fields/FR10`,
`architecture:public-component-api/INV8`), `handleRef.focus()` and `blur()`
act on it, and `onFocus`, `onBlur`, and `onKeyDown` report its events. The

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ref now reaches the trigger button here, but Tokenizer.tsx still declares ref?: React.Ref<HTMLInputElement> ("Ref forwarded to the combobox input"), and the summary table says the stable types are unchanged. As written, a caller's input-typed ref receives an HTMLButtonElement on phones under adaptive. Could the record say which element and type ref has in the sheet presentation? One option is a props type that applies only when experimental is passed, so callers who omit it keep Ref<HTMLInputElement>.

| FR16 | `experimental.presentation` selects the surface. `popover`, the value when `experimental` or the option is omitted, is the inline combobox with its anchored suggestion menu, unchanged. `bottom-sheet` is the trigger and modal sheet of FR17–FR24 on every device. `adaptive` resolves to `bottom-sheet` on compact coarse-pointer screens and to `popover` otherwise, through the same shared resolution the selection controls use (`component:Selector/FR4`). A resolution change while the sheet is open takes effect after it closes. | DEC-3; touch-flow evidence from the Lab prototype | Current contract. Implementation gap: the shipped Tokenizer has no `experimental` prop and renders the inline combobox on every device. |
| FR17 | In the sheet presentation the control surface is one button, named by the field label, with `aria-haspopup="dialog"` and `aria-expanded`. It shows the selected tokens without remove controls, or the placeholder when empty, wraps tokens onto further lines, and keeps the inline field's block size for its `size` step. Activating it opens one tall modal BottomSheet named by the field label; the suggestion popover, inline input, and end lane do not render. While disabled the trigger stays focusable with `aria-disabled="true"`, does not open, and shows `disabledMessage` as the inline field does (`spec:AST-085/FR1`); a field that becomes disabled while its sheet is open closes the sheet and returns focus to the trigger (`spec:AST-085/FR2`). | DEC-3; Lab touch-flow evidence; `spec:AST-085` | Current contract. Implementation gap: no trigger exists. |
| FR18 | The sheet is a single surface: a search row at the top using the panel search field the selection controls share, a scrolling list of rows, and, while the query is empty, a footer with Clear all and Done. There is no second sheet. Done closes the sheet and clears the query; swipe, scrim, and Escape dismissal follow BottomSheet `purpose="info"`; focus returns to the trigger after the sheet closes. | DEC-3; Lab touch-flow evidence; `component:BottomSheet` | Current contract. Implementation gap: no sheet exists. |
| FR19 | Each row is a `checkbox` named by the item label whose `aria-checked` mirrors membership in `value`. Activating an unchecked row commits an `add` change; a checked row commits a `remove` change; both apply immediately and are announced as the inline field announces them. Row order is stable while the sheet is open: the items selected when it opened, in `value` order, then selected items absent from the results, then the results in source order; a toggle never moves an existing row. The next open takes a fresh snapshot. The query filters the selected rows by case-insensitive label substring. | DEC-3; Lab touch-flow evidence | Current contract. Implementation gap: no rows exist. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SearchableItem carries action?: ReactNode, and spec:AST-058 FR9 names Tokenizer as an adopter "through the one panel BaseTypeahead renders". The sheet is a second panel, and FR19 doesn't say what a row does with an item's action. One sentence would close the gap: render the item without it and warn in development, as BaseTypeahead does today, until adoption covers the sheet rows.


## Compatibility and migration

- The public API and the default `hasEntriesOnFocus={false}` path are unchanged.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This bullet still says "The public API … unchanged", while Public concepts now introduces the experimental experimental prop and the bullet at lines 75-80 describes it. Maybe "The stable public API …"?

Comment on lines +306 to +309
- **AR6 — Sheet semantics.** In the sheet presentation the trigger is a button
named by the field label with `aria-haspopup="dialog"` and `aria-expanded`,
and its accessible description lists the selected token labels, so assistive
technology hears the selection the sighted person sees (`spec:AST-020/FR1`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spec:AST-020 FR1 asks each WCAG expectation to cite a success criterion. Other records write "WCAG 2.2 SC 1.3.1 and 4.1.2 through spec:AST-020/FR1". FR15 also has the record name the activation keys a role adopts. Could AR6 name the criterion for the token description and the keys that toggle a row?

@imdreamrunner
imdreamrunner force-pushed the spec/tokenizer-experimental-presentation branch from 1edddb0 to d19e19f Compare October 23, 2026 21:25
@github-actions github-actions Bot added needs:spec-owner-review Current knowledge records await owner approval and removed spec-auto-merge Auto-merge was enabled by the spec owner gate labels Oct 23, 2026

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This spec change is on hold for an engineering owner's decision. The only /approve-spec names the previous head, and this head changes the normative text of component:Tokenizer DEC-3 (FR16, FR19, FR20, AR6 and the ref rule), so an engineering owner needs to record approval by commenting /approve-spec with the full head SHA, which the author may do as an engineering owner. Two record fixes are needed either way:

  • AR6's in-dialog live region should also announce how a search ended (the result count or the empty-state message), because the page-level announcement channel is not exposed inside the native modal sheet.
  • The Sheet trigger and Sheet footer design rows cite component:Token and component:Button, which are not records.

[Automated review]

Comment on lines +332 to +336
open, additions, removals, and the search's busy start are announced through
a persistent polite live region inside the sheet's dialog, because the shared
page-body channel is not exposed while a native modal is open
(`spec:AST-086/FR10`, Platform support), and that region follows FR3, FR5,
and FR6 of that record; after the sheet closes focus returns to the trigger

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This region carries toggles and the busy start, but not how a search ended. The inline field announces the result count and the empty-search message today (BaseTypeahead.tsx:736-742), and the page-level channel isn't exposed inside the native modal, so someone typing in the sheet's search never hears the outcome (WCAG 2.2 SC 4.1.3). Could the region also announce the settled outcome once per search, with those announcements added to the sheet verification row? spec:AST-086 FR10 covers owners rehosted above a modal, so Platform support is the closer citation.

| Suggestion menu | Retains current outer-field anchoring and exact preferred `menuWidth`; focus bootstrap keeps only eligible choices and a valid active option. | Current BaseTypeahead source | Prominent | FR2, FR13–FR15, AR5 |
| Overflow placeholder/layer | Preserves a usable outer-field fallback while live content may reparent or collapse. | Current Tokenizer/Layer composition | Supporting | FR3, FR7–FR8 |
| Dependent narrower surface | Uses the descriptor only after its owning component resolves a width narrower than the field. | Consuming component plus Layer | Prominent | FR9–FR12 |
| Sheet trigger | The field's control surface in the sheet presentation: tokens or placeholder, the inline field's size step, opens the sheet. | Tokenizer source; `component:Token` | Prominent | FR17, AR6 |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

component:Token here and component:Button in the Sheet footer row (line 356) aren't record ids on main or at this head, so a reader following them finds nothing. Naming the components without the record-id form, as the other rows do, would fix it.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This spec change needs an engineering owner's decision and one record fix before it can be approved. An engineering owner has to approve component:Tokenizer DEC-3 at the current head, because it adds the experimental presentation option and the sheet requirements FR16–FR24, AR6, PR6 and AV7 to a record whose main version has none of them, and the only /approve-spec so far names an earlier commit; any engineering owner, including the author, records that decision by commenting /approve-spec followed by the full head SHA. Before that, FR16's new hydration sentence says nothing is focused before an adaptive field swaps to its trigger, but a field with hasAutoFocus server-renders its input with autofocus, which takes focus on load, so FR16 should say whether adaptive defers hasAutoFocus or moves focus to the trigger, and the sheet verification row should list that case.

[Automated review]

| FR13 | Tokenizer MUST exclude every choice whose `SearchableItem.id` is already present in its committed `value` from displayed bootstrap and search cohorts, including results that settle after the controlled value changes. A caller-rejected proposed value remains eligible because it was not committed. Distinct IDs remain eligible even when their labels match. | Approved Tokenizer multi-selection contract | Current contract; implementation and verification pending |
| FR14 | With `hasEntriesOnFocus` enabled, committing a choice from the current settled empty-query bootstrap cohort MUST keep the input focused and the popup open when another eligible loaded choice remains. The active option advances to the next remaining choice in displayed order; when the committed choice was last, the nearest preceding choice becomes active. `aria-activedescendant` MUST reference that rendered option. | Approved rapid multi-select lifecycle | Current contract; implementation and verification pending |
| FR15 | The retained popup MUST close and clear its active descendant when no eligible loaded choice remains, `maxEntries` is reached, the control becomes disabled, focus leaves, or the person dismisses it. Disabled Tokenizer remains non-interactive. Pending or stale source work MUST NOT reopen a terminal popup or expose stale choices; loading has no selectable placeholder and its busy state clears when work is cancelled or settles. | Approved terminal-state safety boundary | Current contract; implementation and verification pending |
| FR16 | `experimental.presentation` selects the surface. `popover`, the value when `experimental` or the option is omitted, is the inline combobox with its anchored suggestion menu, unchanged. `bottom-sheet` is the trigger and modal sheet of FR17–FR24 on every device. `adaptive` resolves to `bottom-sheet` on compact coarse-pointer screens and to `popover` otherwise, through the same shared resolution the selection controls use (`component:Selector/FR4`). `adaptive` renders on the server, and hydrates, as the inline field, because the resolver answers `popover` where there is no `matchMedia` and in the hydrating render; on a compact coarse-pointer client the field swaps to the trigger in the first render after hydration, with nothing focused before interaction, so focus is undisturbed (`document.activeElement` stays the body). The trigger keeps the field's box — the same width, top, label, and `size` block size — except the inline input's own row: when the tokens leave the input no room on their row, the inline field wraps the input onto a row of its own, and the trigger, which has no input, drops that row (one `md` row, 27px, on a 390 and a 414px phone with five tokens); with room for the input beside the tokens, nothing shifts. A resolution change while the sheet is open takes effect after it closes and focus has returned to the trigger. Whenever the presentation changes while its control holds focus — then, or while the sheet is closed — focus moves to the new presentation's control, the combobox input or the trigger, so it never lands on the page body (`spec:AST-085/FR4`). | DEC-3; touch-flow evidence from the Lab prototype | Current contract. Implementation gap: the shipped Tokenizer has no `experimental` prop and renders the inline combobox on every device. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This says the swap happens "with nothing focused before interaction", and the sheet verification row expects the body still active, but a field with hasAutoFocus server-renders its input with autofocus (BaseTypeahead renders autoFocus={hasAutoFocus}, and React 19's server renderer writes <input autofocus="">). On a phone that input holds focus before any interaction, so this row's later rule then moves focus to the trigger. Could FR16 name the case, for example "unless hasAutoFocus focused the input, in which case focus moves to the trigger by the rule below", or say that adaptive applies hasAutoFocus only once the presentation resolves, and could the sheet verification row list it?

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This spec change needs one decision before it can be approved: an engineering owner has to approve component:Tokenizer DEC-3 at the current head, because it adds the experimental presentation option and the sheet requirements FR16–FR24, AR6, PR6 and AV7 to a record whose main version has none of them, and the only /approve-spec so far names an earlier commit. The new FR16 clause for hasAutoFocus across hydration settles the last record fix, so nothing else needs to change first. Any engineering owner, including the author, records the decision by commenting /approve-spec followed by the full head SHA.

[Automated review]

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This spec change needs one decision before it can be approved: an engineering owner has to approve component:Tokenizer DEC-3 at the current head, because it adds the experimental presentation option and the sheet requirements FR16–FR24, AR6, PR6 and AV7 to a record whose main version has none of them, and the only /approve-spec so far names an earlier commit. The new FR24 sentence for an empty-selection Clear all (focusable, aria-disabled, a tooltip, no action) matches spec:AST-085, so no record fix is needed first; with Button's default tooltip, a tap on a phone does not show that tooltip, which the owner may accept or refine. Any engineering owner, including the author, records the decision by commenting /approve-spec followed by the full head SHA.

[Automated review]

| FR21 | Long lists render progressively: 50 rows at a time, the next batch when the list scrolls within 200px of its end, with the window reset to the first batch on open and on each query change. The rendered and total counts are exposed as `data-rendered-count` and `data-total-count` on the list, and the rows sit in a `list` whose `listitem` wrappers carry `aria-setsize` with the total and `aria-posinset` with the row's position — the `checkbox` role itself admits neither — so a person moving through the rows can learn that more exist before the list has scrolled; whether a screen reader speaks the position ("n of total") from the focused checkbox depends on the screen reader (`spec:AST-009/FR8`), and the accessibility tree proves the `list`, `listitem`, `aria-setsize`, and `aria-posinset` exposure; focus reaching the last rendered row renders the next batch (WCAG 2.2 SC 1.3.1 Info and Relationships and 4.1.2 Name, Role, Value through `spec:AST-020/FR1`). | DEC-3; Lab touch-flow evidence | Current contract. Implementation gap: no list exists. |
| FR22 | With `hasCreate`, a non-empty trimmed query that matches no selected id, no selected label, and no result label (case-insensitive) adds a create row above the list showing `Create "{text}"` with a trailing Add action named `Add {text}`. Activating it commits a `create` change whose item has the text as both id and label, clears the query, and moves focus to the sheet's search field, since the row it was on is gone. The create row is absent at `maxEntries`. | DEC-3; Lab touch-flow evidence; `hasCreate` contract of the inline field | Current contract. Implementation gap: no create row exists. |
| FR23 | At `maxEntries`, unchecked rows carry `aria-disabled="true"`, stay focusable, and ignore activation; checked rows remain toggleable so the person can make room (`spec:AST-085/FR1`). | DEC-3; Lab touch-flow evidence; `spec:AST-085` | Current contract. Implementation gap: no rows exist. |
| FR24 | Clear all is disabled while `value` is empty: `aria-disabled`, still in the focus order, with a tooltip saying nothing is selected, and a click opens nothing (`spec:AST-085/FR1`). Otherwise it opens an AlertDialog confirmation naming the field; confirming commits `[]` with a `remove` change naming the last selected item, closes the confirmation, and — once the confirmation has closed — moves focus to the sheet's search field, the destination this record names (`spec:AST-085/FR4`): the clear is spent, and the search field is where the next selection starts (WCAG 2.2 SC 2.4.3 through `spec:AST-020/FR1`), and announces "All items removed" inside the sheet (AR6); cancelling changes nothing and focus returns to Clear all. | DEC-3; Lab touch-flow evidence | Current contract. Implementation gap: no footer exists. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional, not blocking: if this footer's Clear all uses Button's tooltip, the tooltip runs with touchTrigger auto, which keeps it shut when a button is tapped and ignores focus that comes from a touch. On a phone the "nothing is selected" reason would then show only to keyboard and mouse users (and to screen readers as the button's description). If touch users should see why Clear all is unavailable, this row could say a tap shows the tooltip.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This spec change needs one decision before it can be approved: an engineering owner has to approve component:Tokenizer DEC-3 at the current head, because it adds the experimental presentation option and the sheet requirements FR16–FR24, AR6, PR6 and AV7 to a record whose main version has none of them, and the only /approve-spec so far names an earlier commit. The record text is unchanged from that commit and no record fix is needed first. Any engineering owner, including the author, records the decision by commenting /approve-spec followed by the full head SHA.

[Automated review]

interface and keeps `ref?: Ref<HTMLInputElement>` as released, so `interface
X extends TokenizerProps<T>` and `Omit<TokenizerProps<T>, …>` wrappers keep
compiling; the component's own parameter type is where the widening lives:
with `experimental` passed, `ref` accepts `Ref<HTMLInputElement |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional, not blocking: with the repository's TypeScript 6.0.3 and @types/react 19.2.17, an input-only RefObject<HTMLInputElement | null> or callback ref still compiles where Ref<HTMLInputElement | HTMLButtonElement> is expected, so a caller who adds experimental={{presentation: 'adaptive'}} to a field holding useRef<HTMLInputElement>(null) gets an HTMLButtonElement on a phone without a type error. If that is the intent, a clause saying the wider type documents the trigger case rather than rejecting input-only refs would set callers' expectations.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This spec change needs two record fixes and an engineering owner's decision before it can be approved. FR20 puts the sheet's busy semantics on the list, but family:input-fields FR7 on main still requires Tokenizer's source-busy feedback to expose them on the combobox, so FR7 needs a matching amendment in this pull request; and the ownership bullet should name spec:AST-099, which main now makes the owner of BottomSheet's on-screen keyboard behavior. Once those land, any engineering owner, including the author, records the decision on component:Tokenizer DEC-3 by commenting /approve-spec followed by the full head SHA.

[Automated review]

| FR17 | In the sheet presentation the control surface is one button, named by the field label, with `aria-haspopup="dialog"` and `aria-expanded`. It shows the selected tokens without remove controls, or the placeholder when empty, wraps tokens onto further lines, and keeps the inline field's block size for its `size` step. Activating it opens one tall modal BottomSheet named by the field label; the suggestion popover, inline input, and end lane do not render. While disabled the trigger stays focusable with `aria-disabled="true"`, does not open, and shows `disabledMessage` as the inline field does (`spec:AST-085/FR1`); a field that becomes disabled while its sheet is open closes the sheet and returns focus to the trigger (`spec:AST-085/FR2`). | DEC-3; Lab touch-flow evidence; `spec:AST-085` | Current contract. Implementation gap: no trigger exists. |
| FR18 | The sheet is a single surface: a search row at the top using the panel search field the selection controls share, a scrolling list of rows, and, while the query is empty, a footer with Clear all and Done. There is no second sheet. On open, focus moves to the sheet's panel, not the search field, so the reading order starts at the top of the sheet and the on-screen keyboard rises only when the person reaches for the search (WCAG 2.2 SC 2.4.3 Focus Order and 3.2.1 On Focus through `spec:AST-020/FR1`). Done closes the sheet; swipe, scrim, and Escape dismissal follow BottomSheet `purpose="info"`; each of those closes clears the query and reports it through `onChangeQuery`, so the next open lists the bootstrap set, while a close the field forces (becoming disabled, FR17) keeps the query, since nothing the person did changed it; focus returns to the trigger after the sheet closes. | DEC-3; Lab touch-flow evidence; `component:BottomSheet` | Current contract. Implementation gap: no sheet exists. |
| FR19 | Each row is a `checkbox` named by the item label whose `aria-checked` mirrors membership in `value`. Activating an unchecked row commits an `add` change; a checked row commits a `remove` change; both apply immediately and are announced with the inline field's words through the sheet's own live region (AR6). Row order is stable while the sheet is open: the items selected when it opened, in `value` order, then selected items absent from the results, then the results in source order; a toggle never moves an existing row. The next open takes a fresh snapshot. The query filters the selected rows by case-insensitive label substring. A row renders an item without its `action` and warns once in development that the sheet does not render it, as the inline panel does (`spec:AST-058/FR9`). | DEC-3; Lab touch-flow evidence | Current contract. Implementation gap: no rows exist. |
| FR20 | An empty query lists the source's bootstrap set, requested at once on open and on clearing the query, so an instant source shows no busy feedback and announces no busy start — the outcome below still applies, so an instant source with no bootstrap items shows and announces the "No items available" empty state; a query shorter than `minQueryLength` is not searched: it lists only the selected rows it matches and, with `hasCreate`, the create row, with no busy feedback, no empty state, and no outcome announcement, as the inline field shows nothing for such a query; otherwise the source is searched `debounceMs` after the query change and the results are capped at `maxMenuItems`. `onChangeQuery` fires on every query change. From open, and from each query change, until that search settles, the list is pending: it shows named busy feedback above any rows still listed from the previous search, the list element carries the busy semantics (`aria-busy`) since the sheet has no combobox (`family:input-fields/FR7`), and no empty state shows; when the settled search removes the row that holds focus, focus moves to the search field (`spec:AST-085/FR4`). Once settled, the outcome is announced through the sheet's live region (AR6): the result count for a searched query, or the rendered text of the empty state when it shows (`spec:AST-056/FR3`). A source that rejects settles the search to no results, as the inline field does. With no rows and no create row, an empty query shows a "No items available" empty state and a searched query shows the `emptySearchText` empty state (its default message when omitted). | DEC-3; Lab touch-flow evidence; `family:input-fields` | Current contract. Implementation gap: no sheet search exists. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This puts the sheet's busy semantics on the list "since the sheet has no combobox", but family:input-fields FR7 on main still says Tokenizer's visible source-busy feedback must "expose busy semantics on the combobox", and architecture:knowledge-contracts DEC-1 resolves a conflict between current records at the canonical owner rather than in the narrower record. Could this pull request also amend FR7 (or the family's Adoption and exceptions table) so a presentation without a combobox exposes busy semantics on the list its search updates?

Comment on lines +138 to +140
- The sheet's dialog, scrim, gestures, snap behavior, keyboard accommodation,
and dismissal paths — owned by `component:BottomSheet` and
`family:overlay-dismissal`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On main, component:BottomSheet now says "BottomSheet's on-screen keyboard behavior is owned by spec:AST-099". Could this bullet, and the Content boundary at the end of the record, name spec:AST-099 for the sheet's on-screen keyboard so a reader lands on its owner?

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This spec change needs one record fix and an engineering owner's decision before it can be approved. FR20 shows and announces a fixed "No items available" whenever nothing is selected and the source's bootstrap set is empty, so a search-only source such as {search, bootstrap: () => []} tells people on every open that nothing is available, while the inline field shows nothing in that state; please settle what the sheet shows there. The decision covers component:Tokenizer DEC-3 and the new family:input-fields FR7 clause, which this head adds without an exact-head approval: any engineering owner, including the author, records it by commenting /approve-spec followed by the full head SHA.

[Automated review]

| FR17 | In the sheet presentation the control surface is one button, named by the field label, with `aria-haspopup="dialog"` and `aria-expanded`. It shows the selected tokens without remove controls, or the placeholder when empty, wraps tokens onto further lines, and keeps the inline field's block size for its `size` step. Activating it opens one tall modal BottomSheet named by the field label; the suggestion popover, inline input, and end lane do not render. While disabled the trigger stays focusable with `aria-disabled="true"`, does not open, and shows `disabledMessage` as the inline field does (`spec:AST-085/FR1`); a field that becomes disabled while its sheet is open closes the sheet and returns focus to the trigger (`spec:AST-085/FR2`). | DEC-3; Lab touch-flow evidence; `spec:AST-085` | Current contract. Implementation gap: no trigger exists. |
| FR18 | The sheet is a single surface: a search row at the top using the panel search field the selection controls share, a scrolling list of rows, and, while the query is empty, a footer with Clear all and Done. There is no second sheet. On open, focus moves to the sheet's panel, not the search field, so the reading order starts at the top of the sheet and the on-screen keyboard rises only when the person reaches for the search (WCAG 2.2 SC 2.4.3 Focus Order and 3.2.1 On Focus through `spec:AST-020/FR1`). Done closes the sheet; swipe, scrim, and Escape dismissal follow BottomSheet `purpose="info"`; each of those closes clears the query and reports it through `onChangeQuery`, so the next open lists the bootstrap set, while a close the field forces (becoming disabled, FR17) keeps the query, since nothing the person did changed it; focus returns to the trigger after the sheet closes. | DEC-3; Lab touch-flow evidence; `component:BottomSheet` | Current contract. Implementation gap: no sheet exists. |
| FR19 | Each row is a `checkbox` named by the item label whose `aria-checked` mirrors membership in `value`. Activating an unchecked row commits an `add` change; a checked row commits a `remove` change; both apply immediately and are announced with the inline field's words through the sheet's own live region (AR6). Row order is stable while the sheet is open: the items selected when it opened, in `value` order, then selected items absent from the results, then the results in source order; a toggle never moves an existing row. The next open takes a fresh snapshot. The query filters the selected rows by case-insensitive label substring. A row renders an item without its `action` and warns once in development that the sheet does not render it, as the inline panel does (`spec:AST-058/FR9`). | DEC-3; Lab touch-flow evidence | Current contract. Implementation gap: no rows exist. |
| FR20 | An empty query lists the source's bootstrap set, requested at once on open and on clearing the query, so an instant source shows no busy feedback and announces no busy start — the outcome below still applies, so an instant source with no bootstrap items shows and announces the "No items available" empty state; a query shorter than `minQueryLength` is not searched: it lists only the selected rows it matches and, with `hasCreate`, the create row, with no busy feedback, no empty state, and no outcome announcement, as the inline field shows nothing for such a query; otherwise the source is searched `debounceMs` after the query change and the results are capped at `maxMenuItems`. `onChangeQuery` fires on every query change. From open, and from each query change, until that search settles, the list is pending: it shows named busy feedback above any rows still listed from the previous search, the list element carries the busy semantics (`aria-busy`), since the sheet has no combobox (`family:input-fields/FR7`, which places busy semantics on the list a presentation without a combobox updates), and no empty state shows; when the settled search removes the row that holds focus, focus moves to the search field (`spec:AST-085/FR4`). Once settled, the outcome is announced through the sheet's live region (AR6): the result count for a searched query, or the rendered text of the empty state when it shows (`spec:AST-056/FR3`). A source that rejects settles the search to no results, as the inline field does. With no rows and no create row, an empty query shows a "No items available" empty state and a searched query shows the `emptySearchText` empty state (its default message when omitted). | DEC-3; Lab touch-flow evidence; `family:input-fields` | Current contract. Implementation gap: no sheet search exists. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With nothing selected and a source whose bootstrap() returns [] (a supported shape: the minQueryLength={3} "People" field in Tokenizer.test.tsx uses {search, bootstrap: () => []}), this row shows and announces "No items available" on every open, although typing finds items. The inline field shows and reports nothing in that state (BaseTypeahead.tsx treats a bootstrap that returns nothing as "not a search that failed"), and Tokenizer has no emptyText to change the words. Could an empty query with an empty bootstrap set show no empty state and announce nothing, as the inline field does, with the verification row's "no bootstrap items" case updated to match?

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This spec change is ready for an engineering owner's decision: component:Tokenizer DEC-3 adds the experimental presentation option and the sheet requirements FR16–FR24, AR6, PR6 and AV7, and this pull request adds a clause to family:input-fields FR7, none of which main states yet, while the only /approve-spec so far names an earlier commit. The FR20 fix is in: with nothing selected and an empty bootstrap set, the sheet now shows only the search field and announces nothing, as the inline field does, so no record fix is needed first. Any engineering owner, including the author, records the decision by commenting /approve-spec followed by the full head SHA. Optionally, update the description's "FR20 timing and focus" bullet, which still says an instant source with no bootstrap items shows and announces "No items available".

[Automated review]

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This spec change needs one record fix and an engineering owner's decision before it can be approved. Under adaptive, the sheet that replaces the popover on a phone always renders more elements than the popover, and architecture:component-style-authoring INV7 (with spec:AST-017 FR60) allows that only through an owner exception recorded with element counts, while AV7 allows the sheet "any internal structure" and DEC-3 records no exception: please record the exception in DEC-3, add INV7 to AV7's proviso, and list element counts at rest and open under a fine and a coarse pointer in the sheet verification row. The decision then covers component:Tokenizer DEC-3 and the family:input-fields FR7 clause, which no /approve-spec names at this head; any engineering owner, including the author, records it by commenting /approve-spec followed by the full head SHA.

[Automated review]

Comment on lines +235 to +238
- **AV7 — Sheet surface composition.** The sheet presentation may compose
BottomSheet, the shared panel search field, EmptyState, AlertDialog, and the
checkbox indicator in any internal structure, and may load that surface on
demand, provided FR16–FR24, AR6, and PR6 hold.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AV7 lets the sheet compose its parts "in any internal structure", but architecture:component-style-authoring INV7 (current on main) also binds that structure: an open layer may have no more elements under a coarse pointer than under a fine one, and spec:AST-017 FR60 allows more only "through an owner exception recorded with its element counts". Under adaptive this sheet always outnumbers the popover it replaces (the panel search field, every selected row, a list item and checkbox indicator per row, the footer, the sheet's panel and handle). Could AV7's proviso name INV7, DEC-3 record the exception with its reason, and the sheet verification row list element counts at rest and open under a fine and a coarse pointer?

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This spec change is ready for an engineering owner's decision: component:Tokenizer DEC-3 adds the experimental presentation option and the sheet requirements FR16–FR24, AR6, PR6 and AV7, and the pull request amends family:input-fields FR7, none of which main states yet. The architecture:component-style-authoring INV7 exception for the open sheet is now recorded in DEC-3 with its element counts, so no record fix is needed first; the decision should also settle whether that exception, which covers the open sheet only, needs to name the closed adaptive trigger that replaces the inline input under a coarse pointer. Any engineering owner, including the author, records the decision by commenting /approve-spec followed by the full head SHA.

[Automated review]

forced `bottom-sheet` presentation renders the same elements under either
pointer (114 at rest, 165 open). At rest the coarse
pointer never has more elements than the fine one: nothing of the sheet is in
the DOM until a press. The exception covers the open sheet only; its counts

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This scopes the exception to the open sheet, but under adaptive the closed field is also a coarse-pointer presentation built from different elements (FR16–FR17: a trigger button in place of the inline input and its token remove actions), and architecture:component-style-authoring INV7's at-rest budget for "text and other inputs" says "A pointer-specific presentation is a change of attributes and styles on the same elements". Worth settling in the decision whether the exception should also name the closed trigger (its counts, 154 fine / 120 coarse, are already here) or whether the swap meets the at-rest budget as it stands.

@astracat-bot astracat-bot Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This spec change needs one record fix and an engineering owner's decision before it can be approved. DEC-3's INV7 exception covers the closed adaptive trigger as well as the open sheet, but AV7 still ends "except as DEC-3 records for the open sheet" and paraphrases INV7 without its at-rest rule that a pointer-specific presentation changes only attributes and styles on the same elements, so please extend AV7 to the closed trigger and cite that rule directly. After that, an engineering owner needs to approve component:Tokenizer DEC-3 (the experimental presentation option, FR16–FR24, AR6, PR6, AV7 and the element-budget exception) together with this pull request's amendment of family:input-fields FR7, none of which main states yet; the only /approve-spec so far names an earlier commit. Any engineering owner, including the author, records that decision by commenting /approve-spec followed by the full head SHA.

[Automated review]

demand, provided FR16–FR24, AR6, and PR6 hold and the structure stays within
`architecture:component-style-authoring` INV7 — minimal DOM, content
rendered once, at rest no more elements under a coarse pointer than under a
fine one — except as DEC-3 records for the open sheet.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DEC-3 now ends "The exception covers the closed adaptive trigger and the open sheet" (line 545), but this clause still limits the exception to the open sheet, so a reviewer applying AV7 to the trigger finds no exception for it. Could it read "except as DEC-3 records for the closed adaptive trigger and the open sheet"?

@astracat-bot astracat-bot Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This spec change needs two small record fixes and an engineering owner's decision before it can be approved. DEC-3's element breakdown counts a footer that FR18 does not render at the measured query "e" and does not add up to the open total it reports, and AV7 still paraphrases architecture:component-style-authoring INV7's at-rest rule as a count instead of citing INV7's own words. Once both are fixed, an engineering owner needs to approve component:Tokenizer DEC-3 (the experimental presentation option, FR16–FR24, AR6, PR6, AV7 and the element-budget exception) together with this pull request's amendment of family:input-fields FR7, since the only /approve-spec so far names an earlier commit. Any engineering owner, including the author, records that decision by commenting /approve-spec followed by the full head SHA.

[Automated review]

Comment on lines +239 to +241
`architecture:component-style-authoring` INV7 — minimal DOM, content
rendered once, at rest no more elements under a coarse pointer than under a
fine one — except as DEC-3 records for the closed `adaptive` trigger and

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gloss states INV7's at-rest rule as a count, but INV7 on main ("A pointer-specific presentation is a change of attributes and styles on the same elements") and DEC-3 below state it as the same elements; the coarse-not-more-than-fine count is INV7's open-layer budget. Read alone, AV7 would need no exception for the closed adaptive trigger it excepts (120 against 154). Could AV7 cite INV7 without the gloss, or use INV7's words: at rest, the same elements under any pointer; open, no more elements under a coarse pointer than under a fine one?

@astracat-bot astracat-bot Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approval of this spec change waits on an engineering owner's decision on component:Tokenizer DEC-3 and the family:input-fields FR7 clause it relies on, and three record fixes are needed first:

  • DEC-3's open-sheet breakdown, measured at query "e", counts a footer that FR18 and the searching state do not show and a header the record never names, and its totals do not reconcile (120 + 47 is 167, not the 165 reported open; 114 + 47 is 161, not 165).
  • Public concepts says every prop outside the inert list keeps its meaning, but FR20 lists the whole bootstrap set in the sheet while maxMenuItems caps it in the inline menu, so that sentence should name maxMenuItems (or FR20 should cap the bootstrap list too).
  • AV7 restates architecture:component-style-authoring INV7's open-layer budget without the spec:AST-017 FR60 / DEC-29 allowance INV7 cites; citing INV7 without the paraphrase keeps the two in step.

Once those are fixed, any engineering owner, the author included, records the decision by commenting /approve-spec followed by the full head SHA of that head.

[Automated review]

Comment on lines +539 to +544
`pointer: coarse`, open = query "e": at rest 154 fine / 120 coarse (the
trigger with its tokens is smaller than the inline field with its token
remove actions and input), open 157 fine (inline menu) / 165 coarse (sheet);
the open sheet's dialog is 47 elements: search row 17, list 15 (two rows of 7
and the list), the sheet's live region 1, and the panel, handle, header and
footer 14; a pending Clear all confirmation adds 20 while it is open. The

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At query "e" FR18 and the searching state render no footer, and no other part of the record names a header, yet the 47-element dialog counts "the panel, handle, header and footer 14". The totals also do not reconcile: 120 at rest plus 47 is 167, not the 165 reported open. Please re-measure at a state the record describes and name each part the sheet adds beyond BottomSheet's frame, so the parts add up to the open total.

Comment on lines +242 to +244
trigger departs from as DEC-3 records; open, the layer under a coarse
pointer has at most as many elements as under a fine one, which the open
sheet departs from as DEC-3 records.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

INV7's open-layer budget on main continues "(beyond that, only by the open-layer allowance spec:AST-017 FR60 and DEC-29 set)"; this clause drops it, so the record states the rule more strictly than its owner does. Citing INV7 without the paraphrase, or carrying the allowance, keeps the two in step.

@astracat-bot astracat-bot Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This spec change waits on an engineering owner's decision on component:Tokenizer DEC-3 and the family:input-fields FR7 amendment it relies on, and three small record fixes belong on the head that decision approves:

  • AV7 (L244–L245) and the sheet verification row (L446) call DEC-3's exception "that allowance", but spec:AST-017 FR60 allows an open layer only 5 extra elements and nothing for a closed trigger, so please word both as the owner exception DEC-3 records.
  • Public concepts (L171–L172) says every prop outside the inert list keeps its meaning, but FR20 lists the whole empty-query bootstrap set while the inline field caps it at maxMenuItems, so please say that there or cap the bootstrap list in FR20.
  • FR16 and the ownership bullet (L135–L137, L204) cite component:Selector/FR4 for the rule spec:AST-094 FR13 owns, and DEC-3 (L546–L547) cites component:Dialog for the toast outlet spec:AST-003 FR18 states, so please cite those owners.

Once those are in, any engineering owner, the author included, records the decision by commenting /approve-spec followed by the full head SHA.

[Automated review]

lists the bootstrap set), `menuWidth`, `tokenOverflowBehavior`, and
`hasAutoFocus`. Passing one of them with the sheet presentation emits one
development-mode warning naming the prop, so it is not silently dropped
(`architecture:public-component-api/INV3`); every other prop keeps its

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence covers maxMenuItems, but FR20 (L208) lists the whole bootstrap set on an empty query and caps only searched results. The inline field caps the bootstrap list too (BaseTypeahead.tsx L779 slices it to maxMenuItems, default 10). Could this name maxMenuItems as capping only searched results in the sheet, or could FR20 cap the bootstrap list as well?

attributes and styles on the same elements; open, in INV7's words, "the
open layer under a coarse pointer has at most as many elements as under a
fine pointer (beyond that, only by the open-layer allowance `spec:AST-017`
FR60 and DEC-29 set)". DEC-3 records that allowance for the closed

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spec:AST-017 FR60 and DEC-29 set an allowance of up to 5 more elements for an open layer, and none for a closed trigger. "Anything beyond this needs an owner exception with before and after counts." The open sheet's 165 elements against 157, and the trigger that replaces the input, are that owner exception, which is what DEC-3 (L520) calls them. Could this read "DEC-3 records the owner exception FR60 requires beyond that allowance, for the closed adaptive trigger and the open sheet", with the verification row (L446) saying "two-part exception"?

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This spec change waits on an engineering owner's decision on component:Tokenizer DEC-3 and the family:input-fields FR7 amendment it relies on, and one record fix belongs on the head that decision approves; the three fixes asked of the previous head are in. AR6 (L343–L347) defines the sheet trigger's accessible description as the selected count and token labels only, so please say that the field's helper text, status message and disabled reason, which the inline input carries in aria-describedby, stay in that description and in what order, since spec:AST-085 FR5 requires a disabled control to expose its description and disabled reason on itself, and list that case in the sheet verification row. Once that is in, any engineering owner, the author included, records the decision by commenting /approve-spec followed by the full head SHA.

[Automated review]

Comment on lines +343 to +347
and its accessible description is the selected count followed by the
selected token labels, so the selection the sighted person sees is exposed
to assistive technology as the trigger's description; the labels are the
rendered tokens themselves, each referenced by id from `aria-describedby`
after a hidden count, so no label renders twice

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes the trigger's description the count and token labels only. The inline input's aria-describedby also carries the caller's ids, the field description, the status message and the disabledMessage reason (Tokenizer.tsx L916–L923 and L1046–L1047 on main), Public concepts says description and status keep their meaning, and spec:AST-085 FR5 requires a disabled control to expose "any description or disabled reason" on itself. Could AR6 say those stay in the trigger's description, and in what order relative to the count and labels, with the sheet verification row listing a trigger that has a description and an error status?

…on (DEC-3)

component:Tokenizer gains DEC-3: the touch surface is the presentation option
of the optional experimental prop object (popover | bottom-sheet | adaptive),
resolved through the shared compact-touch policy, with the single searchable
sheet's behavior recorded as FR16-FR24, AR6 and PR6. Omitting experimental
keeps the inline combobox. Rows main does not meet yet carry implementation
gap notes.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This spec change is ready for an engineering owner's decision: the AR6 trigger-description fix asked of the previous head is in, and no record fix remains. Approval of component:Tokenizer DEC-3 and the family:input-fields FR7 amendment waits on an engineering owner, the author included, commenting /approve-spec followed by the full head SHA, because the earlier approval named an earlier commit. Approving as written also accepts that hasAutoFocus, listed as inert in the sheet presentation, still moves focus onto the trigger under adaptive but not under a forced bottom-sheet.

[Automated review]

@imdreamrunner

Copy link
Copy Markdown
Contributor Author

/approve-spec 538789a

This branch was successfully deployed

1 active deployment
Preview — 538789ac Deployed Oct 26, 2026 by vercel[bot]
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. spec-auto-merge Auto-merge was enabled by the spec owner gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant