Skip to content

feat(core): modals drop the backdrop blur - #8120

Draft
vjeux wants to merge 6 commits into
meta:mainfrom
vjeux:uikit/c16-dims
Draft

vjeux wants to merge 6 commits into
meta:mainfrom
vjeux:uikit/c16-dims

Conversation

@vjeux

@vjeux vjeux commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

User impact

Dialog (and AlertDialog), Drawer and MobileNav no longer blur the whole page behind the modal, under every pointer. Repainting a full-viewport 2 px backdrop-filter is costly: on the Android emulator (software rendering), main's blurred dim draws at about 20 fps while it opens, this branch's at 60. The dim keeps the theme's --color-overlay; nothing else changes.

Visual authority

None of the template's routes applies: no current record sets the modal backdrop's blur. Owner decisions on this pull request (2026-10-25): the overlay stays theme-controlled, with no new token and no colour change; a forced-colors-visible edge keeps the modal apart from the page; and the blur is dropped everywhere, desktop included: the page behind a modal is dimmed only, on every pointer. The release note says every pointer.

Before and after

Before (top) and after (bottom), iOS 26.2 Safari, base tokens, the page behind an opening Dialog: the same dim colour; the text behind is no longer blurred.

iOS 26.2: the same dim, without the blur

With a mouse, Chromium desktop, light and dark: before, then after.

Chromium desktop, a mouse: the same dim, without the blur

After, Android 15, Chrome 124 (emulator), light and dark: 16.7 ms frames while the Dialog opens.

Android, Chrome 124: the overlay without the blur

State matrix

  • A finger or a mouse, light and dark, base tokens or a bundled theme: the theme's overlay, unblurred.
  • Forced colors paint the backdrop as Canvas, so the blur was the only cue that a page sits behind the modal. Dialog draws a 1 px CanvasText border there (its outline stays the focus ring's); Drawer and MobileNav keep their anchored-side border, which forced colors already paint as one 1 px edge.
  • BottomSheet (never blurred), Lightbox and Overlay's media scrim: unchanged here.
  • A theme keeps the blur with backdropFilter: 'blur(2px)' on the modal backdrop: components.dialog.base['::backdrop'], components['mobile-nav'].base['::backdrop'], and components.drawer.base[':modal::backdrop'] (a non-modal drawer keeps none). Theme rules sit in a layer above the components' own.

Scope

  • Appearance is the primary intent.
  • Behavior, API, interaction model, and unrelated layout changes were removed or split.
  • The evidence shows the changed pixels actively running, not only source or computed styles.
  • Public text and artifacts contain no internal Meta context.

Testing

  • pnpm exec vitest run packages/core/src/__tests__/modalBackdrop.test.tsx packages/core/src/{Dialog,Drawer,MobileNav,BottomSheet,AlertDialog,CommandPalette}: 36 files, 751 tests pass. modalBackdrop.test.tsx reads the rules StyleX injects (jsdom drops backdrop-filter): every modal's backdrop keeps --color-overlay; no modal sets a backdrop-filter under any pointer; Dialog's surface draws a 1px solid CanvasText border in forced colors and declares no outline under any condition, and Drawer and MobileNav keep their solid anchored-side border with no outline; BottomSheet has no filter. With main's three components, 4 of its 11 cases fail.
  • pnpm exec playwright test --config playwright.config.ts ModalBackdrop against a fresh pnpm storybook:build: 26 pass and 1 evidence-only skip in Chromium; 20 pass and 7 skips in WebKit (forced colors is Chromium-only; official Playwright image). Under a finger (light, dark, the neutral theme) and with a mouse (each mouse case first asserts a fine pointer) every modal computes the theme's overlay with backdrop-filter: none; in forced colors, under either pointer, Dialog and AlertDialog draw a 1 px border on every side with the outline left at the focus ring's (none until keyboard focus), and Drawer and MobileNav one 1 px border on the side facing the page, no outline. CI's pr-a11y lane discovers it (no workflow step); its routing line runs it when a pull request changes it, Dialog, AlertDialog, Drawer, MobileNav or BottomSheet, or their stories.
  • Chromium desktop (800 × 600, a mouse): before (this pull request's previous head) rgba(1, 18, 40, 0.4) / rgba(17, 17, 18, 0.6) dark with blur(2px); after the same colours with no filter (the still above).
  • Theme override, Chromium under either pointer: a rule in the theme layer setting backdrop-filter: blur(2px) on .astryx-dialog::backdrop, .astryx-drawer:modal::backdrop and .astryx-mobile-nav::backdrop computes blur(2px) for each; the non-modal Drawer (hasScrim={false}, a popover) keeps none.
  • iOS 26.2 simulator (iPhone 17 Pro, Safari) and Android 15 emulator (Chrome 124, recording lease): the recordings above. Read page-side on Android: this branch rgba(1, 18, 40, 0.4) / rgba(17, 17, 18, 0.6) dark with no filter; main the same colours with blur(2px). Main's blurred open steps at about 50 ms per frame there, this branch's at 16.7 ms.
  • Package build (pnpm -F @astryxdesign/core build): astryx.css carries Dialog's border-width: 1px, border-style: solid and border-color: CanvasText under @media (forced-colors: active), no outline rule for the edge, and no backdrop-filter for Dialog, Drawer or MobileNav (its one blur(2px) is Lightbox's, unchanged here).
  • pnpm exec vitest run --project node packages/native/src/theme/swiftIOSTheme.test.ts packages/cli/test: 322 pass. pnpm a11y:audit -- --components Dialog,Drawer,MobileNav,AlertDialog,CommandPalette: no new violations. check:changesets, core typecheck, eslint, prettier and the pre-commit checks pass.

@vercel

vercel Bot commented Oct 24, 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 25, 2026 9:21pm UTC

Request Review

@github-actions github-actions Bot added the needs:design-review Affects visuals — Design should review label Oct 24, 2026
@astracat-bot

astracat-bot Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Oct 25, 2026, 9:49 PM UTC

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 24, 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.

The new --color-background-backdrop token, its flat black 20 % / 48 % default and the removal of the modal blur set a design direction that no current spec record covers, so approval waits on an owner providing or identifying the governing spec for it. An engineering owner can record that decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner; the decision settles the token and its name, the default, the flat dim in every theme, and Lightbox keeping --color-overlay with its blur. Two fixes are needed whatever the decision:

  • Themes that colour the dim through --color-overlay or defineTheme({color}) now fall to the new default, so derive the token in expandColorScale and correct the changeset's line that themed apps keep their colour.
  • In forced colors the blur was the only cue that the page sits behind an open modal, so keep a forced-colors-only blur or border, or have the owner accept that state explicitly.

[Automated review]

Comment thread .changeset/modal-backdrop-token.md Outdated
'@astryxdesign/theme-y2k': patch
---

[feat] New `--color-background-backdrop` token: the dim behind a modal. Dialog (and so AlertDialog), Drawer, MobileNav and BottomSheet paint their `::backdrop` with it, and the dim no longer blurs the page behind it. With the base tokens it is the iOS dimming colour: black at 20 % in light mode and 48 % in dark mode, in place of a blue-grey at 40 % (light) or 60 % (dark) with a 2 px blur. Each bundled theme sets the token to the overlay colour it already dimmed with, so a themed app's dim keeps its colour and only loses the blur. `--color-overlay` stays the scrim that keeps text and icons legible over media (Overlay, Thumbnail). `astryx docs tokens/tokens-color`, the Tailwind bridge (`bg-backdrop`) and the theme template list the new token.

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 holds for the seven bundled themes only. A custom theme that tinted its dim through --color-overlay, or one built with defineTheme({color}) (whose expandColorScale derives --color-overlay but not the new token), now gets black 20 % / 48 %. A warm-neutral color theme resolves --color-overlay to #1A1B2466 but the modal dim to rgba(0, 0, 0, 0.2). Consider deriving --color-background-backdrop in expandColorScale from the same neutral ramp, and tell custom-theme authors to set the new token to keep their dim.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Covered by the later wording in 8b3cba5 (custom themes on iOS get the black default; how to keep the old look). The token is iOS-only now, so deriving it in expandColorScale would only matter on iOS; the changeset tells custom-theme authors to set it.

Comment thread packages/core/src/Dialog/Dialog.tsx Outdated
'::backdrop': {
backgroundColor: colorVars['--color-overlay'],
backdropFilter: 'blur(2px)',
backgroundColor: colorVars['--color-background-backdrop'],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

In forced colors this paints Canvas at the token's alpha, so the dim can only fade the page; the removed blur was the cue that the page sits behind the modal. In Chromium's forced-colors emulation with a light palette, the page behind an open Dialog now stays sharp (rgba(255, 255, 255, 0.2), no filter, against 0.4 with blur(2px) before), and Dialog draws no edge there. A forced-colors-only backdrop-filter or a CanvasText border under @media (forced-colors: active) would keep the cue.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Since 5e3c43c the flat dim applies only under the iOS gate (nested (pointer: coarse) + (-webkit-touch-callout: none)), and iOS Safari has no forced-colors mode. Desktop and Android in forced colors keep --color-overlay with its 2px blur, which the spec's forced-colors case asserts.

@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

📚 Storybook Preview

View Storybook for this PR

🧪 Sandbox Preview

View Sandbox for this PR

Component Size

Component Import before Import after Delta CSS before CSS after Delta
core/AlertDialog 51.2KB 51.2KB +20B 12.2KB 12.2KB +47B
core/AppShell 52.3KB 52.3KB -13B 11.4KB 11.3KB -23B
core/CommandPalette 48.8KB 48.8KB +24B 11.6KB 11.7KB +43B
core/Dialog 13.5KB 13.5KB +21B 6.9KB 7.0KB +50B
core/DialogHeader 45.6KB 45.6KB 0B 9.5KB 9.5KB 0B
core/Drawer 22.6KB 22.6KB -11B 7.9KB 7.9KB -23B
core/DrawerHeader 45.5KB 45.5KB 0B 10.9KB 10.9KB 0B
core/MobileNav 45.5KB 45.5KB -13B 9.8KB 9.8KB -23B
core/MobileNavToggle 43.0KB 43.0KB 0B 9.2KB 9.2KB 0B
core/SideNav 52.6KB 52.6KB -12B 10.3KB 10.3KB -22B
core/TopNav 46.4KB 46.4KB -12B 10.1KB 10.1KB -24B

Sizes are gzip. Import counts the JavaScript the import loads eagerly; CSS counts the package stylesheet rules referenced by the modules it can load from its own package, lazily loaded ones included.

Bundle Size Summary

Gzip figures measure each package's entry file, not the code an import pulls in.

Package Entry (ESM) Entry gzip before Entry gzip after Delta
@astryxdesign/core 4.9KB 1.2KB 1.2KB 0B
Package stylesheet Gzip before Gzip after Delta
@astryxdesign/core 33.7KB 33.7KB 0B

Accessibility Audit

Status: No accessibility violations detected.

Visual Regression

48 of 502 shot(s) changed.

A change here is a question, not a failure: check whether the after is the
picture you intended. Record that review on the PR. Baseline maintenance is an
explicit dispatch of CI; this report never rewrites the baseline or adds a release gate.

component story theme mode pixels
Selector Indicator Space Bottom Sheet Narrow End RTL neutral light 753,933
Selector Indicator Space Bottom Sheet Wide Start neutral light 740,072
RichTextEditor Markdown parity: side by side probe dark 556,900
RichTextEditor Markdown parity: side by side probe light 538,816
Chat Full AI Chat probe light 230,823
Chat Full AI Chat probe dark 229,805
AppShell With Mobile Nav probe light 217,010
AppShell With Mobile Nav probe dark 216,960
Collapsible Dividers probe dark 66,445
Collapsible Dividers probe light 66,445
AppShell Playground neutral dark 65,198
AppShell Playground neutral light 63,788
Chat Default neutral dark 36,304
Chat Default neutral light 35,767
Chat Empty State probe light 23,531
Chat Empty State probe dark 22,622
ChatAutoScroll Scroll Behavior Comparison neutral dark 15,378
ChatAutoScroll Scroll Behavior Comparison neutral light 14,107
Chat With Attachments probe light 13,406
Chat With Attachments probe dark 11,232

and 28 more.


Generated by PR Enrichment workflow | Storybook | Sandbox | View full report

@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.

The iOS modal dim adds direction no current spec sets (the new --color-background-backdrop token, its black 20 % / 48 % default, the dropped blur under the iOS gate, and the INV10 backdrop and glass surface roles), so approval waits on an owner providing or identifying the governing spec. An engineering owner can record that decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner. The decision covers the token and its iOS-only scope, the platform gate, the default and blur, and the edges: custom themes on iOS fall to the default, and Lightbox keeps the blurred overlay. Whatever the decision, these need fixing:

  • Rebase onto main and run pnpm -F @astryxdesign/native generate:swift-ios: the committed Swift theme files lack the new token, so the native drift test fails.
  • Correct the changeset: themes that tint the dim through --color-overlay or defineTheme({color}) get the black default on iOS, and it should say how to keep the old look.
  • Add iOS Safari captures of Drawer, MobileNav and BottomSheet, since the recordings show Dialog only.

[Automated review]

Comment thread .changeset/modal-backdrop-token.md Outdated
'@astryxdesign/theme-y2k': patch
---

[feat] New `--color-background-backdrop` token: the dim behind a modal on iOS. Under a finger on WebKit for iOS, Dialog (and so AlertDialog), Drawer, MobileNav and BottomSheet paint their `::backdrop` with it, and the dim no longer blurs the page behind it. With the base tokens it is the iOS dimming colour: black at 20 % in light mode and 48 % in dark mode, in place of a blue-grey at 40 % (light) or 60 % (dark) with a 2 px blur. Each bundled theme sets the token to the overlay colour it already dimmed with, so a themed app's dim keeps its colour on iOS and only loses the blur. Everywhere else (a mouse, Android) the modals keep `--color-overlay` and its blur, unchanged. `--color-overlay` also stays the scrim that keeps text and icons legible over media (Overlay, Thumbnail). `astryx docs tokens/tokens-color`, the Tailwind bridge (`bg-backdrop`) and the theme template list the new token.

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 holds for the seven bundled themes only. On iOS, a theme that tints its dim through --color-overlay, or one built with defineTheme({color}) (expandColorScale derives --color-overlay but not the new token), gets black 20 % / 48 %: a warm-neutral color theme resolves --color-overlay to #1A1B2466 but the iOS dim to rgba(0, 0, 0, 0.2). Please say so, and add how to keep the old look (set --color-background-backdrop, plus a ::backdrop backdropFilter override for the blur), which visual-direction changesets name.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 8b3cba5: the changeset now says a custom theme that tints its dim through --color-overlay or defineTheme({color}) gets the black default on iOS, and how to keep the old look (set --color-background-backdrop, plus a ::backdrop backdropFilter override).

Comment thread .github/workflows/ci.yml Outdated
- name: Run modal close visibility guard
run: node .github/scripts/modal-close-visibility.js --storybook-dir apps/storybook/dist

# Every modal surface dims the page with one theme colour,

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 describes the earlier design. At this head only the iOS gate paints --color-background-backdrop without blur; elsewhere the modals keep --color-overlay with blur(2px), which this spec's Android and mouse cases assert, and Lightbox keeps the overlay everywhere.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 8b3cba5: the step comment now describes the iOS gate (iOS: the token, unblurred; a mouse or an Android finger: --color-overlay with its 2px blur; Lightbox: the overlay everywhere).

@cixzhang

Copy link
Copy Markdown
Contributor

Posted by Cindy's agent on her behalf.

Parts of this PR re-implement behavior that already exists, or is being specified, as a shared part of core. Converging keeps one implementation per behavior:

  • Touch gating. core/src/Dialog/Dialog.tsx:216 writes its own (pointer: coarse) check. This conflicts with AST-012 FR2 (touch is resolved once, through the closed adaptation vocabulary) and public-component-api INV16. Resolve through the shared touch resolver key so behavior matches the spec. If this behavior genuinely can't be expressed through the resolver, please raise it here as a spec question instead of shipping a separate check.

This is about where the behavior lives, not about the PR's intent.

@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.

The iOS modal dim adds direction no current spec sets (the new --color-background-backdrop token, its black 20 % / 48 % default, the dropped blur under the iOS gate, and the INV10 backdrop and glass surface roles), so approval waits on an owner providing or identifying the governing spec for it. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner; the decision covers the token and its iOS-only scope, the platform gate, the default and the blur, and the edges: custom themes on iOS get the black default unless they set the token, and Lightbox keeps the blurred overlay. The earlier fixes are all in (regenerated Swift theme files, the changeset's custom-theme and keep-the-old-look wording, iOS recordings of Drawer, MobileNav and BottomSheet, the CI comment), so nothing else in this review stands in the way once the direction is decided.

[Automated review]

@vjeux

vjeux commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

Agreed: one implementation.

  • Touch gating: the modal dim stops writing its own (pointer: coarse) check and keys on the resolver's touch presentation, the same on every touch device (Android included); a mouse keeps today's dim. This lands in the next push of this PR.

@vjeux vjeux changed the title feat(core): under a finger, modals dim with the iOS dimming colour, unblurred fix(core): under a finger, modals drop the backdrop blur Oct 25, 2026
@vjeux

vjeux commented Oct 25, 2026

Copy link
Copy Markdown
Contributor Author

Pushed b221227, reduced to the blur only: under every coarse pointer Dialog (and AlertDialog), Drawer and MobileNav drop the 2 px backdrop blur as a performance fix. The dim stays --color-overlay, with no new token and no colour change; in forced colors the modal surface draws a CanvasText edge; BottomSheet is untouched; the release note says every coarse pointer. #8283 is closed.

@github-actions github-actions Bot removed the needs:spec-owner-review Current knowledge records await owner approval label Oct 25, 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.

Thanks for narrowing this to the blur: the head matches the owner decision recorded on this pull request, and the coarse-pointer, mouse and forced-colors behaviour checks out in Chromium. One change is needed before approval: spec:AST-017 FR3 uses [fix] only when an appearance change restores current visual authority, so this owner-approved change of visual direction should be [feat] (still a patch), and the note should say how a theme keeps the blur (a backdropFilter on the target's ::backdrop). Optionally, drop the new outline from Drawer and MobileNav: their anchored-side border already draws a 1px edge in forced colors, and the outline makes it 2px.

[Automated review]

Comment thread .changeset/modal-backdrop-touch-blur.md Outdated
'@astryxdesign/core': patch
---

[fix] Under every coarse pointer, Dialog (and so AlertDialog), Drawer and MobileNav no longer blur the page behind the modal: the 2px full-viewport backdrop blur is costly to repaint on touch devices. The dim keeps the theme's `--color-overlay`, and in forced colors the modal draws an edge so it still stands apart from the page. With a mouse the blur stays. BottomSheet never blurred.

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 FR3 uses [fix] only when an appearance change restores current visual authority. No current record sets a blur-less dim, so dropping the blur under every coarse pointer is an owner-approved change of visual direction: [feat] (the bump stays patch). FR3 also asks the note to say how to keep the old look: a theme can set backdropFilter: 'blur(2px)' on components.dialog.base['::backdrop'], and Drawer's scrim is its target's ::backdrop too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in e1e0854: the changeset is [feat] (still a patch) and says how a theme keeps the blur under a finger: backdropFilter: 'blur(2px)' on components.dialog.base['::backdrop'], and likewise for drawer and 'mobile-nav'. In Chromium, a theme-layer rule setting that on each component's ::backdrop computes blur(2px) under a finger.

Comment thread packages/core/src/Drawer/Drawer.tsx Outdated
Comment on lines +128 to +134
outline: {
default: 'none',
'@media (pointer: coarse)': {
default: 'none',
'@media (forced-colors: active)': '1px solid CanvasText',
},
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

In forced colors the panel's anchored-side border already draws a 1px CanvasText line facing the page (Chromium computes border-inline-start: solid 1px rgb(0, 0, 0) on the base too), so this outline doubles it to 2px. Dialog has no border and needs its outline; consider dropping this one and MobileNav's (MobileNav.tsx:184-190) with their edge test cases. Not blocking.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in e1e0854: Drawer and MobileNav drop the outline. Their anchored-side border is the forced-colors edge, and Dialog keeps its outline. The jsdom test and the forced-colors browser cases now pin one 1px edge per modal: Dialog's outline, and the side panels' single border facing the page with no outline.

Drawer and MobileNav drop the outline added for forced colors: their
anchored-side border already paints a 1px CanvasText edge facing the page
there, and the outline doubled it. Dialog, which has no border, keeps its
outline. The tests pin one 1px edge per modal in forced colors. The
changeset is [feat], an owner-approved change of visual direction (still a
patch), and says how a theme keeps the blur: backdropFilter on the
component's ::backdrop.
@vjeux vjeux changed the title fix(core): under a finger, modals drop the backdrop blur feat(core): under a finger, modals drop the backdrop blur Oct 25, 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.

The blur change matches the owner decision on this pull request and checks out in Chromium under a coarse pointer, with a mouse and in forced colors; one fix is needed in the changeset. Its theme escape for drawer (backdropFilter: 'blur(2px)' on components.drawer.base['::backdrop']) also matches a non-modal Drawer's popover backdrop, so a theme that follows it would blur the page behind every hasScrim={false} drawer, mouse users included. Naming components.drawer.base[':modal::backdrop'] for Drawer instead restores the blur on the modal drawer under a finger and leaves non-modal drawers untouched.

[Automated review]

Comment thread .changeset/modal-backdrop-touch-blur.md Outdated
'@astryxdesign/core': patch
---

[feat] Under every coarse pointer, Dialog (and so AlertDialog), Drawer and MobileNav no longer blur the page behind the modal: the 2px full-viewport backdrop blur is costly to repaint on touch devices. The dim keeps the theme's `--color-overlay`, and in forced colors the modal keeps an edge so it still stands apart from the page. With a mouse the blur stays. BottomSheet never blurred. A theme keeps the blur under a finger by setting `backdropFilter: 'blur(2px)'` on the component's `::backdrop`: `components.dialog.base['::backdrop']`, and likewise for `drawer` and `'mobile-nav'`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The drawer escape also matches a non-modal Drawer: a hasScrim={false} drawer is a popover="manual" host that keeps the astryx-drawer class, and its popover ::backdrop takes this rule. A theme that sets backdropFilter: 'blur(2px)' on components.drawer.base['::backdrop'] therefore blurs the page behind every non-modal inspector drawer, with a mouse too (in Chromium its backdrop filter goes from none to blur(2px)). Naming components.drawer.base[':modal::backdrop'] for Drawer keeps the old look exactly: defineTheme emits .astryx-drawer:modal::backdrop, which restores the blur on the modal drawer under a finger and leaves the non-modal one at none. The dialog and 'mobile-nav' entries are fine as written.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in df8bb50: the changeset now names components.drawer.base[':modal::backdrop'] for Drawer (emitted as .astryx-drawer:modal::backdrop), next to the Dialog and MobileNav entries. Checked in Chromium with a theme-layer rule: under a finger the modal scrim drawer gets blur(2px) back, and the non-modal hasScrim={false} drawer keeps none under either pointer, where the ::backdrop form had blurred it.

A theme that restores the blur through `components.drawer.base['::backdrop']`
also blurred a non-modal Drawer's popover backdrop (`hasScrim={false}`),
with a mouse too. The note now names `components.drawer.base[':modal::backdrop']`
for Drawer, which `defineTheme` emits as `.astryx-drawer:modal::backdrop`:
the modal drawer gets its blur back under a finger and a non-modal one keeps
none. The Dialog and MobileNav entries are unchanged.

@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.

The blur change matches the owner decision on this pull request and checks out in Chromium under a coarse pointer and with a mouse, but Dialog's new forced-colors edge does not render with the package's prebuilt stylesheet. Dialog's styles also carry the shared focus-outline longhands (outline-style: none and outline-width: 0 by default), which outrank the new outline: 1px solid CanvasText in astryx.css, so Dialog, AlertDialog and CommandPalette show neither blur nor edge under a coarse pointer in forced colors; the browser contract passes only because Storybook compiles StyleX with styleResolution: 'application-order', where the shorthand instead removes the dialog's own focus-visible ring. Drawing the edge with longhands that leave the focus ring alone (for example Dialog's border longhands under the same two media queries, as Drawer and MobileNav do) and checking it against the package build would fix both.

[Automated review]

Comment thread packages/core/src/Dialog/Dialog.tsx Outdated
Comment on lines +191 to +197
outline: {
default: null,
'@media (pointer: coarse)': {
default: null,
'@media (forced-colors: active)': '1px solid CanvasText',
},
},

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 shorthand lands on the same <dialog> as the focus-outline longhands from focusOutlineProps.focusVisible(...) (line 798). In the package's prebuilt astryx.css those longhands (outline-style: none, outline-width: 0, three ids of specificity) beat this rule (two ids), so with the shipped build a Dialog under a coarse pointer in forced colors computes outline-style: none and has no edge. In Storybook's application-order build the shorthand instead nulls the longhands, so the edge draws there but the dialog's own :focus-visible ring is gone. Drawing the edge with Dialog's border longhands (or outline longhands that keep the :focus-visible branches) under the same two queries avoids both.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 9f3f385: Dialog's forced-colors edge is now a 1px CanvasText border (border-width, border-style and border-color under the same two queries), which no other style on the dialog sets, so the focus ring keeps its outline longhands. The package's built astryx.css carries those three border rules and no outline rule for the edge. In Chromium, forced colors under a finger, the open Dialog draws its 1px border and, focused with the keyboard, still shows its 2px focus ring. A full-screen dialog draws no edge.

…eps its outline

Under a coarse pointer in forced colors Dialog drew its edge with the
`outline` shorthand on the same element as the focus ring's outline
longhands. In the package's prebuilt stylesheet the longhands outrank the
shorthand, so Dialog (and AlertDialog, CommandPalette) showed neither blur
nor edge; where StyleX resolves in application order the shorthand instead
erased the dialog's own focus-visible ring. The edge is now a 1px
CanvasText border (border-width, border-style and border-color under the
same two queries), which no other style on the element sets; a full-screen
dialog has none. The jsdom test pins the border and that Dialog declares no
outline under any condition; the browser spec reads one 1px border on every
side with the outline left at the focus ring's.

@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.

The blur-only change now matches the engineering owner's decision on this pull request, and the earlier forced-colors problem is fixed: with the package's prebuilt stylesheet, Dialog draws its 1px edge under a coarse pointer and keeps its keyboard focus ring. Under a coarse pointer the backdrop is no longer blurred and keeps the theme's overlay colour, while mouse and trackpad users see no change. Optionally, name CommandPalette in the changeset as well, since it renders through Dialog and changes the same way.

[Automated review]

@cixzhang

cixzhang commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Posted by Cindy's agent on her behalf.

Cindy's decision on the modal scrim: drop the blur everywhere, desktop included. The page behind a modal is dimmed only, on every pointer, keeping the forced-colours edge. Please update the PR, its changeset ("every coarse pointer" becomes "every pointer") and the record to match.

Since this changes the desktop look, the PR also needs desktop before/after captures (mouse, light and dark) and the named reason, as spec:AST-017 FR59 requires for a desktop change.

vjeux, could you update the PR to match these?

Widens the blur drop from a coarse pointer to every pointer: Dialog (and
so AlertDialog), Drawer and MobileNav no longer blur the page behind the
modal with a mouse either. The dim keeps the theme's --color-overlay, with
no colour change; in forced colors Dialog draws its CanvasText edge under
either pointer and the side panels keep their anchored-side border.
BottomSheet never blurred. A theme keeps the blur with a backdropFilter on
the component's ::backdrop. The jsdom test pins that no modal sets a
backdrop-filter under any pointer, and the browser spec reads the computed
backdrop and edge with a mouse too.
@vjeux vjeux changed the title feat(core): under a finger, modals drop the backdrop blur feat(core): modals drop the backdrop blur Oct 25, 2026
@vjeux

vjeux commented Oct 25, 2026

Copy link
Copy Markdown
Contributor Author

Done in 0c6270b. Dialog (and so AlertDialog), Drawer and MobileNav no longer blur the page behind the modal, under any pointer. The dim stays the theme's --color-overlay, and the forced-colours edge stays: Dialog's 1px CanvasText border, and the side panels' anchored-side border. The changeset now says every pointer and keeps the same theme keys for keeping the blur. The description records your decision and adds desktop before/after evidence, with a mouse in light and dark.

@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.

The blur removal matches the owner's decision to drop it under every pointer, and it checks out in Chromium with a mouse, a finger and forced colors, but one CI change is needed before approval. Since #8367, main discovers browser accessibility specs from routing lines in each spec's header, and its routing test rejects any pr-a11y step that names a spec file, so test-node (2/4) and the required test check fail on this pull request. Please rebase onto main, remove the "Run modal backdrop contract" step, and add a @pr-a11y scope line (or @pr-a11y always) to ModalBackdrop.a11y.browser.spec.ts so the contract still runs when the modals change. Optionally, name CommandPalette in the changeset too, since it renders through Dialog and changes the same way.

[Automated review]

Comment thread .github/workflows/ci.yml Outdated
# with an edge in forced colors, under any pointer. jsdom computes no
# ::backdrop and drops backdrop-filter, so only a real engine shows it.
# Its own --output directory, as the contracts below.
- name: Run modal backdrop contract

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Since #8367, main discovers browser accessibility specs from the @pr-a11y routing lines in each spec's header, and .github/scripts/ci-test-routing.test.mjs ("names no spec file in any step") fails on any pr-a11y step that names a spec file. That makes test-node (2/4) and the required test check fail on this pull request. After rebasing, drop this step and add a routing line to the spec's header instead, for example:

// @pr-a11y scope ^(packages/core/src/(Dialog|AlertDialog|CommandPalette|Drawer|MobileNav|BottomSheet|Overlay)/|packages/core/src/__tests__/ModalBackdrop)

…uting line

Main's pr-a11y lane now discovers browser accessibility specs by file name
(meta#8367), and a step that names a spec file fails ci-test-routing's "names no
spec file in any step". This drops the "Run modal backdrop contract" step,
so this pull request no longer touches ci.yml, and gives
ModalBackdrop.a11y.browser.spec.ts a `@pr-a11y scope` line: a pull request
runs it when it changes the spec, Dialog, AlertDialog, Drawer, MobileNav or
BottomSheet, or their stories.

@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 recommended: removing the modal backdrop blur matches the engineering owner's decision to drop it under every pointer, and Dialog, AlertDialog, CommandPalette, the scrim Drawer and MobileNav all render unblurred under the unchanged dim, with Dialog's 1px edge showing in forced colors. No changes are required. Optionally, the changeset could also name CommandPalette, which renders through Dialog and changes the same way, and Lightbox, which still blurs its backdrop, could follow in a separate change if the owner wants "everywhere" to cover it.

[Automated review]

vjeux added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
…#8120)

The changeset and the backdrop comment said the modal backdrops already dim
without a blur; on main Dialog, Drawer and MobileNav still blur until meta#8120
lands. Both now cite the owners' decision there instead (every modal, every
pointer). No behavior change.
@vjeux
vjeux marked this pull request as draft October 29, 2026 22:31

This branch was successfully deployed

1 active deployment
Preview — 00320eb1 Deployed Oct 25, 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. needs:design-review Affects visuals — Design should review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants