diff --git a/docs/data/base/components/slider/CustomSubcomponents.js b/docs/data/base/components/slider/CustomSubcomponents.js
new file mode 100644
index 0000000000..111e80079b
--- /dev/null
+++ b/docs/data/base/components/slider/CustomSubcomponents.js
@@ -0,0 +1,301 @@
+import * as React from 'react';
+import { useTheme } from '@mui/system';
+import * as Slider from '@base_ui/react/Slider';
+import * as Tooltip from '@base_ui/react/Tooltip';
+
+function useIsDarkMode() {
+ const theme = useTheme();
+ return theme.palette.mode === 'dark';
+}
+
+export default function CustomSubcomponents() {
+ const [valueLabelOpen, setValueLabelOpen] = React.useState(false);
+
+ const handleGlobalPointerUp = () => {
+ if (valueLabelOpen) {
+ setValueLabelOpen(false);
+ }
+ };
+
+ return (
+
+
+
+
+ {Array.from(Array(10), (_, x) => x).map((v) => {
+ return ;
+ })}
+
+ {
+ if (!valueLabelOpen) {
+ setValueLabelOpen(true);
+ }
+ }}
+ onBlur={() => {
+ if (valueLabelOpen) {
+ setValueLabelOpen(false);
+ }
+ }}
+ onPointerOver={() => {
+ if (!valueLabelOpen) {
+ setValueLabelOpen(true);
+ }
+ }}
+ onPointerLeave={(event) => {
+ if (event.buttons !== 1) {
+ setValueLabelOpen(false);
+ } else {
+ document.addEventListener('pointerup', handleGlobalPointerUp, {
+ once: true,
+ });
+ }
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const SliderMark = React.forwardRef(function SliderMark(props, ref) {
+ const { index, style, ...otherProps } = props;
+ const { percentageValues } = Slider.useSliderContext();
+ const isFilled = percentageValues[0] >= index * 10;
+ return (
+
+ );
+});
+
+const SliderTrackFill = React.forwardRef(function SliderTrackFill(props, ref) {
+ const { style, ...otherProps } = props;
+
+ const { axis, disabled, isRtl, orientation, percentageValues } =
+ Slider.useSliderContext();
+
+ const isRange = percentageValues.length > 1;
+
+ let internalStyles;
+
+ if (isRange) {
+ const trackOffset = percentageValues[0];
+ const trackLeap = percentageValues[percentageValues.length - 1] - trackOffset;
+ internalStyles = getRangeStyles(axis, trackOffset, trackLeap);
+ } else if (orientation === 'vertical') {
+ internalStyles = {
+ bottom: 0,
+ height: `${percentageValues[0]}%`,
+ };
+ } else {
+ internalStyles = {
+ width: `${percentageValues[0]}%`,
+ [isRtl ? 'right' : 'left']: 0,
+ };
+ }
+
+ return (
+
+ );
+});
+
+function getRangeStyles(axis, trackOffset, trackLeap) {
+ const axisProps = {
+ horizontal: {
+ offset: (percent) => ({ left: `${percent}%` }),
+ leap: (percent) => ({ width: `${percent}%` }),
+ },
+ 'horizontal-reverse': {
+ offset: (percent) => ({ right: `${percent}%` }),
+ leap: (percent) => ({ width: `${percent}%` }),
+ },
+ vertical: {
+ offset: (percent) => ({ bottom: `${percent}%` }),
+ leap: (percent) => ({ height: `${percent}%` }),
+ },
+ };
+
+ return {
+ ...axisProps[axis].offset(trackOffset),
+ ...axisProps[axis].leap(trackLeap),
+ };
+}
+
+function Styles() {
+ // Replace this with your app logic for determining dark mode
+ const isDarkMode = useIsDarkMode();
+ return (
+
+ );
+}
diff --git a/docs/data/base/components/slider/CustomSubcomponents.tsx b/docs/data/base/components/slider/CustomSubcomponents.tsx
new file mode 100644
index 0000000000..4e576425c9
--- /dev/null
+++ b/docs/data/base/components/slider/CustomSubcomponents.tsx
@@ -0,0 +1,314 @@
+import * as React from 'react';
+import { useTheme } from '@mui/system';
+import * as Slider from '@base_ui/react/Slider';
+import * as Tooltip from '@base_ui/react/Tooltip';
+import { type Axis } from '@base_ui/react/Slider';
+
+function useIsDarkMode() {
+ const theme = useTheme();
+ return theme.palette.mode === 'dark';
+}
+
+export default function CustomSubcomponents() {
+ const [valueLabelOpen, setValueLabelOpen] = React.useState(false);
+
+ const handleGlobalPointerUp = () => {
+ if (valueLabelOpen) {
+ setValueLabelOpen(false);
+ }
+ };
+
+ return (
+
+
+
+
+ {Array.from(Array(10), (_, x) => x).map((v) => {
+ return ;
+ })}
+
+ {
+ if (!valueLabelOpen) {
+ setValueLabelOpen(true);
+ }
+ }}
+ onBlur={() => {
+ if (valueLabelOpen) {
+ setValueLabelOpen(false);
+ }
+ }}
+ onPointerOver={() => {
+ if (!valueLabelOpen) {
+ setValueLabelOpen(true);
+ }
+ }}
+ onPointerLeave={(event) => {
+ if (event.buttons !== 1) {
+ setValueLabelOpen(false);
+ } else {
+ document.addEventListener('pointerup', handleGlobalPointerUp, {
+ once: true,
+ });
+ }
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const SliderMark = React.forwardRef(function SliderMark(
+ props: any,
+ ref: React.ForwardedRef,
+) {
+ const { index, style, ...otherProps } = props;
+ const { percentageValues } = Slider.useSliderContext();
+ const isFilled = percentageValues[0] >= index * 10;
+ return (
+
+ );
+});
+
+const SliderTrackFill = React.forwardRef(function SliderTrackFill(
+ props: any,
+ ref: React.ForwardedRef,
+) {
+ const { style, ...otherProps } = props;
+
+ const { axis, disabled, isRtl, orientation, percentageValues } =
+ Slider.useSliderContext();
+
+ const isRange = percentageValues.length > 1;
+
+ let internalStyles;
+
+ if (isRange) {
+ const trackOffset = percentageValues[0];
+ const trackLeap = percentageValues[percentageValues.length - 1] - trackOffset;
+ internalStyles = getRangeStyles(axis, trackOffset, trackLeap);
+ } else if (orientation === 'vertical') {
+ internalStyles = {
+ bottom: 0,
+ height: `${percentageValues[0]}%`,
+ };
+ } else {
+ internalStyles = {
+ width: `${percentageValues[0]}%`,
+ [isRtl ? 'right' : 'left']: 0,
+ };
+ }
+
+ return (
+
+ );
+});
+
+function getRangeStyles(axis: Axis, trackOffset: number, trackLeap: number) {
+ const axisProps: Record<
+ Axis,
+ {
+ offset: (percent: number) => Record;
+ leap: (percent: number) => Record;
+ }
+ > = {
+ horizontal: {
+ offset: (percent: number) => ({ left: `${percent}%` }),
+ leap: (percent: number) => ({ width: `${percent}%` }),
+ },
+ 'horizontal-reverse': {
+ offset: (percent: number) => ({ right: `${percent}%` }),
+ leap: (percent: number) => ({ width: `${percent}%` }),
+ },
+ vertical: {
+ offset: (percent: number) => ({ bottom: `${percent}%` }),
+ leap: (percent: number) => ({ height: `${percent}%` }),
+ },
+ };
+
+ return {
+ ...axisProps[axis].offset(trackOffset),
+ ...axisProps[axis].leap(trackLeap),
+ };
+}
+
+function Styles() {
+ // Replace this with your app logic for determining dark mode
+ const isDarkMode = useIsDarkMode();
+ return (
+
+ );
+}
diff --git a/docs/data/base/components/slider/DiscreteSlider.js b/docs/data/base/components/slider/DiscreteSlider.js
deleted file mode 100644
index 77113b3adc..0000000000
--- a/docs/data/base/components/slider/DiscreteSlider.js
+++ /dev/null
@@ -1,163 +0,0 @@
-import * as React from 'react';
-import PropTypes from 'prop-types';
-import { styled, alpha, Box } from '@mui/system';
-import { Slider as BaseSlider, sliderClasses } from '@base_ui/react/v5_Slider';
-
-export default function DiscreteSlider() {
- return (
-
-
-
- );
-}
-
-function SliderValueLabel({ children }) {
- return {children};
-}
-
-SliderValueLabel.propTypes = {
- children: PropTypes.element.isRequired,
-};
-
-function valuetext(value) {
- return `${value}°C`;
-}
-
-const blue = {
- 100: '#DAECFF',
- 200: '#99CCF3',
- 400: '#3399FF',
- 300: '#66B2FF',
- 500: '#007FFF',
- 600: '#0072E5',
- 700: '#0059B3',
- 900: '#003A75',
-};
-
-const grey = {
- 50: '#F3F6F9',
- 100: '#E5EAF2',
- 200: '#DAE2ED',
- 300: '#C7D0DD',
- 400: '#B0B8C4',
- 500: '#9DA8B7',
- 600: '#6B7A90',
- 700: '#434D5B',
- 800: '#303740',
- 900: '#1C2025',
-};
-
-const Slider = styled(BaseSlider)(
- ({ theme }) => `
- color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- height: 6px;
- width: 100%;
- padding: 16px 0;
- display: inline-flex;
- align-items: center;
- position: relative;
- cursor: pointer;
- touch-action: none;
- -webkit-tap-highlight-color: transparent;
-
- &.${sliderClasses.disabled} {
- pointer-events: none;
- cursor: default;
- color: ${theme.palette.mode === 'light' ? grey[300] : grey[600]};
- opacity: 0.4;
- }
-
- & .${sliderClasses.rail} {
- display: block;
- position: absolute;
- width: 100%;
- height: 4px;
- border-radius: 6px;
- background-color: currentColor;
- opacity: 0.3;
- }
-
- & .${sliderClasses.track} {
- display: block;
- position: absolute;
- height: 4px;
- border-radius: 6px;
- background-color: currentColor;
- }
-
- & .${sliderClasses.thumb} {
- display: flex;
- align-items: center;
- justify-content: center;
- position: absolute;
- margin-left: -6px;
- width: 20px;
- height: 20px;
- box-sizing: border-box;
- border-radius: 50%;
- outline: 0;
- background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- transition-property: box-shadow, transform;
- transition-timing-function: ease;
- transition-duration: 120ms;
- transform-origin: center;
-
- &:hover {
- box-shadow: 0 0 0 6px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[300],
- 0.3,
- )};
- }
-
- &.${sliderClasses.focusVisible} {
- box-shadow: 0 0 0 8px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[400],
- 0.5,
- )};
- outline: none;
- }
-
- &.${sliderClasses.active} {
- box-shadow: 0 0 0 8px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[400],
- 0.5,
- )};
- outline: none;
- transform: scale(1.2);
- }
- }
-
- & .${sliderClasses.mark} {
- position: absolute;
- width: 10px;
- height: 10px;
- border-radius: 99%;
- background-color: ${theme.palette.mode === 'light' ? blue[200] : blue[700]};
- transform: translateX(-50%);
- }
-
- & .${sliderClasses.markActive} {
- background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- }
-
- & .valueLabel {
- font-family: IBM Plex Sans;
- font-weight: 600;
- font-size: 12px;
- position: relative;
- top: -2em;
- text-align: center;
- align-self: center;
- }
-`,
-);
diff --git a/docs/data/base/components/slider/DiscreteSlider.tsx b/docs/data/base/components/slider/DiscreteSlider.tsx
deleted file mode 100644
index 54d3605561..0000000000
--- a/docs/data/base/components/slider/DiscreteSlider.tsx
+++ /dev/null
@@ -1,162 +0,0 @@
-import * as React from 'react';
-import { styled, alpha, Box } from '@mui/system';
-import { Slider as BaseSlider, sliderClasses } from '@base_ui/react/v5_Slider';
-
-export default function DiscreteSlider() {
- return (
-
-
-
- );
-}
-
-interface SliderValueLabelProps {
- children: React.ReactElement;
-}
-
-function SliderValueLabel({ children }: SliderValueLabelProps) {
- return {children};
-}
-
-function valuetext(value: number) {
- return `${value}°C`;
-}
-
-const blue = {
- 100: '#DAECFF',
- 200: '#99CCF3',
- 400: '#3399FF',
- 300: '#66B2FF',
- 500: '#007FFF',
- 600: '#0072E5',
- 700: '#0059B3',
- 900: '#003A75',
-};
-
-const grey = {
- 50: '#F3F6F9',
- 100: '#E5EAF2',
- 200: '#DAE2ED',
- 300: '#C7D0DD',
- 400: '#B0B8C4',
- 500: '#9DA8B7',
- 600: '#6B7A90',
- 700: '#434D5B',
- 800: '#303740',
- 900: '#1C2025',
-};
-
-const Slider = styled(BaseSlider)(
- ({ theme }) => `
- color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- height: 6px;
- width: 100%;
- padding: 16px 0;
- display: inline-flex;
- align-items: center;
- position: relative;
- cursor: pointer;
- touch-action: none;
- -webkit-tap-highlight-color: transparent;
-
- &.${sliderClasses.disabled} {
- pointer-events: none;
- cursor: default;
- color: ${theme.palette.mode === 'light' ? grey[300] : grey[600]};
- opacity: 0.4;
- }
-
- & .${sliderClasses.rail} {
- display: block;
- position: absolute;
- width: 100%;
- height: 4px;
- border-radius: 6px;
- background-color: currentColor;
- opacity: 0.3;
- }
-
- & .${sliderClasses.track} {
- display: block;
- position: absolute;
- height: 4px;
- border-radius: 6px;
- background-color: currentColor;
- }
-
- & .${sliderClasses.thumb} {
- display: flex;
- align-items: center;
- justify-content: center;
- position: absolute;
- margin-left: -6px;
- width: 20px;
- height: 20px;
- box-sizing: border-box;
- border-radius: 50%;
- outline: 0;
- background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- transition-property: box-shadow, transform;
- transition-timing-function: ease;
- transition-duration: 120ms;
- transform-origin: center;
-
- &:hover {
- box-shadow: 0 0 0 6px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[300],
- 0.3,
- )};
- }
-
- &.${sliderClasses.focusVisible} {
- box-shadow: 0 0 0 8px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[400],
- 0.5,
- )};
- outline: none;
- }
-
- &.${sliderClasses.active} {
- box-shadow: 0 0 0 8px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[400],
- 0.5,
- )};
- outline: none;
- transform: scale(1.2);
- }
- }
-
- & .${sliderClasses.mark} {
- position: absolute;
- width: 10px;
- height: 10px;
- border-radius: 99%;
- background-color: ${theme.palette.mode === 'light' ? blue[200] : blue[700]};
- transform: translateX(-50%);
- }
-
- & .${sliderClasses.markActive} {
- background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- }
-
- & .valueLabel {
- font-family: IBM Plex Sans;
- font-weight: 600;
- font-size: 12px;
- position: relative;
- top: -2em;
- text-align: center;
- align-self: center;
- }
-`,
-);
diff --git a/docs/data/base/components/slider/DiscreteSlider.tsx.preview b/docs/data/base/components/slider/DiscreteSlider.tsx.preview
deleted file mode 100644
index 1d0ad0d7b2..0000000000
--- a/docs/data/base/components/slider/DiscreteSlider.tsx.preview
+++ /dev/null
@@ -1,11 +0,0 @@
-
\ No newline at end of file
diff --git a/docs/data/base/components/slider/DiscreteSliderMarks.js b/docs/data/base/components/slider/DiscreteSliderMarks.js
deleted file mode 100644
index a1ea9df512..0000000000
--- a/docs/data/base/components/slider/DiscreteSliderMarks.js
+++ /dev/null
@@ -1,168 +0,0 @@
-import * as React from 'react';
-import { styled, alpha, Box } from '@mui/system';
-import { Slider as BaseSlider, sliderClasses } from '@base_ui/react/v5_Slider';
-
-export default function DiscreteSliderMarks() {
- return (
-
-
-
- );
-}
-
-const marks = [
- {
- value: 0,
- label: '0°C',
- },
- {
- value: 20,
- label: '20°C',
- },
- {
- value: 37,
- label: '37°C',
- },
- {
- value: 100,
- label: '100°C',
- },
-];
-
-function valuetext(value) {
- return `${value}°C`;
-}
-
-const blue = {
- 100: '#DAECFF',
- 200: '#99CCF3',
- 400: '#3399FF',
- 300: '#66B2FF',
- 500: '#007FFF',
- 600: '#0072E5',
- 700: '#0059B3',
- 900: '#003A75',
-};
-
-const grey = {
- 50: '#F3F6F9',
- 100: '#E5EAF2',
- 200: '#DAE2ED',
- 300: '#C7D0DD',
- 400: '#B0B8C4',
- 500: '#9DA8B7',
- 600: '#6B7A90',
- 700: '#434D5B',
- 800: '#303740',
- 900: '#1C2025',
-};
-
-const Slider = styled(BaseSlider)(
- ({ theme }) => `
- color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- height: 6px;
- width: 100%;
- padding: 16px 0;
- display: inline-flex;
- align-items: center;
- position: relative;
- cursor: pointer;
- touch-action: none;
- -webkit-tap-highlight-color: transparent;
-
- &.${sliderClasses.disabled} {
- pointer-events: none;
- cursor: default;
- color: ${theme.palette.mode === 'light' ? grey[300] : grey[600]};
- opacity: 0.4;
- }
-
- & .${sliderClasses.rail} {
- display: block;
- position: absolute;
- width: 100%;
- height: 4px;
- border-radius: 6px;
- background-color: currentColor;
- opacity: 0.3;
- }
-
- & .${sliderClasses.track} {
- display: block;
- position: absolute;
- height: 4px;
- border-radius: 6px;
- background-color: currentColor;
- }
-
- & .${sliderClasses.thumb} {
- display: flex;
- align-items: center;
- justify-content: center;
- position: absolute;
- margin-left: -6px;
- width: 20px;
- height: 20px;
- box-sizing: border-box;
- border-radius: 50%;
- outline: 0;
- background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- transition-property: box-shadow, transform;
- transition-timing-function: ease;
- transition-duration: 120ms;
- transform-origin: center;
-
- &:hover {
- box-shadow: 0 0 0 6px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[300],
- 0.3,
- )};
- }
-
- &.${sliderClasses.focusVisible} {
- box-shadow: 0 0 0 8px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[400],
- 0.5,
- )};
- outline: none;
- }
-
- &.${sliderClasses.active} {
- box-shadow: 0 0 0 8px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[400],
- 0.5,
- )};
- outline: none;
- transform: scale(1.2);
- }
- }
-
- & .${sliderClasses.mark} {
- position: absolute;
- width: 10px;
- height: 10px;
- border-radius: 99%;
- background-color: ${theme.palette.mode === 'light' ? blue[200] : blue[900]};
- transform: translateX(-50%);
- }
-
- & .${sliderClasses.markActive} {
- background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- }
-
- & .${sliderClasses.markLabel} {
- font-family: IBM Plex Sans;
- font-weight: 600;
- font-size: 12px;
- position: absolute;
- top: 24px;
- transform: translateX(-50%);
- margin-top: 8px;
- }
-`,
-);
diff --git a/docs/data/base/components/slider/DiscreteSliderMarks.tsx b/docs/data/base/components/slider/DiscreteSliderMarks.tsx
deleted file mode 100644
index 973e349951..0000000000
--- a/docs/data/base/components/slider/DiscreteSliderMarks.tsx
+++ /dev/null
@@ -1,168 +0,0 @@
-import * as React from 'react';
-import { styled, alpha, Box } from '@mui/system';
-import { Slider as BaseSlider, sliderClasses } from '@base_ui/react/v5_Slider';
-
-export default function DiscreteSliderMarks() {
- return (
-
-
-
- );
-}
-
-const marks = [
- {
- value: 0,
- label: '0°C',
- },
- {
- value: 20,
- label: '20°C',
- },
- {
- value: 37,
- label: '37°C',
- },
- {
- value: 100,
- label: '100°C',
- },
-];
-
-function valuetext(value: number) {
- return `${value}°C`;
-}
-
-const blue = {
- 100: '#DAECFF',
- 200: '#99CCF3',
- 400: '#3399FF',
- 300: '#66B2FF',
- 500: '#007FFF',
- 600: '#0072E5',
- 700: '#0059B3',
- 900: '#003A75',
-};
-
-const grey = {
- 50: '#F3F6F9',
- 100: '#E5EAF2',
- 200: '#DAE2ED',
- 300: '#C7D0DD',
- 400: '#B0B8C4',
- 500: '#9DA8B7',
- 600: '#6B7A90',
- 700: '#434D5B',
- 800: '#303740',
- 900: '#1C2025',
-};
-
-const Slider = styled(BaseSlider)(
- ({ theme }) => `
- color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- height: 6px;
- width: 100%;
- padding: 16px 0;
- display: inline-flex;
- align-items: center;
- position: relative;
- cursor: pointer;
- touch-action: none;
- -webkit-tap-highlight-color: transparent;
-
- &.${sliderClasses.disabled} {
- pointer-events: none;
- cursor: default;
- color: ${theme.palette.mode === 'light' ? grey[300] : grey[600]};
- opacity: 0.4;
- }
-
- & .${sliderClasses.rail} {
- display: block;
- position: absolute;
- width: 100%;
- height: 4px;
- border-radius: 6px;
- background-color: currentColor;
- opacity: 0.3;
- }
-
- & .${sliderClasses.track} {
- display: block;
- position: absolute;
- height: 4px;
- border-radius: 6px;
- background-color: currentColor;
- }
-
- & .${sliderClasses.thumb} {
- display: flex;
- align-items: center;
- justify-content: center;
- position: absolute;
- margin-left: -6px;
- width: 20px;
- height: 20px;
- box-sizing: border-box;
- border-radius: 50%;
- outline: 0;
- background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- transition-property: box-shadow, transform;
- transition-timing-function: ease;
- transition-duration: 120ms;
- transform-origin: center;
-
- &:hover {
- box-shadow: 0 0 0 6px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[300],
- 0.3,
- )};
- }
-
- &.${sliderClasses.focusVisible} {
- box-shadow: 0 0 0 8px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[400],
- 0.5,
- )};
- outline: none;
- }
-
- &.${sliderClasses.active} {
- box-shadow: 0 0 0 8px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[400],
- 0.5,
- )};
- outline: none;
- transform: scale(1.2);
- }
- }
-
- & .${sliderClasses.mark} {
- position: absolute;
- width: 10px;
- height: 10px;
- border-radius: 99%;
- background-color: ${theme.palette.mode === 'light' ? blue[200] : blue[900]};
- transform: translateX(-50%);
- }
-
- & .${sliderClasses.markActive} {
- background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- }
-
- & .${sliderClasses.markLabel} {
- font-family: IBM Plex Sans;
- font-weight: 600;
- font-size: 12px;
- position: absolute;
- top: 24px;
- transform: translateX(-50%);
- margin-top: 8px;
- }
-`,
-);
diff --git a/docs/data/base/components/slider/DiscreteSliderMarks.tsx.preview b/docs/data/base/components/slider/DiscreteSliderMarks.tsx.preview
deleted file mode 100644
index 32bef2b878..0000000000
--- a/docs/data/base/components/slider/DiscreteSliderMarks.tsx.preview
+++ /dev/null
@@ -1,6 +0,0 @@
-
\ No newline at end of file
diff --git a/docs/data/base/components/slider/DiscreteSliderValues.js b/docs/data/base/components/slider/DiscreteSliderValues.js
deleted file mode 100644
index ac0cb96228..0000000000
--- a/docs/data/base/components/slider/DiscreteSliderValues.js
+++ /dev/null
@@ -1,169 +0,0 @@
-import * as React from 'react';
-import { styled, alpha, Box } from '@mui/system';
-import { Slider as BaseSlider, sliderClasses } from '@base_ui/react/v5_Slider';
-
-export default function DiscreteSliderValues() {
- return (
-
-
-
- );
-}
-
-const marks = [
- {
- value: 0,
- label: '0°C',
- },
- {
- value: 20,
- label: '20°C',
- },
- {
- value: 37,
- label: '37°C',
- },
- {
- value: 100,
- label: '100°C',
- },
-];
-
-function valuetext(value) {
- return `${value}°C`;
-}
-
-const blue = {
- 100: '#DAECFF',
- 200: '#99CCF3',
- 400: '#3399FF',
- 300: '#66B2FF',
- 500: '#007FFF',
- 600: '#0072E5',
- 700: '#0059B3',
- 900: '#003A75',
-};
-
-const grey = {
- 50: '#F3F6F9',
- 100: '#E5EAF2',
- 200: '#DAE2ED',
- 300: '#C7D0DD',
- 400: '#B0B8C4',
- 500: '#9DA8B7',
- 600: '#6B7A90',
- 700: '#434D5B',
- 800: '#303740',
- 900: '#1C2025',
-};
-
-const Slider = styled(BaseSlider)(
- ({ theme }) => `
- color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- height: 6px;
- width: 100%;
- padding: 16px 0;
- display: inline-flex;
- align-items: center;
- position: relative;
- cursor: pointer;
- touch-action: none;
- -webkit-tap-highlight-color: transparent;
-
- &.${sliderClasses.disabled} {
- pointer-events: none;
- cursor: default;
- color: ${theme.palette.mode === 'light' ? grey[300] : grey[600]};
- opacity: 0.4;
- }
-
- & .${sliderClasses.rail} {
- display: block;
- position: absolute;
- width: 100%;
- height: 4px;
- border-radius: 6px;
- background-color: currentColor;
- opacity: 0.3;
- }
-
- & .${sliderClasses.track} {
- display: block;
- position: absolute;
- height: 4px;
- border-radius: 6px;
- background-color: currentColor;
- }
-
- & .${sliderClasses.thumb} {
- display: flex;
- align-items: center;
- justify-content: center;
- position: absolute;
- margin-left: -6px;
- width: 20px;
- height: 20px;
- box-sizing: border-box;
- border-radius: 50%;
- outline: 0;
- background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- transition-property: box-shadow, transform;
- transition-timing-function: ease;
- transition-duration: 120ms;
- transform-origin: center;
-
- &:hover {
- box-shadow: 0 0 0 6px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[300],
- 0.3,
- )};
- }
-
- &.${sliderClasses.focusVisible} {
- box-shadow: 0 0 0 8px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[400],
- 0.5,
- )};
- outline: none;
- }
-
- &.${sliderClasses.active} {
- box-shadow: 0 0 0 8px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[400],
- 0.5,
- )};
- outline: none;
- transform: scale(1.2);
- }
- }
-
- & .${sliderClasses.mark} {
- position: absolute;
- width: 10px;
- height: 10px;
- border-radius: 99%;
- background-color: ${theme.palette.mode === 'light' ? blue[200] : blue[900]};
- transform: translateX(-50%);
- }
-
- & .${sliderClasses.markActive} {
- background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- }
-
- & .${sliderClasses.markLabel} {
- font-family: IBM Plex Sans;
- font-weight: 600;
- font-size: 12px;
- position: absolute;
- top: 24px;
- transform: translateX(-50%);
- margin-top: 8px;
- }
-`,
-);
diff --git a/docs/data/base/components/slider/DiscreteSliderValues.tsx b/docs/data/base/components/slider/DiscreteSliderValues.tsx
deleted file mode 100644
index 3da423aaee..0000000000
--- a/docs/data/base/components/slider/DiscreteSliderValues.tsx
+++ /dev/null
@@ -1,169 +0,0 @@
-import * as React from 'react';
-import { styled, alpha, Box } from '@mui/system';
-import { Slider as BaseSlider, sliderClasses } from '@base_ui/react/v5_Slider';
-
-export default function DiscreteSliderValues() {
- return (
-
-
-
- );
-}
-
-const marks = [
- {
- value: 0,
- label: '0°C',
- },
- {
- value: 20,
- label: '20°C',
- },
- {
- value: 37,
- label: '37°C',
- },
- {
- value: 100,
- label: '100°C',
- },
-];
-
-function valuetext(value: number) {
- return `${value}°C`;
-}
-
-const blue = {
- 100: '#DAECFF',
- 200: '#99CCF3',
- 400: '#3399FF',
- 300: '#66B2FF',
- 500: '#007FFF',
- 600: '#0072E5',
- 700: '#0059B3',
- 900: '#003A75',
-};
-
-const grey = {
- 50: '#F3F6F9',
- 100: '#E5EAF2',
- 200: '#DAE2ED',
- 300: '#C7D0DD',
- 400: '#B0B8C4',
- 500: '#9DA8B7',
- 600: '#6B7A90',
- 700: '#434D5B',
- 800: '#303740',
- 900: '#1C2025',
-};
-
-const Slider = styled(BaseSlider)(
- ({ theme }) => `
- color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- height: 6px;
- width: 100%;
- padding: 16px 0;
- display: inline-flex;
- align-items: center;
- position: relative;
- cursor: pointer;
- touch-action: none;
- -webkit-tap-highlight-color: transparent;
-
- &.${sliderClasses.disabled} {
- pointer-events: none;
- cursor: default;
- color: ${theme.palette.mode === 'light' ? grey[300] : grey[600]};
- opacity: 0.4;
- }
-
- & .${sliderClasses.rail} {
- display: block;
- position: absolute;
- width: 100%;
- height: 4px;
- border-radius: 6px;
- background-color: currentColor;
- opacity: 0.3;
- }
-
- & .${sliderClasses.track} {
- display: block;
- position: absolute;
- height: 4px;
- border-radius: 6px;
- background-color: currentColor;
- }
-
- & .${sliderClasses.thumb} {
- display: flex;
- align-items: center;
- justify-content: center;
- position: absolute;
- margin-left: -6px;
- width: 20px;
- height: 20px;
- box-sizing: border-box;
- border-radius: 50%;
- outline: 0;
- background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- transition-property: box-shadow, transform;
- transition-timing-function: ease;
- transition-duration: 120ms;
- transform-origin: center;
-
- &:hover {
- box-shadow: 0 0 0 6px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[300],
- 0.3,
- )};
- }
-
- &.${sliderClasses.focusVisible} {
- box-shadow: 0 0 0 8px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[400],
- 0.5,
- )};
- outline: none;
- }
-
- &.${sliderClasses.active} {
- box-shadow: 0 0 0 8px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[400],
- 0.5,
- )};
- outline: none;
- transform: scale(1.2);
- }
- }
-
- & .${sliderClasses.mark} {
- position: absolute;
- width: 10px;
- height: 10px;
- border-radius: 99%;
- background-color: ${theme.palette.mode === 'light' ? blue[200] : blue[900]};
- transform: translateX(-50%);
- }
-
- & .${sliderClasses.markActive} {
- background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- }
-
- & .${sliderClasses.markLabel} {
- font-family: IBM Plex Sans;
- font-weight: 600;
- font-size: 12px;
- position: absolute;
- top: 24px;
- transform: translateX(-50%);
- margin-top: 8px;
- }
-`,
-);
diff --git a/docs/data/base/components/slider/DiscreteSliderValues.tsx.preview b/docs/data/base/components/slider/DiscreteSliderValues.tsx.preview
deleted file mode 100644
index f5e963865b..0000000000
--- a/docs/data/base/components/slider/DiscreteSliderValues.tsx.preview
+++ /dev/null
@@ -1,7 +0,0 @@
-
\ No newline at end of file
diff --git a/docs/data/base/components/slider/LabeledValuesSlider.js b/docs/data/base/components/slider/LabeledValuesSlider.js
deleted file mode 100644
index 17c2fd45ff..0000000000
--- a/docs/data/base/components/slider/LabeledValuesSlider.js
+++ /dev/null
@@ -1,161 +0,0 @@
-import * as React from 'react';
-import PropTypes from 'prop-types';
-import { styled, alpha, Box } from '@mui/system';
-import { Slider as BaseSlider, sliderClasses } from '@base_ui/react/v5_Slider';
-
-export default function LabeledValuesSlider() {
- return (
-
-
-
- );
-}
-
-function SliderValueLabel({ children }) {
- return (
-
- {children}
-
- );
-}
-
-SliderValueLabel.propTypes = {
- children: PropTypes.element.isRequired,
-};
-
-const blue = {
- 100: '#DAECFF',
- 200: '#99CCF3',
- 400: '#3399FF',
- 300: '#66B2FF',
- 500: '#007FFF',
- 600: '#0072E5',
- 700: '#0059B3',
- 900: '#003A75',
-};
-
-const grey = {
- 50: '#F3F6F9',
- 100: '#E5EAF2',
- 200: '#DAE2ED',
- 300: '#C7D0DD',
- 400: '#B0B8C4',
- 500: '#9DA8B7',
- 600: '#6B7A90',
- 700: '#434D5B',
- 800: '#303740',
- 900: '#1C2025',
-};
-
-const Slider = styled(BaseSlider)(
- ({ theme }) => `
- color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- height: 6px;
- width: 100%;
- padding: 16px 0;
- display: inline-flex;
- align-items: center;
- position: relative;
- cursor: pointer;
- touch-action: none;
- -webkit-tap-highlight-color: transparent;
-
- &.${sliderClasses.disabled} {
- pointer-events: none;
- cursor: default;
- color: ${theme.palette.mode === 'light' ? grey[300] : grey[600]};
- opacity: 0.4;
- }
-
- & .${sliderClasses.rail} {
- display: block;
- position: absolute;
- width: 100%;
- height: 4px;
- border-radius: 6px;
- background-color: currentColor;
- opacity: 0.3;
- }
-
- & .${sliderClasses.track} {
- display: block;
- position: absolute;
- height: 4px;
- border-radius: 6px;
- background-color: currentColor;
- }
-
- & .${sliderClasses.thumb} {
- display: flex;
- align-items: center;
- justify-content: center;
- position: absolute;
- margin-left: -6px;
- width: 20px;
- height: 20px;
- box-sizing: border-box;
- border-radius: 50%;
- outline: 0;
- background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- transition-property: box-shadow, transform;
- transition-timing-function: ease;
- transition-duration: 120ms;
- transform-origin: center;
-
- &:hover {
- box-shadow: 0 0 0 6px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[300],
- 0.3,
- )};
- }
-
- &.${sliderClasses.focusVisible} {
- box-shadow: 0 0 0 8px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[400],
- 0.5,
- )};
- outline: none;
- }
-
- &.${sliderClasses.active} {
- box-shadow: 0 0 0 8px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[400],
- 0.5,
- )};
- outline: none;
- transform: scale(1.2);
- }
- }
-
- & .label {
- font-family: IBM Plex Sans;
- font-weight: 600;
- font-size: 14px;
- background: unset;
- background-color: ${theme.palette.mode === 'light' ? blue[600] : blue[900]};
- width: 32px;
- height: 32px;
- padding: 0px;
- visibility: hidden;
- color: #fff;
- border-radius: 50% 50% 50% 0;
- position: absolute;
- transform: translate(0%, -140%) rotate(-45deg) scale(0);
- transition: transform 0.2s ease;
- display: flex;
- align-items: center;
- justify-content: center;
- }
-
- :hover .label {
- visibility: visible;
- transform: translate(0%, -140%) rotate(-45deg) scale(1);
- }
-
- :hover .value {
- transform: rotate(45deg);
- text-align: center;
- }
- }
-`,
-);
diff --git a/docs/data/base/components/slider/LabeledValuesSlider.tsx b/docs/data/base/components/slider/LabeledValuesSlider.tsx
deleted file mode 100644
index fe5a7462a8..0000000000
--- a/docs/data/base/components/slider/LabeledValuesSlider.tsx
+++ /dev/null
@@ -1,160 +0,0 @@
-import * as React from 'react';
-import { styled, alpha, Box } from '@mui/system';
-import { Slider as BaseSlider, sliderClasses } from '@base_ui/react/v5_Slider';
-
-export default function LabeledValuesSlider() {
- return (
-
-
-
- );
-}
-
-interface SliderValueLabelProps {
- children: React.ReactElement;
-}
-
-function SliderValueLabel({ children }: SliderValueLabelProps) {
- return (
-
- {children}
-
- );
-}
-
-const blue = {
- 100: '#DAECFF',
- 200: '#99CCF3',
- 400: '#3399FF',
- 300: '#66B2FF',
- 500: '#007FFF',
- 600: '#0072E5',
- 700: '#0059B3',
- 900: '#003A75',
-};
-
-const grey = {
- 50: '#F3F6F9',
- 100: '#E5EAF2',
- 200: '#DAE2ED',
- 300: '#C7D0DD',
- 400: '#B0B8C4',
- 500: '#9DA8B7',
- 600: '#6B7A90',
- 700: '#434D5B',
- 800: '#303740',
- 900: '#1C2025',
-};
-
-const Slider = styled(BaseSlider)(
- ({ theme }) => `
- color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- height: 6px;
- width: 100%;
- padding: 16px 0;
- display: inline-flex;
- align-items: center;
- position: relative;
- cursor: pointer;
- touch-action: none;
- -webkit-tap-highlight-color: transparent;
-
- &.${sliderClasses.disabled} {
- pointer-events: none;
- cursor: default;
- color: ${theme.palette.mode === 'light' ? grey[300] : grey[600]};
- opacity: 0.4;
- }
-
- & .${sliderClasses.rail} {
- display: block;
- position: absolute;
- width: 100%;
- height: 4px;
- border-radius: 6px;
- background-color: currentColor;
- opacity: 0.3;
- }
-
- & .${sliderClasses.track} {
- display: block;
- position: absolute;
- height: 4px;
- border-radius: 6px;
- background-color: currentColor;
- }
-
- & .${sliderClasses.thumb} {
- display: flex;
- align-items: center;
- justify-content: center;
- position: absolute;
- margin-left: -6px;
- width: 20px;
- height: 20px;
- box-sizing: border-box;
- border-radius: 50%;
- outline: 0;
- background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- transition-property: box-shadow, transform;
- transition-timing-function: ease;
- transition-duration: 120ms;
- transform-origin: center;
-
- &:hover {
- box-shadow: 0 0 0 6px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[300],
- 0.3,
- )};
- }
-
- &.${sliderClasses.focusVisible} {
- box-shadow: 0 0 0 8px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[400],
- 0.5,
- )};
- outline: none;
- }
-
- &.${sliderClasses.active} {
- box-shadow: 0 0 0 8px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[400],
- 0.5,
- )};
- outline: none;
- transform: scale(1.2);
- }
- }
-
- & .label {
- font-family: IBM Plex Sans;
- font-weight: 600;
- font-size: 14px;
- background: unset;
- background-color: ${theme.palette.mode === 'light' ? blue[600] : blue[900]};
- width: 32px;
- height: 32px;
- padding: 0px;
- visibility: hidden;
- color: #fff;
- border-radius: 50% 50% 50% 0;
- position: absolute;
- transform: translate(0%, -140%) rotate(-45deg) scale(0);
- transition: transform 0.2s ease;
- display: flex;
- align-items: center;
- justify-content: center;
- }
-
- :hover .label {
- visibility: visible;
- transform: translate(0%, -140%) rotate(-45deg) scale(1);
- }
-
- :hover .value {
- transform: rotate(45deg);
- text-align: center;
- }
- }
-`,
-);
diff --git a/docs/data/base/components/slider/LabeledValuesSlider.tsx.preview b/docs/data/base/components/slider/LabeledValuesSlider.tsx.preview
deleted file mode 100644
index ab77eaafc1..0000000000
--- a/docs/data/base/components/slider/LabeledValuesSlider.tsx.preview
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/docs/data/base/components/slider/RangeSlider.js b/docs/data/base/components/slider/RangeSlider.js
index dc8b7d413e..2fcc7926fe 100644
--- a/docs/data/base/components/slider/RangeSlider.js
+++ b/docs/data/base/components/slider/RangeSlider.js
@@ -1,52 +1,43 @@
import * as React from 'react';
-import { styled, alpha, Box } from '@mui/system';
-import { Slider as BaseSlider, sliderClasses } from '@base_ui/react/v5_Slider';
+import { styled, useTheme, Box } from '@mui/system';
+import * as BaseSlider from '@base_ui/react/Slider';
export default function RangeSlider() {
+ // Replace this with your app logic for determining dark mode
+ const isDarkMode = useIsDarkMode();
const [value, setValue] = React.useState([20, 37]);
- const handleChange = (event, newValue) => {
- setValue(newValue);
- };
-
return (
-
+
{/* controlled: */}
'Temperature range'}
- getAriaValueText={valuetext}
- min={0}
- max={100}
- />
+ onValueChange={setValue}
+ aria-labelledby="ControlledRangeLabel"
+ >
+
+
+
+
+
+
+
{/* uncontrolled: */}
- 'Temperature range'}
- getAriaValueText={valuetext}
- min={0}
- max={100}
- />
+
+
+
+
+
+
+
+
);
}
-function valuetext(value) {
- return `${value}°C`;
-}
-
-const blue = {
- 100: '#DAECFF',
- 200: '#99CCF3',
- 400: '#3399FF',
- 300: '#66B2FF',
- 500: '#007FFF',
- 600: '#0072E5',
- 700: '#0059B3',
- 900: '#003A75',
-};
-
const grey = {
50: '#F3F6F9',
100: '#E5EAF2',
@@ -60,98 +51,102 @@ const grey = {
900: '#1C2025',
};
-const Slider = styled(BaseSlider)(
- ({ theme }) => `
- color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- height: 6px;
+function useIsDarkMode() {
+ const theme = useTheme();
+ return theme.palette.mode === 'dark';
+}
+
+const Slider = styled(BaseSlider.Root)`
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 1rem;
width: 100%;
- padding: 16px 0;
- display: inline-flex;
align-items: center;
position: relative;
- cursor: pointer;
- touch-action: none;
-webkit-tap-highlight-color: transparent;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ grid-gap: 1rem;
+`;
+
+const SliderOutput = styled(BaseSlider.Output)`
+ text-align: right;
+`;
+
+const SliderTrack = styled(BaseSlider.Track)`
+ grid-column: 1/3;
+ display: flex;
+ align-items: center;
+ position: relative;
+ width: 100%;
+ height: 16px;
+ border-radius: 9999px;
+ touch-action: none;
- &.${sliderClasses.disabled} {
- pointer-events: none;
- cursor: default;
- color: ${theme.palette.mode === 'light' ? grey[300] : grey[600]};
- opacity: 0.4;
+ &::before {
+ content: '';
+ width: 100%;
+ height: 2px;
+ border-radius: 9999px;
+ background-color: ${grey[400]};
+ touch-action: none;
}
- & .${sliderClasses.rail} {
- display: block;
- position: absolute;
- width: 100%;
- height: 4px;
- border-radius: 6px;
- background-color: currentColor;
- opacity: 0.3;
+ &[data-disabled] {
+ cursor: not-allowed;
+ }
+
+ .dark &::before {
+ background-color: ${grey[700]};
+ }
+`;
+
+const SliderThumb = styled(BaseSlider.Thumb)`
+ position: absolute;
+ width: 16px;
+ height: 16px;
+ box-sizing: border-box;
+ border-radius: 50%;
+ background-color: black;
+ transform: translateX(-50%);
+ touch-action: none;
+
+ &:focus-visible {
+ outline: 2px solid black;
+ outline-offset: 2px;
+ }
+
+ .dark & {
+ background-color: ${grey[300]};
}
- & .${sliderClasses.track} {
- display: block;
- position: absolute;
- height: 4px;
- border-radius: 6px;
- background-color: currentColor;
+ .dark &:focus-visible {
+ outline-color: ${grey[300]};
+ outline-width: 1px;
+ outline-offset: 3px;
}
- & .${sliderClasses.thumb} {
- display: flex;
- align-items: center;
- justify-content: center;
- position: absolute;
- margin-left: -6px;
- width: 20px;
- height: 20px;
- box-sizing: border-box;
- border-radius: 50%;
- outline: 0;
- background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
- transition-property: box-shadow, transform;
- transition-timing-function: ease;
- transition-duration: 120ms;
- transform-origin: center;
-
- &:hover {
- box-shadow: 0 0 0 6px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[300],
- 0.3,
- )};
- }
-
- &.${sliderClasses.focusVisible} {
- box-shadow: 0 0 0 8px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[400],
- 0.5,
- )};
- outline: none;
- }
-
- &.${sliderClasses.active} {
- box-shadow: 0 0 0 8px ${alpha(
- theme.palette.mode === 'light' ? blue[200] : blue[400],
- 0.5,
- )};
- outline: none;
- transform: scale(1.2);
- }
+ &[data-dragging='true'] {
+ background-color: pink;
}
- & .${sliderClasses.mark} {
- position: absolute;
- width: 10px;
- height: 10px;
- border-radius: 99%;
- background-color: ${theme.palette.mode === 'light' ? blue[200] : blue[900]};
- top: 44%;
- transform: translateX(-50%);
+ &[data-disabled],
+ .dark &[data-disabled] {
+ background-color: ${grey[600]};
}
- & .${sliderClasses.markActive} {
- background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]};
+ .dark &[data-dragging='true'] {
+ background-color: pink;
+ }
+`;
+
+// we can't use a