Skip to content

fix(react-slider): stop emitting the deprecated slider-vertical appearance - #36946

Open
Jake Salvesen (jakesalvesen) wants to merge 7 commits into
microsoft:masterfrom
jakesalvesen:fix/36945-slider-vertical-appearance
Open

Jake Salvesen (jakesalvesen) wants to merge 7 commits into
microsoft:masterfrom
jakesalvesen:fix/36945-slider-vertical-appearance

Conversation

@jakesalvesen

@jakesalvesen Jake Salvesen (jakesalvesen) commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Previous Behavior

Slider vertical logged the Chromium deprecation warning for the non-standard -webkit-appearance: slider-vertical value in every app that mounts a vertical slider:

[Deprecation] The keyword 'slider-vertical' specified to an 'appearance' property is not standardized. It will be removed in the future. Use <input type=range style="writing-mode: vertical-lr; direction: rtl"> instead.

#34022 moved the value into an @supports not (writing-mode: sideways-lr) branch, which is right for browsers that lack sideways-lr. Chromium 132 shipped sideways-lr, so in current Chromium that branch never matches and the vertical slider is styled by the @supports (writing-mode: sideways-lr) branch. The deprecated declaration is dead in those browsers, but Chromium reports deprecated CSS values while parsing: the warning fires even when the condition does not match, when the selector matches no element, and when the rule is inserted at runtime through CSSStyleSheet.insertRule, which is how Griffel inserts styles.

New Behavior

vertical uses the standardized declarations directly:

vertical: {
  height: '100%',
  width: `var(${sliderThumbSizeVar})`,
  writingMode: 'vertical-lr',
  // Keep values increasing upwards in RTL too; Griffel would flip this to ltr.
  direction: 'rtl /* @noflip */' as 'rtl',
},

The deprecated value is no longer emitted with the stylesheet. Engines that cannot orient a range input with writing-mode get it at runtime instead, from a useEffect in useSlider_unstable that runs only for vertical sliders and calls ensureLegacyVerticalSliderInput(input, renderer.styleElementAttributes) with the rendered input element.

Detection. legacyVerticalSliderAppearance.ts measures an off-screen <input type="range"> with writing-mode: vertical-lr; direction: rtl, rendered inside a shadow root of its own so no application selector reaches it or its range pseudo-elements, and compares offsetWidth/offsetHeight (pre-transform). A range input that follows writing-mode swaps its axes. This is a layout probe rather than CSS.supports('writing-mode', 'sideways-lr') because the two features shipped separately: Chrome 124-131 and Safari 17.4-18.3 orient range inputs but do not support sideways-lr. A conclusive result is cached per document.

The rule, added only when the probe says the engine cannot orient range inputs:

.fui-Slider__input[orient="vertical"] { -webkit-appearance: slider-vertical; }

It is scoped to [orient="vertical"] because in those engines the value also turns a horizontal range input vertical.

Where it goes. Into the styling root the Slider renders in: a shadow root (as with createShadowDOMRenderer) adopts a constructed sheet, or gets a <style> when constructed sheets are unavailable; otherwise the document head gets a <style> carrying the Griffel renderer's styleElementAttributes, so a CSP nonce set on a DOM renderer applies. Each root is deduplicated by what it already contains, so a removed rule is restored and a second copy of the package does not add it again. If the rendered input is not reachable through a ref, the provider's targetDocument is used.

When it is decided. If the document has no layout yet (for example a Slider mounted in a display: none iframe), the probe is inconclusive and nothing is installed on a guess; a ResizeObserver on the input decides once it is laid out, so revealing it is enough with no prop change. The observer is disconnected on a decision or on unmount.

RTL. direction is marked /* @noflip */, as in useRangeSliderStyles.styles.ts. Without it, Griffel flips the value to ltr under an RTL provider, which reverses the native input: the top sets the minimum and ArrowUp decreases the value. On master that already happened wherever the @supports (writing-mode: sideways-lr) branch applies (verified in Chrome 156), and without the marker this PR would have extended it to Chromium 124-131.

Resulting behavior:

  • Chromium/Edge 124+, Firefox 120+ and Safari 17.4+ take the stylesheet-only path and log no deprecation warning.
  • Chromium/Edge 84-123 and Safari 14.1-17.3 keep a working vertical slider from the runtime rule. Verified in Chrome 123 with vertical form controls disabled; Safari before 17.4 was not available to me.
  • Firefox before 120 was already taking the writing-mode branch before this change (it supports sideways-lr since 43), and -webkit-appearance: slider-vertical is not a Gecko value, so its behavior there is unchanged by this PR.
  • Vertical sliders keep the maximum at the top in RTL, for pointer and keyboard interaction.
  • Server rendering is unaffected because the work happens in an effect. Hydration cannot mismatch because only a style is added and component markup never changes.
  • The Slider snapshots keep semantic class names (fui-Slider__input) and no markup changes, so no snapshot needs regeneration.

Validation

  • Chrome 155.0.8059.85 (Windows 11): before the change, the rule the package emits logs the deprecation while parsing even though CSS.supports('writing-mode', 'sideways-lr') is true and the rule never applies. The repro page in [Bug]: Vertical Slider still logs the deprecated 'slider-vertical' warning in Chromium 132+ #36945 shows this side by side.
  • Probe across engines (Chromium 156, Chrome 123, Chrome 123 with FormControlsVerticalWritingModeSupport disabled, Firefox 157, WebKit) under application styles including input { width: 100% }, !important input sizing, ::-webkit-slider-runnable-track and ::-webkit-slider-thumb sizing, appearance: none !important, an RTL vertical html, and a rotated and zoomed body: 40/40 correct, with nothing installed on supporting engines.
  • In the legacy mode, a vertical Slider input in a shadow root receives the adopted sheet and drags vertically (top 100, bottom 0); in supporting engines nothing is installed and vertical drag still works.
  • Cypress component tests (legacyVerticalSliderAppearance.cy.tsx, legacyVerticalSliderAppearance.shadowDom.cy.tsx, using the same cypress.config.ts / tsconfig.cy.json setup as react-color-picker): probe isolation under global input and range pseudo-element styles, a vertical Slider under all of them, the createShadowDOMRenderer setup, and a Slider mounted in a display: none iframe and then revealed. Each review case fails against the code before its fix.
  • Cypress Slider.cy.tsx clicks the top and bottom of a vertical Slider's input and presses ArrowUp/ArrowDown with real events, in LTR and RTL. RTL fails on master in Chrome 156 and on this branch without @noflip in Electron 130 and Chrome 156, and passes with it.
  • Jest (legacyVerticalSliderAppearance.test.ts) covers the probe, per-root installation and dedupe, restore after removal, cross-copy dedupe, a document without a head, the document fallback, the CSP attributes, and the inconclusive retry and its cleanup.
  • Locally on Node 24.22 with this repository's toolchain: nx run-many -t test,type-check,lint -p react-slider passes (69 tests, 3 snapshots), and cypress run --component passes 20/20 on React 19 (Electron 130 and Chrome 156) and in the React 17 (Electron 118) and React 18 (Electron 130) integration projects. The changed files pass prettier --check.

Related Issue(s)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Removing the fallback breaks vertical interaction in older browsers still covered by the published support matrix.

1 open issue
What changed in this PR

Removes deprecated vertical Slider CSS to address Chromium warnings reported in #36945, following #34022.

Changes:

  • Applies standardized vertical input styles directly and removes the legacy fallback.
  • Adds a patch release change file for @fluentui/react-slider.
File Description
packages/​react-components/​react-slider/​library/​src/​components/​Slider/​useSliderStyles.styles.ts Replaces conditional appearance rules with standardized vertical styles.
change/​change-@fluentui-react-slider-b43fe38a-5da5-412c-a9f6-692ebb39bb92.json Records the Slider fix for release.

Review effort: Balanced

@jakesalvesen
Jake Salvesen (jakesalvesen) force-pushed the fix/36945-slider-vertical-appearance branch 3 times, most recently from 14120c0 to 3cf0e46 Compare October 20, 2026 13:33

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for working through the compatibility fixes—the vertical-only selector and nonce handling address those concerns. There’s one remaining issue with the capability probe: global input sizing can make a supporting browser appear unsupported and bring the deprecation warning back. Could we isolate the probe from application styles and cover that case before merging?

@PaulGMardling Paul Mardling (PaulGMardling) left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A couple more comments to look at

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The earlier fixes look good. I found two more issues with CSP and stylesheet detection—details in the inline comments. Could we sort those out before merging?

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

CSP handling, React 19 ref cleanup, and stylesheet deduplication have unresolved correctness issues.

4 open issues

Review effort: Balanced

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good now—the issues I raised are addressed. Thanks for sticking with the fixes and adding the regression tests. Happy to approve!

@jakesalvesen

Jake Salvesen (jakesalvesen) commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor Author

Thank you so much, Paul Mardling (@PaulGMardling). I really appreciate the time and care you put into these reviews. Every round caught something real: application input sizing, range pseudo-elements, shadow roots, hidden mounts, then CSP and stylesheet detection. The Slider is much more robust for it, and the regression tests will keep it that way.

Since you confirmed everything is addressed, I've resolved the remaining conversations.

Two small things, in case they're useful. The CI, Check packages, Bundle size and VRT workflows are waiting for maintainer approval to run on this fork (they show "action required"). The PR also still needs a code-owner review from @microsoft/fluentui-react-build, because the Cypress setup adds cypress.config.ts and touches the package tsconfig files. No rush at all, whenever you or the team have a moment. Thanks again!

@jakesalvesen
Jake Salvesen (jakesalvesen) force-pushed the fix/36945-slider-vertical-appearance branch from 043c669 to 41b8f06 Compare October 23, 2026 04:00
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

📊 Bundle size report

Package & Exports Baseline (minified/GZIP) PR Change
react-components
react-components: all base hooks
219.623 kB
68.542 kB
219.629 kB
68.543 kB
6 B
1 B
react-components
react-components: entire library
1.284 MB
322.303 kB
1.286 MB
323.213 kB
2.215 kB
910 B
react-headless-components-preview
react-headless-components-preview: entire library
269.744 kB
80.318 kB
269.75 kB
80.318 kB
6 B
react-slider
Slider
33.727 kB
11.61 kB
35.995 kB
12.387 kB
2.268 kB
777 B
Unchanged fixtures
Package & Exports Size (minified/GZIP)
react-components
react-components: Button, FluentProvider & webLightTheme
68.027 kB
19.76 kB
react-components
react-components: Accordion, Button, FluentProvider, Image, Menu, Popover
224.134 kB
67.419 kB
react-components
react-components: FluentProvider & webLightTheme
41.257 kB
13.798 kB
react-headless-components-preview
react-headless-components-preview: Calendar
42.082 kB
13.905 kB
react-headless-components-preview
@fluentui/react-headless-components-preview/tag-picker
49.563 kB
16.254 kB
react-headless-components-preview
@fluentui/react-headless-components-preview/teaching-popover
32.36 kB
10.867 kB
react-portal-compat
PortalCompatProvider
5.198 kB
2.104 kB
react-timepicker-compat
TimePicker
139.754 kB
45.561 kB
🤖 This report was generated against 1f59fc2121f828b57713915f6aa035b4e77c7d47

@github-actions

Copy link
Copy Markdown

Pull request demo site: URL

@jakesalvesen

Copy link
Copy Markdown
Contributor Author

Thank you for updating the branch and starting CI, Paul Mardling (@PaulGMardling)! The react-major-versions-integration failure came from our new Cypress specs, not the Slider itself:

  • They imported createRoot from react-dom/client, which doesn't exist in React 17, so the React 17 bundle failed to compile. They now render through mount and a portal into the iframe or shadow root, which works on React 17, 18 and 19.
  • Once compiled, the React 17 run uses Cypress 13 with Electron 118, which can't orient range inputs with writing-mode (Chromium 124+). The specs assumed a modern engine, so they now take the expected result from the browser version: on Electron 118 they check that the fallback is installed, and on newer engines that it isn't.

Both specs pass locally on React 17 (Electron 118), 18 and 19 (Electron 130). Sorry for the extra round, and thanks again for your patience.

@tudorpopams

Copy link
Copy Markdown
Contributor

[P2] RTL vertical sliders reverse native interaction on Chromium 124–131

Affected: useSliderStyles.styles.ts:262–263, reviewed at head 59282829140a7b64c33dea589be8b8da5dffd588.

Render <Slider vertical /> inside <FluentProvider dir="rtl">. Griffel flips the new direction: 'rtl' declaration to ltr. Browsers supporting vertical controls but not sideways-lr now switch from the legacy appearance to this reversed native input, while the visible thumb still assumes the maximum is at the top.

The paranoid reviewer verified exact base/head Slider sources in Chromium 130.0.6723.137 / Electron 33.2.1, where CSS.supports('writing-mode', 'sideways-lr') is false:

Native click Base ea1a99bf2bc5e2359192ae463c5fdf5d1923f57b PR head Expected
Top 100 0 100
Bottom 0 100 0

LTR passed; RTL failed with expected '0' to equal '100'. Chromium 132+ already took the writing-mode branch before this PR; the introduced regression is the newly switched browser range.

Please prevent RTL auto-flipping here, e.g. direction: 'rtl /* @noflip */', and cover native RTL interaction.

Verification: isolated scratch Nx target:

node node_modules/nx/bin/nx.js run review:component --outputStyle=stream

Limitations: this used pinned Slider sources with installed dependencies, supplying the missing getPercentInRange helper from the pinned head—not a pristine full-PR build. Native interaction was verified in Chromium 130; pre-124 Chromium and older Safari were not tested.

@jakesalvesen

Copy link
Copy Markdown
Contributor Author

Thank you for the careful review and the clear repro, Tudor Popa (@tudorpopams). You're right. I reproduced it with a new Cypress spec, Slider.cy.tsx, which clicks the top and bottom of a vertical Slider and presses ArrowUp/ArrowDown with real events, in LTR and RTL:

RTL, vertical Slider Electron 130 (Chromium 124–131) Chrome 156
master ✅ (legacy appearance) ❌ top click → 0, ArrowUp → 49
this PR before the fix ❌ top click → 0, ArrowUp → 49 ❌ same
this PR with @noflip ✅ ✅

LTR passes in every case. So this PR extended an existing problem to 124–131: on master, RTL was already reversed wherever the @supports (writing-mode: sideways-lr) branch applies, because Griffel flipped that direction: 'rtl' too.

Fixed in 5c481d7 with direction: 'rtl /* @noflip */' as 'rtl', matching useRangeSliderStyles.styles.ts. The new spec and the existing ones pass on React 17 (Electron 118, where the legacy appearance applies), React 18 and 19 (Electron 130), and in Chrome 156. I've also updated the change file and the PR description to match.

Thanks again for catching this!

@github-actions

Copy link
Copy Markdown

🕵 . No visual changes in this PR

…rance

Slider vertical still logs the Chromium deprecation for the non-standard
-webkit-appearance: slider-vertical value, even though the browser renders the
slider with writing-mode: vertical-lr.

microsoft#34022 moved the value into an @supports not (writing-mode: sideways-lr) branch,
which is right for browsers without that feature. Chromium 132 added
sideways-lr, so the branch is now dead there, but Chromium still reports the
deprecated value while parsing the CSS - the warning is emitted even when the
condition does not match and when the selector matches no element, including
rules inserted at runtime by Griffel.

Use the standardized declarations unconditionally. Vertically oriented form
controls support writing-mode since Chrome 124, Firefox 120 and Safari 17.4,
so every browser that previously took the fallback now gets the same rendering
through writing-mode, and browsers older than that lose the legacy native
vertical appearance - which the removal of the keyword forces eventually anyway.
Chromium reports the deprecated `-webkit-appearance: slider-vertical` value while parsing a stylesheet, so the
fallback cannot live in the Slider styles even inside an `@supports not (...)` branch that never matches.

`useSlider_unstable` now installs it from an effect, only for vertical sliders, in engines where a layout probe
shows range inputs do not follow `writing-mode`. The rule is scoped to `[orient="vertical"]` because the legacy
value also turns horizontal inputs vertical, the style element carries the Griffel renderer attributes so a CSP
nonce applies, an inconclusive probe (no layout) installs nothing, and each document is probed once. Server
rendering is unaffected and markup never changes, so hydration cannot mismatch.
A global rule such as `input { width: 100% }` resized the probe, so a browser that orients range inputs measured as
horizontal, installed the deprecated `slider-vertical` rule and brought the warning back for the whole document.

The probe now resets every author declaration with `all: initial !important` and is measured with
`offsetWidth`/`offsetHeight`, which report the layout box before transforms, so neither global input sizing nor a
transformed ancestor can change the reading.

Adds Cypress component tests that measure the probe in a real layout engine under several global styles, including the
reported `input { width: 100% }` case, and mount a vertical Slider under those styles.
…try hidden mounts

- Insert the fallback into the root the Slider renders in: a shadow root (as with
  createShadowDOMRenderer) gets a constructed sheet, otherwise the document head gets a
  style element. Each root is deduplicated by what it already contains, so a removed rule
  is restored and a second copy of the package does not add it again.
- When the probe is inconclusive (no layout, e.g. a display: none iframe), watch the input
  and decide once it is laid out, so revealing it is enough without any prop change.
- Measure the probe inside its own shadow root, so application styles on range
  pseudo-elements (::-webkit-slider-runnable-track, ::-webkit-slider-thumb) cannot skew it,
  and restore the native appearance with every spelling after all: initial.
- Fall back to the document when the rendered input is not reachable through a ref.

Adds Cypress coverage for the createShadowDOMRenderer setup, mounting while hidden and
revealing, and track/thumb pseudo-element styles, plus Jest coverage for per-root dedupe,
restore after removal, cross-copy dedupe and a document without a head.
…ed sheets and ref wrapping

- Apply the capability probe's host and input styles through CSSOM
  (`style.cssText`) instead of the `style` attribute, which a nonce-only
  `style-src` or `style-src-attr 'none'` blocks.
- Recognise an existing fallback only by its selector and declaration, so an
  unrelated adopted `slider-vertical` sheet no longer suppresses the Slider rule.
- Skip installing where the engine cannot parse the value, checked once per
  window and only after the root is found not to carry the rule, since Chromium
  also reports the deprecation for that query.
- Locate the styling root from the Slider root element, which the base hook
  already composes, so the input ref is passed through unwrapped and keeps
  React 19 ref cleanup.
- Regenerate the API report for the value import of React added earlier.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…s without vertical range support

- Render through `mount` and a portal into the iframe or shadow root instead of
  `createRoot` from `react-dom/client`, which does not exist in React 17.
- Take the expected vertical range support from the browser version: the React 17
  run uses Cypress 13 with Electron 118 (Chromium 118), which needs the fallback,
  while React 18 and 19 run on a Chromium with native support.
- Force a legacy probe reading instead of swapping the probe axes, so the
  simulation reads as legacy on every engine.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Under an RTL provider Griffel flips `direction: 'rtl'` to `ltr`, which reverses the
native vertical range input: the top sets the minimum and ArrowUp decreases the
value. On master this already affected engines that take the `writing-mode` path
(Chromium 132+); this PR moved Chromium 124-131 onto that path as well.

- Mark the declaration `/* @NoFlip */`, as in `useRangeSliderStyles.styles.ts`.
- Add `Slider.cy.tsx`, which clicks the top and bottom of a vertical Slider and
  presses ArrowUp/ArrowDown with real events, in LTR and RTL.
- Mention RTL in the change file.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jakesalvesen
Jake Salvesen (jakesalvesen) force-pushed the fix/36945-slider-vertical-appearance branch from 831c085 to 6052497 Compare October 30, 2026 22:50

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Vertical Slider still logs the deprecated 'slider-vertical' warning in Chromium 132+

4 participants