Component
Dialog
Package version
9.72.10
React version
19.2.3
Environment
System:
OS: macOS 15.6.1
CPU: (16) arm64 Apple M4 Max
Memory: 158.73 MB / 64.00 GB
Shell: 5.9 - /bin/zsh
Browsers:
Chrome: 144.0.7559.59
Edge: 144.0.3719.82
Safari: 18.6
npmPackages:
@analytics/google-analytics: ^1.1.0 => 1.1.0
@babel/core: ^7.28.5 => 7.28.5
@babel/preset-react: ^7.28.5 => 7.28.5
@fluentui/react-components: ^9.72.10 => 9.72.10
@material-symbols-svg/react-rounded: ^0.1.19 => 0.1.19
@microsoft/applicationinsights-react-js: ^19.3.8 => 19.3.8
@microsoft/applicationinsights-web: ^3.3.10 => 3.3.10
@microsoft/clarity: ^1.0.2 => 1.0.2
@microsoft/signalr: ^10.0.0 => 10.0.0
@nx/devkit: 22.3.3 => 22.3.3
@nx/eslint: 22.3.3 => 22.3.3
@nx/eslint-plugin: 22.3.3 => 22.3.3
@nx/jest: 22.3.3 => 22.3.3
@nx/js: 22.3.3 => 22.3.3
@nx/playwright: 22.3.3 => 22.3.3
@nx/react: 22.3.3 => 22.3.3
@nx/vite: 22.3.3 => 22.3.3
@nx/vitest: 22.3.3 => 22.3.3
@nx/web: 22.3.3 => 22.3.3
@nx/workspace: 22.3.3 => 22.3.3
@playwright/test: ^1.49.1 => 1.57.0
@swc-node/register: ~ 1.11.1 => 1.11.1
@swc/cli: 0.7.9 => 0.7.9
@swc/core: 1.15.8 => 1.15.8
@swc/helpers: 0.5.18 => 0.5.18
@testing-library/jest-dom: ^6.6.3 => 6.9.1
@testing-library/react: 16.1.0 => 16.1.0
@testing-library/user-event: ^14.5.2 => 14.6.1
@trivago/prettier-plugin-sort-imports: ^6.0.2 => 6.0.2
@types/jest: 30.0.0 => 30.0.0
@types/leaflet: ^1.9.21 => 1.9.21
@types/node: ^22.10.2 => 22.19.5
@types/qrcode: ^1.5.6 => 1.5.6
@types/qs: ^6.14.0 => 6.14.0
@types/react: 19.2.7 => 19.2.7
@types/react-dom: 19.2.3 => 19.2.3
@types/react-google-recaptcha: ^2.1.9 => 2.1.9
@types/react-router-dom: ^5.3.3 => 5.3.3
@types/ua-parser-js: ^0.7.39 => 0.7.39
@types/use-analytics: ^0.0.3 => 0.0.3
@types/webappsec-credential-management: ^0.6.9 => 0.6.9
@typescript-eslint/eslint-plugin: 8.52.0 => 8.52.0
@typescript-eslint/parser: 8.52.0 => 8.52.0
@uidotdev/usehooks: ^2.4.1 => 2.4.1
@vitejs/plugin-react: 5.1.2 => 5.1.2
@vitest/coverage-v8: 4.0.16 => 4.0.16
@vitest/ui: 4.0.16 => 4.0.16
analytics: ^0.8.19 => 0.8.19
axios: ^1.13.2 => 1.13.2
axios-retry: ^4.5.0 => 4.5.0
env-cmd: ^11.0.0 => 11.0.0
eslint: 9.39.2 => 9.39.2
eslint-config-prettier: 10.1.8 => 10.1.8
eslint-plugin-import: 2.32.0 => 2.32.0
eslint-plugin-jsx-a11y: 6.10.2 => 6.10.2
eslint-plugin-playwright: ^2.4.0 => 2.5.0
eslint-plugin-prettier: ^5.5.4 => 5.5.4
eslint-plugin-react: 7.37.5 => 7.37.5
eslint-plugin-react-google-translate: ^0.1.1 => 0.1.1
eslint-plugin-react-hooks: 7.0.1 => 7.0.1
glob: ^13.0.0 => 13.0.0
html-to-image: ^1.11.13 => 1.11.13
i18next: ^25.7.4 => 25.7.4
jest: 30.2.0 => 30.2.0
jest-cucumber: ^4.5.0 => 4.5.0
jest-environment-jsdom: 30.2.0 => 30.2.0
jest-util: 30.2.0 => 30.2.0
jiti: 2.6.1 => 2.6.1
jsdom: ~ 27.4.0 => 27.4.0
jspdf: ^4.0.0 => 4.0.0
jwt-decode: ^4.0.0 => 4.0.0
leaflet: ^1.9.4 => 1.9.4
nx: 22.3.3 => 22.3.3
nx-cloud: 19.1.0 => 19.1.0
patch-package: ^8.0.1 => 8.0.1
playwright-bdd: ^8.4.2 => 8.4.2
prettier: ^3.7.4 => 3.7.4
qrcode: ^1.5.4 => 1.5.4
qs: ^6.14.1 => 6.14.1
react: 19.2.3 => 19.2.3
react-cookie: ^8.0.1 => 8.0.1
react-country-flag: ^3.1.0 => 3.1.0
react-dom: 19.2.3 => 19.2.3
react-google-recaptcha: ^3.1.0 => 3.1.0
react-hook-form: ^7.70.0 => 7.71.0
react-i18next: ^16.5.1 => 16.5.2
react-leaflet: ^5.0.0 => 5.0.0
react-leaflet-cluster: ^4.0.0 => 4.0.0
react-markdown: ^10.1.0 => 10.1.0
react-pdf: ^10.3.0 => 10.3.0
react-router-dom: ^7.12.0 => 7.12.0
ts-jest: 29.4.6 => 29.4.6
ts-node: 10.9.2 => 10.9.2
tslib: ^2.8.1 => 2.8.1
typescript: 5.9.3 => 5.9.3
typescript-eslint: ^8.52.0 => 8.52.0
ua-parser-js: ^1.0.41 => 1.0.41
use-analytics: ^1.1.0 => 1.1.0
uuid: ^12.0.0 => 12.0.0
vite: 7.3.1 => 7.3.1
vite-plugin-dts: 4.5.4 => 4.5.4
vite-plugin-pwa: ^1.2.0 => 1.2.0
vite-plugin-singlefile: ^2.3.0 => 2.3.0
vite-plugin-svgr: ^4.5.0 => 4.5.0
vite-tsconfig-paths: ~ 6.0.3 => 6.0.4
vitest: 4.0.16 => 4.0.16
Current Behavior
The useDisableBodyScroll has a conditional check that skips applying scroll lock when the body content doesn't exceed the viewport height:
const isHorizontalScrollbarVisible =
// When the window is a fractional height, `innerHeight` always rounds down but `clientHeight` rounds either up or down depending on the value.
// To properly compare the body clientHeight to the window innerHeight, manually round down the fractional value to match innerHeight's calculation.
Math . floor ( targetDocument . body . getBoundingClientRect ( ) . height ) > ( targetDocument . defaultView ?. innerHeight ?? 0 ) ;
if ( ! isHorizontalScrollbarVisible ) {
return ;
}
Problem:
While this optimization makes sense for desktop browsers (no scrollbar = nothing to scroll = no need to lock), it causes issues on mobile/touch devices:
Overscroll/rubber-banding: Even when content fits the viewport, users can still drag/swipe on the backdrop and trigger the overscroll bounce effect
Non-modal feel: The page visually moves behind the dialog, making it feel broken and non-modal
iOS Safari: The issue is particularly noticeable on iOS where overscroll behavior is more pronounced
Untitled.mp4
Expected Behavior
When a modal dialog is open, background scroll should be completely locked regardless of whether content exceeds the viewport, especially on touch devices.
Simplest solution would be to just get rid of this check. If that's ok I can open a PR. Otherwise maybe have some check that will only run this check on desktop devices.
Reproduction
https://stackblitz.com/edit/vitejs-vite-ekhfgvlg?file=src%2FApp.tsx,package.json&terminal=dev
Steps to reproduce
On a mobile device (or mobile emulator), touch the backdrop and drag up/down
Observe: The page rubber-bands/overscrolls even though the dialog should block all interaction
Are you reporting an Accessibility issue?
None
Suggested severity
High - No workaround
Products/sites affected
No response
Are you willing to submit a PR to fix?
yes
Validations
Component
Dialog
Package version
9.72.10
React version
19.2.3
Environment
System: OS: macOS 15.6.1 CPU: (16) arm64 Apple M4 Max Memory: 158.73 MB / 64.00 GB Shell: 5.9 - /bin/zsh Browsers: Chrome: 144.0.7559.59 Edge: 144.0.3719.82 Safari: 18.6 npmPackages: @analytics/google-analytics: ^1.1.0 => 1.1.0 @babel/core: ^7.28.5 => 7.28.5 @babel/preset-react: ^7.28.5 => 7.28.5 @fluentui/react-components: ^9.72.10 => 9.72.10 @material-symbols-svg/react-rounded: ^0.1.19 => 0.1.19 @microsoft/applicationinsights-react-js: ^19.3.8 => 19.3.8 @microsoft/applicationinsights-web: ^3.3.10 => 3.3.10 @microsoft/clarity: ^1.0.2 => 1.0.2 @microsoft/signalr: ^10.0.0 => 10.0.0 @nx/devkit: 22.3.3 => 22.3.3 @nx/eslint: 22.3.3 => 22.3.3 @nx/eslint-plugin: 22.3.3 => 22.3.3 @nx/jest: 22.3.3 => 22.3.3 @nx/js: 22.3.3 => 22.3.3 @nx/playwright: 22.3.3 => 22.3.3 @nx/react: 22.3.3 => 22.3.3 @nx/vite: 22.3.3 => 22.3.3 @nx/vitest: 22.3.3 => 22.3.3 @nx/web: 22.3.3 => 22.3.3 @nx/workspace: 22.3.3 => 22.3.3 @playwright/test: ^1.49.1 => 1.57.0 @swc-node/register: ~1.11.1 => 1.11.1 @swc/cli: 0.7.9 => 0.7.9 @swc/core: 1.15.8 => 1.15.8 @swc/helpers: 0.5.18 => 0.5.18 @testing-library/jest-dom: ^6.6.3 => 6.9.1 @testing-library/react: 16.1.0 => 16.1.0 @testing-library/user-event: ^14.5.2 => 14.6.1 @trivago/prettier-plugin-sort-imports: ^6.0.2 => 6.0.2 @types/jest: 30.0.0 => 30.0.0 @types/leaflet: ^1.9.21 => 1.9.21 @types/node: ^22.10.2 => 22.19.5 @types/qrcode: ^1.5.6 => 1.5.6 @types/qs: ^6.14.0 => 6.14.0 @types/react: 19.2.7 => 19.2.7 @types/react-dom: 19.2.3 => 19.2.3 @types/react-google-recaptcha: ^2.1.9 => 2.1.9 @types/react-router-dom: ^5.3.3 => 5.3.3 @types/ua-parser-js: ^0.7.39 => 0.7.39 @types/use-analytics: ^0.0.3 => 0.0.3 @types/webappsec-credential-management: ^0.6.9 => 0.6.9 @typescript-eslint/eslint-plugin: 8.52.0 => 8.52.0 @typescript-eslint/parser: 8.52.0 => 8.52.0 @uidotdev/usehooks: ^2.4.1 => 2.4.1 @vitejs/plugin-react: 5.1.2 => 5.1.2 @vitest/coverage-v8: 4.0.16 => 4.0.16 @vitest/ui: 4.0.16 => 4.0.16 analytics: ^0.8.19 => 0.8.19 axios: ^1.13.2 => 1.13.2 axios-retry: ^4.5.0 => 4.5.0 env-cmd: ^11.0.0 => 11.0.0 eslint: 9.39.2 => 9.39.2 eslint-config-prettier: 10.1.8 => 10.1.8 eslint-plugin-import: 2.32.0 => 2.32.0 eslint-plugin-jsx-a11y: 6.10.2 => 6.10.2 eslint-plugin-playwright: ^2.4.0 => 2.5.0 eslint-plugin-prettier: ^5.5.4 => 5.5.4 eslint-plugin-react: 7.37.5 => 7.37.5 eslint-plugin-react-google-translate: ^0.1.1 => 0.1.1 eslint-plugin-react-hooks: 7.0.1 => 7.0.1 glob: ^13.0.0 => 13.0.0 html-to-image: ^1.11.13 => 1.11.13 i18next: ^25.7.4 => 25.7.4 jest: 30.2.0 => 30.2.0 jest-cucumber: ^4.5.0 => 4.5.0 jest-environment-jsdom: 30.2.0 => 30.2.0 jest-util: 30.2.0 => 30.2.0 jiti: 2.6.1 => 2.6.1 jsdom: ~27.4.0 => 27.4.0 jspdf: ^4.0.0 => 4.0.0 jwt-decode: ^4.0.0 => 4.0.0 leaflet: ^1.9.4 => 1.9.4 nx: 22.3.3 => 22.3.3 nx-cloud: 19.1.0 => 19.1.0 patch-package: ^8.0.1 => 8.0.1 playwright-bdd: ^8.4.2 => 8.4.2 prettier: ^3.7.4 => 3.7.4 qrcode: ^1.5.4 => 1.5.4 qs: ^6.14.1 => 6.14.1 react: 19.2.3 => 19.2.3 react-cookie: ^8.0.1 => 8.0.1 react-country-flag: ^3.1.0 => 3.1.0 react-dom: 19.2.3 => 19.2.3 react-google-recaptcha: ^3.1.0 => 3.1.0 react-hook-form: ^7.70.0 => 7.71.0 react-i18next: ^16.5.1 => 16.5.2 react-leaflet: ^5.0.0 => 5.0.0 react-leaflet-cluster: ^4.0.0 => 4.0.0 react-markdown: ^10.1.0 => 10.1.0 react-pdf: ^10.3.0 => 10.3.0 react-router-dom: ^7.12.0 => 7.12.0 ts-jest: 29.4.6 => 29.4.6 ts-node: 10.9.2 => 10.9.2 tslib: ^2.8.1 => 2.8.1 typescript: 5.9.3 => 5.9.3 typescript-eslint: ^8.52.0 => 8.52.0 ua-parser-js: ^1.0.41 => 1.0.41 use-analytics: ^1.1.0 => 1.1.0 uuid: ^12.0.0 => 12.0.0 vite: 7.3.1 => 7.3.1 vite-plugin-dts: 4.5.4 => 4.5.4 vite-plugin-pwa: ^1.2.0 => 1.2.0 vite-plugin-singlefile: ^2.3.0 => 2.3.0 vite-plugin-svgr: ^4.5.0 => 4.5.0 vite-tsconfig-paths: ~6.0.3 => 6.0.4 vitest: 4.0.16 => 4.0.16Current Behavior
The
useDisableBodyScrollhas a conditional check that skips applying scroll lock when the body content doesn't exceed the viewport height:fluentui/packages/react-components/react-dialog/library/src/utils/useDisableBodyScroll.ts
Lines 24 to 30 in f595806
Problem:
While this optimization makes sense for desktop browsers (no scrollbar = nothing to scroll = no need to lock), it causes issues on mobile/touch devices:
Untitled.mp4
Expected Behavior
When a modal dialog is open, background scroll should be completely locked regardless of whether content exceeds the viewport, especially on touch devices.
Simplest solution would be to just get rid of this check. If that's ok I can open a PR. Otherwise maybe have some check that will only run this check on desktop devices.
Reproduction
https://stackblitz.com/edit/vitejs-vite-ekhfgvlg?file=src%2FApp.tsx,package.json&terminal=dev
Steps to reproduce
Are you reporting an Accessibility issue?
None
Suggested severity
High - No workaround
Products/sites affected
No response
Are you willing to submit a PR to fix?
yes
Validations