Skip to content

docs(spec): promote component:BottomSheet and component:BottomSheetSwitcher to current - #7822

Merged
imdreamrunner merged 1 commit into
mainfrom
spec/promote-bottom-sheet-records
Oct 22, 2026
Merged

imdreamrunner merged 1 commit into
mainfrom
spec/promote-bottom-sheet-records

Conversation

@imdreamrunner

@imdreamrunner imdreamrunner commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Exact decision needed

Promote component:BottomSheet and component:BottomSheetSwitcher from draft to current, so the per-step dialog contract the owner approved in #7744 (BottomSheetSwitcher DEC-1 and DEC-2, FR2, FR5, FR8, FR9, PR1, the representative states; BottomSheet's ownership and anatomy notes) governs implementation and review of #7720 and #7757.

Existing authority searched

Decision

Both records: authority: current, approved_by: imdreamrunner, approved_at: 2026-10-21. owners is unchanged. Following the shape of other current component records, each behavioral table drops its draft-only "Draft review state" column and is headed "Invariant". No requirement text changes. Every other non-front-matter edit:

component:BottomSheetSwitcher

  • Intent is rewritten in the present tense: the switcher coordinates a sequence of children each presented in its own native dialog (DEC-1); rows the shipped switcher does not yet meet carry a gap note; OQ1–OQ3 stay open. The "This draft records…" and "Owner decision" wording goes.
  • Ownership → Owns, first bullet, implementation gap: the switcher renders one dialog of its own and hosts every child as a panel inside it; no child owns a dialog yet.
  • Public concepts → Dialog surface: "(owner decision, DEC-2)" becomes "(DEC-2)"; implementation gap: every dialog prop, ref, id, and data-testid reaches the switcher's one shared dialog.
  • The two sentences above the behavioral table ("Draft requirements identify their basis… This draft cannot clear the gaps it records.") are removed.
  • FR2 implementation gap: the switcher presents one shared dialog once per flow and hosts every child as a panel inside it; a handoff swaps panels in that dialog, and no dialog is re-presented.
  • FR5: "(owner decision, DEC-1)" → "(DEC-1)" twice, "(verified current behavior)" removed; implementation gap: a script focus trap runs on top of the modal dialog's native containment; a non-modal flow is one shared dialog carrying the page-level value itself, with no flow wrapper. (The scroll-lock clause holds on main: useScrollLock(isModal) once per flow.)
  • FR8 implementation gap: the one backdrop is the shared dialog's, so no takeover between dialogs happens.
  • FR9 implementation gap: every forwarded input, ref, id, and data-testid reaches the one shared dialog, which never changes on a handoff.
  • Representative states lead-in: where a state names a sheet's own dialog, the retained dialog, or the flow wrapper, the shipped switcher meets it with its one shared dialog instead; FR2 and FR5 record that gap. Modal state gains the focus-trap gap note.
  • PR1 implementation gap: one shared dialog is presented throughout, so a handoff has one dialog open, not two.
  • Design relationships: "Shared dialog" requirement reads "one per child sheet (DEC-1)" with the gap note "one such host serves the whole flow"; "Scrim" reads "one sheet dialog paints it at a time (DEC-1)"; "Sheet panels" authority drops the word "draft".
  • Family and system relationships: "this draft does not treat it as an exception" → "not an exception".
  • Verification map, FR5–FR7/AR1–AR3 row: BottomSheetNativeModal.a11y.browser.spec.ts carries "(implementation gap: not yet in the tree)".
  • FR1, FR3, FR4, FR6, FR7, AV1–AV2, ORD1–ORD2, PR2, AR1–AR3, the theming anatomy, DEC-1, DEC-2, and OQ1–OQ3 are unchanged.

component:BottomSheet

  • Intent is rewritten in the present tense and names the native dialog every sheet renders; the "This draft records…" wording goes.
  • Ownership → Owns, last bullet: "(…DEC-1's decided structure)" → "(…DEC-1)"; implementation gap: a switcher-managed sheet renders a panel inside the switcher's one shared dialog and owns no dialog of its own.
  • Ownership → Does not own, switcher bullet: "(…DEC-1's decided structure)" → "(…DEC-1)".
  • Representative states, switcher-managed bullet: "(…DEC-1's decided structure, not shipped anatomy)" → "(…DEC-1)"; implementation gap: the panel renders inside the switcher's one shared dialog.
  • FR3 basis drops "issue Bottom Sheet: keyboard users cannot scroll the sheet body (axe serious) #5207" (R5: no issue references in a record); the requirement, spec:AST-025, and the tests stay.
  • Compatibility and migration is rewritten for the shipped state, in the shape other current component records use when no transition is in flight (as component:Dialog): "Released default preserved: yes; with no padding prop and no theme padding, the Content area insets by --spacing-4 on every logical edge, and padding={0} keeps an unpadded Content area"; "Compatibility class: records the shipped anatomy, padded Content area, and native dialog; no public prop, default, or export changes"; controlled/uncontrolled unchanged; "Migration decision: none; the precedence rule under Transformation and precedence order is the whole contract". The precedence rule itself (prop, then theme padding on bottom-sheet, then --spacing-4) is unchanged.
  • DEP-0009 and CLN-0018 are removed. The transition they described is complete (see Evidence), and a record describes the present; a current record cannot carry DEP-0009's "open decision: This record is draft and needs approval" row.
  • FR1, FR2, FR4, AR1–AR2, the theming anatomy, DEC-1, and OQ1 are unchanged.

Rejected alternatives

None on authority: row-level authority is not a convention for these records; authority is a record-level field. Promoting only the switcher record was considered and rejected: the owner asked for both, and the BottomSheet record's ownership and anatomy notes are the half of DEC-1 that the switcher record's rows refer to. Keeping DEP-0009/CLN-0018 with a completed state was considered and rejected: spec:AST-017 FR26's lifecycle vocabulary has no state for a finished cleanup, the ids are optional cross-references (FR7, DEC-18), and the record would then carry history.

Claim scope and non-goals

Promotes the records' existing claims only, and retires the completed padded-default transition's lifecycle entries. It adds no requirement and fixes no implementation; every per-step gap is recorded, not fixed here. It does not choose between DEC-1 and #7648's deprecation of the switcher. It does not clear the spec:AST-027 non-modal gap.

Evidence and implementation relationship

Each existing claim, checked against source on main (BottomSheetSwitcher.tsx, BottomSheet.tsx, their tests):

  • Switcher FR1 (one interactive child; others inert and aria-hidden), FR3 (entering above retained; alignment; fade after transform), FR4 (retained through exit; focus to the captured opener), FR6 (purpose gates Escape, scrim click, swipe; alertdialog), FR7 (one useLayerDismissal registration; LayerDepthProvider around step content), ORD1, ORD2 (consumer aria-label, then aria-labelledby, then the active child's label), PR2, AR1–AR3: hold on main.
  • Switcher FR5, scroll-lock clause: holds (useScrollLock(isModal) once per flow; isModal stays true through a handoff and the exit). Modal showModal() and non-modal show(): hold. Native-containment and flow-wrapper clauses: gaps (the JS focus trap; the shared dialog carries zIndex: 1000 itself).
  • Switcher FR8: "a stale gesture from an outgoing sheet cannot change the flow's scrim" and "unmounting a retained sheet cannot keep … the flow's modal layer held" hold (tests on main); the takeover between dialogs is a gap.
  • Switcher FR2, FR9 (per-dialog forwarding, host-only id/data-testid, moving ref), Dialog-surface concept, Owns bullet, PR1, the First open/Handoff states, the "Shared dialog" design row: gaps — one shared dialog.
  • BottomSheet FR1 (panel, Content area, Handle; Scrim only when scrim-backed), FR2 (bottom-sheet target only), FR3 (useScrollableArea, keyboardAccess.owner="contentOrViewport"), FR4 (Content area inset --spacing-4 by default, published as a container publisher), AR1–AR2: hold on main. The switcher-managed dialog in the Owns bullet and the state row: gap.
  • BottomSheet Compatibility: the padded default has shipped. The 0.7.0 changelog entry "BottomSheet pads its content by default, like Dialog. With no padding prop and no theme padding, the content box now pads by --spacing-4…" (feat(core)!: BottomSheet pads its content by default, like Dialog #6884) names the padding={0} migration and the preserve-bottom-sheet-content-padding codemod astryx upgrade runs; BottomSheetPanel.tsx resolves the prop, then the theme's bottom-sheet padding, then --spacing-4. DEP-0009 (proposed) and CLN-0018 (pending) described that transition before it landed.

What #7720 deletes when it lands (it merges after this PR, stacked on it): the switcher record's Owns-bullet gap, Dialog-surface gap, FR2, FR5, FR8, FR9 and PR1 gap sentences, the Modal-state gap, the representative-states lead-in, the "Shared dialog" row's gap (together with the anatomy rename it already carries), and the BottomSheet record's Owns-bullet and state-row gaps — exactly those sentences, and nothing else in either record. #7757 deletes the verification-map "(implementation gap: not yet in the tree)" note when it adds the engine spec. The spec:AST-027 non-modal gap remains.

Writing rubric results

  • R1 narration: both Intents rewritten in the present tense ("This draft records…" gone). BottomSheet's Compatibility section states the shipped default without a release number or the former default; DEP-0009/CLN-0018, which narrated the transition, are removed. All fourteen implementation-gap notes (switcher: Owns, Dialog surface, FR2, FR5, FR8, FR9, states lead-in, Modal state, PR1, Shared dialog row, verification map; BottomSheet: Owns, state row) are present tense and carry no fix tracking; tracking lives in this description. "Owner decision", "verified current behavior", "DEC-1's decided structure", "this draft does not treat it as an exception", and "draft as observational context" are gone from the switcher record; "DEC-1's decided structure, not shipped anatomy" is gone from the BottomSheet record.
  • R2 research in the records: none.
  • R3 evidence naming a change: none; verified_by and the verification maps name test layers.
  • R4 rejected alternatives over one line or outside a decision: none new; DEC-1's three and DEC-2's one one-line rejections are unchanged.
  • R5 pull request or issue references remaining: none in the switcher record; the BottomSheet record's issue #5207 (FR3 basis) is removed (searched # + digits, /pull/, /issues/).
  • R6 ruled open questions still listing options: none; switcher OQ1–OQ3 and BottomSheet OQ1 are unruled and stay.

Scope

  • This PR contains specification records only.
  • It records observable behavior and preserves equivalent internal implementations.
  • Any named internal mechanism is intentionally public, names its dependent caller/system, and explains why an equivalent implementation would not satisfy the contract: only the native modal-dialog state (showModal()) is named, because the family and layer contracts depend on it.
  • 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: aligned
  • Prettier on both records: clean
  • git diff --check: clean
  • Table shape: every row in both records matches its header's cell count (script-checked).

@vercel

vercel Bot commented Oct 21, 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 22, 2026 5:46am UTC

Request Review

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

astracat-bot Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
🔄 In progress (for maintainers only) Oct 22, 2026, 5:41 AM UTC

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

Current run: The current change has no visual or preview-producing scope. Visual checks are not applicable; preview links are not shown.

The exact-head preview is not available yet.


Generated by PR Enrichment workflow | View current CI run

@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 waits on an engineering owner approving component:BottomSheet and component:BottomSheetSwitcher as current at this exact head: spec-owner-approval is still pending, and the approval on #7744 covered the switcher's decisions as draft content on another head. Before that, please drop packages/core/src/BottomSheet/__tests__/BottomSheetNativeModal.a11y.browser.spec.ts from the verified_by list of both records (BottomSheet.spec.md line 18, BottomSheetSwitcher.spec.md line 16), because the file is not in the tree yet and only the switcher's verification map says so. A new commit resets exact-head approval, so making that edit first saves a second approval. The rewritten Compatibility section, the DEP-0009/CLN-0018 removal and the implementation-gap notes match the shipped code.

[Automated review]

kind: component
id: component:BottomSheet
authority: draft
authority: current

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Promoting this record keeps packages/core/src/BottomSheet/__tests__/BottomSheetNativeModal.a11y.browser.spec.ts in verified_by (line 18), but that file is not in the tree yet and nothing in this record says so. Please drop it from the list until the browser spec lands.

| Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
| FR1–FR4, FR8 | `BottomSheetSwitcher.test.tsx` | closed, first open, handoffs in either completion order, rapid replacement, unmount, close | A child becomes interactive at the wrong time, a retained child disappears early, a second backdrop paints, or a sheet dialog's lifecycle breaks. | `audit:BottomSheetSwitcher/behavior` |
| FR5–FR7, AR1–AR3 | `BottomSheetSwitcher.test.tsx`; `BottomSheetNativeModal.a11y.browser.spec.ts` (implementation gap: not yet in the tree) | modal/non-modal, info/form/required, nested layer, IME, focus return, scroll lock in engines, non-modal local ordering | Modality, dismissal order, naming, inertness, scroll lock, or focus behavior diverges, or a sheet dialog takes a page-level z-index. | `audit:BottomSheetSwitcher/accessibility` |

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 row now marks the browser spec as not yet in the tree, yet verified_by (line 16) still lists it. Dropping it there too keeps the front matter consistent until the spec is added.

…itcher to current

Both records become current, approved by the owner on the per-step dialog
decision (BottomSheetSwitcher DEC-1 and DEC-2). Every row is checked against
the shipped source: where the switcher's one shared dialog does not yet meet
a per-step requirement, the row carries a present-tense implementation gap
note. The draft-only constructs go: the candidate-invariant and review-state
columns, the owner-decision markers, the draft intent wording, and the
completed padded-default transition (its deprecation and cleanup entries),
whose Compatibility section now states the shipped default. No requirement
text changes.
@imdreamrunner
imdreamrunner force-pushed the spec/promote-bottom-sheet-records branch from a6280af to 4152c07 Compare October 22, 2026 05:41
@imdreamrunner

Copy link
Copy Markdown
Contributor Author

/approve-spec 4152c07

@github-actions github-actions Bot removed the needs:spec-owner-review Current knowledge records await owner approval label Oct 22, 2026
@imdreamrunner
imdreamrunner merged commit 3133fb0 into main Oct 22, 2026
41 of 42 checks passed
@github-actions
github-actions Bot deleted the spec/promote-bottom-sheet-records branch October 22, 2026 06:22

This branch was successfully deployed

1 active deployment
Preview — 4152c075 Deployed Oct 22, 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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant