;
+
+Button.propTypes /* remove-proptypes */ = {
+ // ----------------------------- Warning --------------------------------
+ // | These PropTypes are generated from the TypeScript type definitions |
+ // | To update them edit TypeScript types and run "yarn proptypes" |
+ // ----------------------------------------------------------------------
+ /**
+ * A ref for imperative actions.
+ * It currently only supports `focusVisible()` action.
+ */
+ action: refType,
+ /**
+ * The content of the component.
+ */
+ children: PropTypes.node,
+ /**
+ * @ignore
+ */
+ className: PropTypes.string,
+ /**
+ * The color of the component.
+ * It supports both default and custom theme colors, which can be added as shown in the
+ * [palette customization guide](https://mui.com/material-ui/customization/palette/#adding-new-colors).
+ * @default 'primary'
+ */
+ color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([
+ PropTypes.oneOf(['primary', 'secondary', 'tertiary']),
+ PropTypes.string,
+ ]),
+ /**
+ * The component used for the root node.
+ * Either a string to use a HTML element or a component.
+ */
+ component: elementTypeAcceptingRef,
+ /**
+ * If `true`, the component is disabled.
+ * @default false
+ */
+ disabled: PropTypes.bool,
+ /**
+ * If `true`, no elevation is used.
+ * @default false
+ */
+ disableElevation: PropTypes.bool,
+ /**
+ * Element placed after the children.
+ */
+ endIcon: PropTypes.node,
+ /**
+ * @ignore
+ */
+ focusVisibleClassName: PropTypes.string,
+ /**
+ * If `true`, the button will take up the full width of its container.
+ * @default false
+ */
+ fullWidth: PropTypes.bool,
+ /**
+ * The URL to link to when the button is clicked.
+ * If defined, an `a` element will be used as the root node.
+ */
+ href: PropTypes.string,
+ /**
+ * The component used to render a link when the `href` prop is provided.
+ * @default 'a'
+ */
+ LinkComponent: PropTypes.elementType,
+ /**
+ * @ignore
+ */
+ onBlur: PropTypes.func,
+ /**
+ * @ignore
+ */
+ onClick: PropTypes.func,
+ /**
+ * @ignore
+ */
+ onContextMenu: PropTypes.func,
+ /**
+ * @ignore
+ */
+ onDragLeave: PropTypes.func,
+ /**
+ * @ignore
+ */
+ onFocus: PropTypes.func,
+ /**
+ * Callback fired when the component is focused with a keyboard.
+ * We trigger a `onFocus` callback too.
+ */
+ onFocusVisible: PropTypes.func,
+ /**
+ * @ignore
+ */
+ onKeyDown: PropTypes.func,
+ /**
+ * @ignore
+ */
+ onKeyUp: PropTypes.func,
+ /**
+ * @ignore
+ */
+ onMouseDown: PropTypes.func,
+ /**
+ * @ignore
+ */
+ onMouseLeave: PropTypes.func,
+ /**
+ * @ignore
+ */
+ onMouseUp: PropTypes.func,
+ /**
+ * @ignore
+ */
+ onTouchEnd: PropTypes.func,
+ /**
+ * @ignore
+ */
+ onTouchMove: PropTypes.func,
+ /**
+ * @ignore
+ */
+ onTouchStart: PropTypes.func,
+ /**
+ * The size of the component.
+ * `small` is equivalent to the dense button styling.
+ * @default 'medium'
+ */
+ size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([
+ PropTypes.oneOf(['small', 'medium', 'large']),
+ PropTypes.string,
+ ]),
+ /**
+ * Element placed before the children.
+ */
+ startIcon: PropTypes.node,
+ /**
+ * @default 0
+ */
+ tabIndex: PropTypes.number,
+ /**
+ * @ignore
+ */
+ type: PropTypes.oneOfType([PropTypes.oneOf(['button', 'reset', 'submit']), PropTypes.string]),
+ /**
+ * The variant to use.
+ * @default 'text'
+ */
+ variant: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([
+ PropTypes.oneOf(['text', 'outlined', 'filled', 'filledTonal', 'elevated']),
+ PropTypes.string,
+ ]),
+} as any;
+
+export default Button;
diff --git a/packages/mui-material-next/src/Button/Button.d.ts b/packages/mui-material-next/src/Button/Button.types.ts
similarity index 65%
rename from packages/mui-material-next/src/Button/Button.d.ts
rename to packages/mui-material-next/src/Button/Button.types.ts
index 525e4586cd40b0..c24fe6c77d9262 100644
--- a/packages/mui-material-next/src/Button/Button.d.ts
+++ b/packages/mui-material-next/src/Button/Button.types.ts
@@ -1,14 +1,11 @@
import * as React from 'react';
import {
- DistributiveOmit,
- OverridableComponent,
OverridableStringUnion,
- OverridableTypeMap,
OverrideProps,
+ OverridableComponent,
+ OverridableTypeMap,
} from '@mui/types';
-import { SxProps } from '@mui/system';
-import { Theme } from '@mui/material';
-import { TouchRippleProps } from './TouchRipple';
+import { SxProps } from '../styles/Theme.types';
import { ButtonClasses } from './buttonClasses';
export interface ButtonPropsVariantOverrides {}
@@ -21,19 +18,36 @@ export interface ButtonActions {
focusVisible(): void;
}
-export interface ButtonTypeMap {
+export type ButtonTypeMap
= {
props: P & {
/**
* A ref for imperative actions.
- * It exposes the `focusVisible()` action.
+ * It currently only supports `focusVisible()` action.
*/
action?: React.Ref;
/**
- * If `true`, the ripples are centered.
- * They won't start at the cursor interaction position.
- * @default false
+ * This prop can help identify which element has keyboard focus.
+ * The class name will be applied when the element gains the focus through keyboard interaction.
+ * It's a polyfill for the [CSS :focus-visible selector](https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo).
+ * The rationale for using this feature [is explained here](https://github.com/WICG/focus-visible/blob/HEAD/explainer.md).
+ * A [polyfill can be used](https://github.com/WICG/focus-visible) to apply a `focus-visible` class to other components
+ * if needed.
+ */
+ focusVisibleClassName?: string;
+ /**
+ * The component used to render a link when the `href` prop is provided.
+ * @default 'a'
*/
- centerRipple?: boolean;
+ LinkComponent?: React.ElementType;
+ /**
+ * Callback fired when the component is focused with a keyboard.
+ * We trigger a `onFocus` callback too.
+ */
+ onFocusVisible?: React.FocusEventHandler;
+ /**
+ * @default 0
+ */
+ tabIndex?: NonNullable['tabIndex']>;
/**
* The content of the component.
*/
@@ -43,13 +57,12 @@ export interface ButtonTypeMap {
*/
classes?: Partial;
/**
- * The color of the component. It supports those theme colors that make sense for this component.
+ * The color of the component.
+ * It supports both default and custom theme colors, which can be added as shown in the
+ * [palette customization guide](https://mui.com/material-ui/customization/palette/#adding-new-colors).
* @default 'primary'
*/
- color?: OverridableStringUnion<
- 'inherit' | 'primary' | 'secondary' | 'success' | 'error' | 'info' | 'warning',
- ButtonPropsColorOverrides
- >;
+ color?: OverridableStringUnion<'primary' | 'secondary' | 'tertiary', ButtonPropsColorOverrides>;
/**
* If `true`, the component is disabled.
* @default false
@@ -65,32 +78,10 @@ export interface ButtonTypeMap {
* @default false
*/
disableFocusRipple?: boolean;
- /**
- * If `true`, the ripple effect is disabled.
- *
- * ⚠️ Without a ripple there is no styling for :focus-visible by default. Be sure
- * to highlight the element by applying separate styles with the `.Mui-focusVisible` class.
- * @default false
- */
- disableRipple?: boolean;
- /**
- * If `true`, the touch ripple effect is disabled.
- * @default false
- */
- disableTouchRipple?: boolean;
/**
* Element placed after the children.
*/
endIcon?: React.ReactNode;
- /**
- * This prop can help identify which element has keyboard focus.
- * The class name will be applied when the element gains the focus through keyboard interaction.
- * It's a polyfill for the [CSS :focus-visible selector](https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo).
- * The rationale for using this feature [is explained here](https://github.com/WICG/focus-visible/blob/HEAD/explainer.md).
- * A [polyfill can be used](https://github.com/WICG/focus-visible) to apply a `focus-visible` class to other components
- * if needed.
- */
- focusVisibleClassName?: string;
/**
* If `true`, the button will take up the full width of its container.
* @default false
@@ -101,16 +92,6 @@ export interface ButtonTypeMap
{
* If defined, an `a` element will be used as the root node.
*/
href?: string;
- /**
- * The component used to render a link when the `href` prop is provided.
- * @default 'a'
- */
- LinkComponent?: React.ElementType;
- /**
- * Callback fired when the component is focused with a keyboard.
- * We trigger a `onFocus` callback too.
- */
- onFocusVisible?: React.FocusEventHandler;
/**
* The size of the component.
* `small` is equivalent to the dense button styling.
@@ -124,62 +105,42 @@ export interface ButtonTypeMap {
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
- sx?: SxProps;
- /**
- * @default 0
- */
- tabIndex?: NonNullable['tabIndex']>;
- /**
- * Props applied to the `TouchRipple` element.
- */
- TouchRippleProps?: Partial;
+ sx?: SxProps;
/**
* The variant to use.
* @default 'text'
*/
variant?: OverridableStringUnion<
- 'text' | 'outlined' | 'contained',
+ 'text' | 'outlined' | 'filled' | 'filledTonal' | 'elevated',
ButtonPropsVariantOverrides
>;
};
defaultComponent: D;
+};
+
+export interface ButtonOwnerState extends ButtonProps {
+ /**
+ * If `true`, the button's focus is visible.
+ */
+ focusVisible?: boolean;
}
/**
- * utility to create component types that inherit props from Button.
+ * A utility to create component types that inherit props from the Button.
* This component has an additional overload if the `href` prop is set which
* can make extension quite tricky
*/
export interface ExtendButtonTypeMap {
- props: M['props'] &
- (M['props'] extends { classes?: Record }
- ? DistributiveOmit
- : ButtonTypeMap['props']);
+ props: M['props'] & ButtonTypeMap['props'];
defaultComponent: M['defaultComponent'];
}
export type ExtendButton = ((
props: { href: string } & OverrideProps, 'a'>,
) => JSX.Element) &
- OverridableComponent>;
-
-/**
- *
- * Demos:
- *
- * - [Button group](https://mui.com/material-ui/react-button-group/)
- * - [Button](https://mui.com/material-ui/react-button/)
- *
- * API:
- *
- * - [Button API](https://mui.com/api/button/)
- * - inherits [ButtonBase API](https://mui.com/api/button-base/)
- */
-declare const Button: ExtendButton;
+ OverridableComponent> & { propTypes?: any };
export type ButtonProps<
D extends React.ElementType = ButtonTypeMap['defaultComponent'],
P = {},
> = OverrideProps, D>;
-
-export default Button;
diff --git a/packages/mui-material-next/src/Button/Ripple.js b/packages/mui-material-next/src/Button/Ripple.js
deleted file mode 100644
index ae9ac4f6e965d1..00000000000000
--- a/packages/mui-material-next/src/Button/Ripple.js
+++ /dev/null
@@ -1,96 +0,0 @@
-import * as React from 'react';
-import PropTypes from 'prop-types';
-import clsx from 'clsx';
-
-/**
- * @ignore - internal component.
- */
-function Ripple(props) {
- const {
- className,
- classes,
- pulsate = false,
- rippleX,
- rippleY,
- rippleSize,
- in: inProp,
- onExited,
- timeout,
- } = props;
- const [leaving, setLeaving] = React.useState(false);
-
- const rippleClassName = clsx(className, classes.ripple, classes.rippleVisible, {
- [classes.ripplePulsate]: pulsate,
- });
-
- const rippleStyles = {
- width: rippleSize,
- height: rippleSize,
- top: -(rippleSize / 2) + rippleY,
- left: -(rippleSize / 2) + rippleX,
- };
-
- const childClassName = clsx(classes.child, {
- [classes.childLeaving]: leaving,
- [classes.childPulsate]: pulsate,
- });
-
- if (!inProp && !leaving) {
- setLeaving(true);
- }
- React.useEffect(() => {
- if (!inProp && onExited != null) {
- // react-transition-group#onExited
- const timeoutId = setTimeout(onExited, timeout);
- return () => {
- clearTimeout(timeoutId);
- };
- }
- return undefined;
- }, [onExited, inProp, timeout]);
-
- return (
-
-
-
- );
-}
-
-Ripple.propTypes = {
- /**
- * Override or extend the styles applied to the component.
- * See [CSS API](#css) below for more details.
- */
- classes: PropTypes.object.isRequired,
- className: PropTypes.string,
- /**
- * @ignore - injected from TransitionGroup
- */
- in: PropTypes.bool,
- /**
- * @ignore - injected from TransitionGroup
- */
- onExited: PropTypes.func,
- /**
- * If `true`, the ripple pulsates, typically indicating the keyboard focus state of an element.
- */
- pulsate: PropTypes.bool,
- /**
- * Diameter of the ripple.
- */
- rippleSize: PropTypes.number,
- /**
- * Horizontal position of the ripple center.
- */
- rippleX: PropTypes.number,
- /**
- * Vertical position of the ripple center.
- */
- rippleY: PropTypes.number,
- /**
- * exit delay
- */
- timeout: PropTypes.number.isRequired,
-};
-
-export default Ripple;
diff --git a/packages/mui-material-next/src/Button/Ripple.test.js b/packages/mui-material-next/src/Button/Ripple.test.js
deleted file mode 100644
index 4c445dc4f6c065..00000000000000
--- a/packages/mui-material-next/src/Button/Ripple.test.js
+++ /dev/null
@@ -1,128 +0,0 @@
-import * as React from 'react';
-import { expect } from 'chai';
-import { spy } from 'sinon';
-import { createRenderer } from 'test/utils';
-import Ripple from './Ripple';
-import classes from './touchRippleClasses';
-
-describe(' ', () => {
- const { clock, render } = createRenderer();
-
- it('should have the ripple className', () => {
- const { container } = render(
- ,
- );
- const ripple = container.querySelector('span');
- expect(ripple).to.have.class(classes.ripple);
- expect(ripple).not.to.have.class(classes.fast);
- });
-
- describe('starting and stopping', () => {
- it('should start the ripple', () => {
- const { container, setProps } = render(
- ,
- );
-
- setProps({ in: true });
-
- const ripple = container.querySelector('span');
- expect(ripple).to.have.class(classes.rippleVisible);
- });
-
- it('should stop the ripple', () => {
- const { container, setProps } = render(
- ,
- );
-
- setProps({ in: false });
-
- const child = container.querySelector('span > span');
- expect(child).to.have.class(classes.childLeaving);
- });
- });
-
- describe('pulsating and stopping 1', () => {
- it('should render the ripple inside a pulsating Ripple', () => {
- const { container } = render(
- ,
- );
-
- const ripple = container.querySelector('span');
- expect(ripple).to.have.class(classes.ripple);
- expect(ripple).to.have.class(classes.ripplePulsate);
- const child = container.querySelector('span > span');
- expect(child).to.have.class(classes.childPulsate);
- });
-
- it('should start the ripple', () => {
- const { container, setProps } = render(
- ,
- );
-
- setProps({ in: true });
-
- const ripple = container.querySelector('span');
- expect(ripple).to.have.class(classes.rippleVisible);
- const child = container.querySelector('span > span');
- expect(child).to.have.class(classes.childPulsate);
- });
-
- it('should stop the ripple', () => {
- const { container, setProps } = render(
- ,
- );
-
- setProps({ in: true });
- setProps({ in: false });
- const child = container.querySelector('span > span');
- expect(child).to.have.class(classes.childLeaving);
- });
- });
-
- describe('pulsating and stopping 2', () => {
- clock.withFakeTimers();
-
- it('handleExit should trigger a timer', () => {
- const handleExited = spy();
- const { setProps } = render(
- ,
- );
-
- setProps({ in: false });
- clock.tick(549);
- expect(handleExited.callCount).to.equal(0);
- clock.tick(1);
- expect(handleExited.callCount).to.equal(1);
- });
-
- it('unmount should defuse the handleExit timer', () => {
- const handleExited = spy();
- const { setProps, unmount } = render(
- ,
- );
-
- setProps({ in: false });
- unmount();
- clock.tick(550);
- expect(handleExited.callCount).to.equal(0);
- });
- });
-});
diff --git a/packages/mui-material-next/src/Button/TouchRipple.d.ts b/packages/mui-material-next/src/Button/TouchRipple.d.ts
deleted file mode 100644
index f23164f5f562b1..00000000000000
--- a/packages/mui-material-next/src/Button/TouchRipple.d.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import * as React from 'react';
-import { InternalStandardProps as StandardProps } from '@mui/material';
-import { TouchRippleClasses, TouchRippleClassKey } from './touchRippleClasses';
-
-export { TouchRippleClassKey };
-
-export interface StartActionOptions {
- pulsate?: boolean;
- center?: boolean;
-}
-
-export interface TouchRippleActions {
- start: (
- event?: React.SyntheticEvent,
- options?: StartActionOptions,
- callback?: () => void,
- ) => void;
- pulsate: (event?: React.SyntheticEvent) => void;
- stop: (event?: React.SyntheticEvent, callback?: () => void) => void;
-}
-
-export type TouchRippleProps = StandardProps> & {
- center?: boolean;
- /**
- * Override or extend the styles applied to the component.
- */
- classes?: Partial;
-};
-
-declare const TouchRipple: React.ForwardRefRenderFunction;
-
-export default TouchRipple;
diff --git a/packages/mui-material-next/src/Button/TouchRipple.js b/packages/mui-material-next/src/Button/TouchRipple.js
deleted file mode 100644
index 8ce631381db30c..00000000000000
--- a/packages/mui-material-next/src/Button/TouchRipple.js
+++ /dev/null
@@ -1,333 +0,0 @@
-import * as React from 'react';
-import PropTypes from 'prop-types';
-import { TransitionGroup } from 'react-transition-group';
-import clsx from 'clsx';
-import { keyframes, useThemeProps } from '@mui/system';
-import styled from '@mui/material/styles/styled';
-import Ripple from './Ripple';
-import touchRippleClasses from './touchRippleClasses';
-
-const DURATION = 550;
-export const DELAY_RIPPLE = 80;
-
-const enterKeyframe = keyframes`
- 0% {
- transform: scale(0);
- opacity: 0.1;
- }
-
- 100% {
- transform: scale(1);
- opacity: 0.3;
- }
-`;
-
-const exitKeyframe = keyframes`
- 0% {
- opacity: 1;
- }
-
- 100% {
- opacity: 0;
- }
-`;
-
-const pulsateKeyframe = keyframes`
- 0% {
- transform: scale(1);
- }
-
- 50% {
- transform: scale(0.92);
- }
-
- 100% {
- transform: scale(1);
- }
-`;
-
-export const TouchRippleRoot = styled('span', {
- name: 'MuiTouchRipple',
- slot: 'Root',
-})({
- overflow: 'hidden',
- pointerEvents: 'none',
- position: 'absolute',
- zIndex: 0,
- top: 0,
- right: 0,
- bottom: 0,
- left: 0,
- borderRadius: 'inherit',
-});
-
-// This `styled()` function invokes keyframes. `styled-components` only supports keyframes
-// in string templates. Do not convert these styles in JS object as it will break.
-export const TouchRippleRipple = styled(Ripple, {
- name: 'MuiTouchRipple',
- slot: 'Ripple',
-})`
- opacity: 0;
- position: absolute;
-
- &.${touchRippleClasses.rippleVisible} {
- opacity: 0.3;
- transform: scale(1);
- animation-name: ${enterKeyframe};
- animation-duration: ${DURATION}ms;
- animation-timing-function: ${({ theme }) => theme.transitions.easing.easeInOut};
- }
-
- &.${touchRippleClasses.ripplePulsate} {
- animation-duration: ${({ theme }) => theme.transitions.duration.shorter}ms;
- }
-
- & .${touchRippleClasses.child} {
- opacity: 1;
- display: block;
- width: 100%;
- height: 100%;
- border-radius: 50%;
- background-color: currentColor;
- }
-
- & .${touchRippleClasses.childLeaving} {
- opacity: 0;
- animation-name: ${exitKeyframe};
- animation-duration: ${DURATION}ms;
- animation-timing-function: ${({ theme }) => theme.transitions.easing.easeInOut};
- }
-
- & .${touchRippleClasses.childPulsate} {
- position: absolute;
- /* @noflip */
- left: 0px;
- top: 0;
- animation-name: ${pulsateKeyframe};
- animation-duration: 2500ms;
- animation-timing-function: ${({ theme }) => theme.transitions.easing.easeInOut};
- animation-iteration-count: infinite;
- animation-delay: 200ms;
- }
-`;
-
-/**
- * @ignore - internal component.
- *
- * TODO v5: Make private
- */
-const TouchRipple = React.forwardRef(function TouchRipple(inProps, ref) {
- const props = useThemeProps({ props: inProps, name: 'MuiTouchRipple' });
-
- const { center: centerProp = false, classes = {}, className, ...other } = props;
- const [ripples, setRipples] = React.useState([]);
- const nextKey = React.useRef(0);
- const rippleCallback = React.useRef(null);
-
- React.useEffect(() => {
- if (rippleCallback.current) {
- rippleCallback.current();
- rippleCallback.current = null;
- }
- }, [ripples]);
-
- // Used to filter out mouse emulated events on mobile.
- const ignoringMouseDown = React.useRef(false);
- // We use a timer in order to only show the ripples for touch "click" like events.
- // We don't want to display the ripple for touch scroll events.
- const startTimer = React.useRef(null);
-
- // This is the hook called once the previous timeout is ready.
- const startTimerCommit = React.useRef(null);
- const container = React.useRef(null);
-
- React.useEffect(() => {
- return () => {
- clearTimeout(startTimer.current);
- };
- }, []);
-
- const startCommit = React.useCallback(
- (params) => {
- const { pulsate, rippleX, rippleY, rippleSize, cb } = params;
-
- setRipples((oldRipples) => [
- ...oldRipples,
- ,
- ]);
- nextKey.current += 1;
- rippleCallback.current = cb;
- },
- [classes],
- );
-
- const start = React.useCallback(
- (event = {}, options = {}, cb = () => {}) => {
- const {
- pulsate = false,
- center = centerProp || options.pulsate,
- fakeElement = false, // For test purposes
- } = options;
-
- if (event.type === 'mousedown' && ignoringMouseDown.current) {
- ignoringMouseDown.current = false;
- return;
- }
-
- if (event.type === 'touchstart') {
- ignoringMouseDown.current = true;
- }
-
- const element = fakeElement ? null : container.current;
- const rect = element
- ? element.getBoundingClientRect()
- : {
- width: 0,
- height: 0,
- left: 0,
- top: 0,
- };
-
- // Get the size of the ripple
- let rippleX;
- let rippleY;
- let rippleSize;
-
- if (
- center ||
- (event.clientX === 0 && event.clientY === 0) ||
- (!event.clientX && !event.touches)
- ) {
- rippleX = Math.round(rect.width / 2);
- rippleY = Math.round(rect.height / 2);
- } else {
- const { clientX, clientY } = event.touches ? event.touches[0] : event;
- rippleX = Math.round(clientX - rect.left);
- rippleY = Math.round(clientY - rect.top);
- }
-
- if (center) {
- rippleSize = Math.sqrt((2 * rect.width ** 2 + rect.height ** 2) / 3);
-
- // For some reason the animation is broken on Mobile Chrome if the size is even.
- if (rippleSize % 2 === 0) {
- rippleSize += 1;
- }
- } else {
- const sizeX =
- Math.max(Math.abs((element ? element.clientWidth : 0) - rippleX), rippleX) * 2 + 2;
- const sizeY =
- Math.max(Math.abs((element ? element.clientHeight : 0) - rippleY), rippleY) * 2 + 2;
- rippleSize = Math.sqrt(sizeX ** 2 + sizeY ** 2);
- }
-
- // Touche devices
- if (event.touches) {
- // check that this isn't another touchstart due to multitouch
- // otherwise we will only clear a single timer when unmounting while two
- // are running
- if (startTimerCommit.current === null) {
- // Prepare the ripple effect.
- startTimerCommit.current = () => {
- startCommit({ pulsate, rippleX, rippleY, rippleSize, cb });
- };
- // Delay the execution of the ripple effect.
- startTimer.current = setTimeout(() => {
- if (startTimerCommit.current) {
- startTimerCommit.current();
- startTimerCommit.current = null;
- }
- }, DELAY_RIPPLE); // We have to make a tradeoff with this value.
- }
- } else {
- startCommit({ pulsate, rippleX, rippleY, rippleSize, cb });
- }
- },
- [centerProp, startCommit],
- );
-
- const pulsate = React.useCallback(() => {
- start({}, { pulsate: true });
- }, [start]);
-
- const stop = React.useCallback((event, cb) => {
- clearTimeout(startTimer.current);
-
- // The touch interaction occurs too quickly.
- // We still want to show ripple effect.
- if (event.type === 'touchend' && startTimerCommit.current) {
- startTimerCommit.current();
- startTimerCommit.current = null;
- startTimer.current = setTimeout(() => {
- stop(event, cb);
- });
- return;
- }
-
- startTimerCommit.current = null;
-
- setRipples((oldRipples) => {
- if (oldRipples.length > 0) {
- return oldRipples.slice(1);
- }
- return oldRipples;
- });
- rippleCallback.current = cb;
- }, []);
-
- React.useImperativeHandle(
- ref,
- () => ({
- pulsate,
- start,
- stop,
- }),
- [pulsate, start, stop],
- );
-
- return (
-
-
- {ripples}
-
-
- );
-});
-
-TouchRipple.propTypes = {
- /**
- * If `true`, the ripple starts at the center of the component
- * rather than at the point of interaction.
- */
- center: PropTypes.bool,
- /**
- * Override or extend the styles applied to the component.
- * See [CSS API](#css) below for more details.
- */
- classes: PropTypes.object,
- /**
- * @ignore
- */
- className: PropTypes.string,
-};
-
-export default TouchRipple;
diff --git a/packages/mui-material-next/src/Button/TouchRipple.test.js b/packages/mui-material-next/src/Button/TouchRipple.test.js
deleted file mode 100644
index 0f225674f8fc7e..00000000000000
--- a/packages/mui-material-next/src/Button/TouchRipple.test.js
+++ /dev/null
@@ -1,270 +0,0 @@
-import * as React from 'react';
-import { expect } from 'chai';
-import { describeConformance, act, createRenderer } from 'test/utils';
-import TouchRipple, { DELAY_RIPPLE } from './TouchRipple';
-
-const cb = () => {};
-
-describe(' ', () => {
- const { clock, render } = createRenderer();
-
- /**
- * @param {object} other props to spread to TouchRipple
- */
- function renderTouchRipple(other) {
- const touchRippleRef = React.createRef();
- const { container, unmount } = render(
- ,
- );
-
- return {
- instance: touchRippleRef.current,
- queryAllActiveRipples() {
- return container.querySelectorAll('.ripple-visible .child:not(.child-leaving)');
- },
- queryAllStoppingRipples() {
- return container.querySelectorAll('.ripple-visible .child-leaving');
- },
- queryRipple() {
- return container.querySelector('.ripple');
- },
- unmount,
- };
- }
-
- describeConformance( , () => ({
- classes: {},
- inheritComponent: 'span',
- render,
- refInstanceof: Object,
- muiName: 'MuiTouchRipple',
- skip: [
- 'componentProp',
- 'componentsProp',
- 'refForwarding',
- 'themeStyleOverrides',
- 'themeVariants',
- ],
- }));
-
- describe('prop: center', () => {
- it('should compute the right ripple dimensions', () => {
- const { instance, queryRipple } = renderTouchRipple({ center: true });
-
- act(() => {
- instance.start(
- {},
- {
- fakeElement: true,
- },
- cb,
- );
- });
-
- expect(queryRipple()).toHaveInlineStyle({ height: '1px' });
- expect(queryRipple()).toHaveInlineStyle({ width: '1px' });
- });
- });
-
- it('should create individual ripples', () => {
- const { instance, queryAllActiveRipples, queryAllStoppingRipples } = renderTouchRipple();
-
- expect(queryAllActiveRipples()).to.have.lengthOf(0);
- expect(queryAllStoppingRipples()).to.have.lengthOf(0);
-
- act(() => {
- instance.start({ clientX: 0, clientY: 0 }, cb);
- });
-
- expect(queryAllActiveRipples()).to.have.lengthOf(1);
- expect(queryAllStoppingRipples()).to.have.lengthOf(0);
-
- act(() => {
- instance.start({ clientX: 0, clientY: 0 }, cb);
- });
-
- expect(queryAllActiveRipples()).to.have.lengthOf(2);
- expect(queryAllStoppingRipples()).to.have.lengthOf(0);
-
- act(() => {
- instance.start({ clientX: 0, clientY: 0 }, cb);
- });
-
- expect(queryAllActiveRipples()).to.have.lengthOf(3);
- expect(queryAllStoppingRipples()).to.have.lengthOf(0);
-
- act(() => {
- instance.stop({ type: 'mouseup' });
- });
-
- expect(queryAllActiveRipples()).to.have.lengthOf(2);
- expect(queryAllStoppingRipples()).to.have.lengthOf(1);
-
- act(() => {
- instance.stop({ type: 'mouseup' });
- });
-
- expect(queryAllActiveRipples()).to.have.lengthOf(1);
- expect(queryAllStoppingRipples()).to.have.lengthOf(2);
-
- act(() => {
- instance.stop({ type: 'mouseup' });
- });
-
- expect(queryAllActiveRipples()).to.have.lengthOf(0);
- expect(queryAllStoppingRipples()).to.have.lengthOf(3);
- });
-
- describe('creating unique ripples', () => {
- it('should create a ripple', () => {
- const { instance, queryAllActiveRipples, queryAllStoppingRipples } = renderTouchRipple();
-
- act(() => {
- instance.start(
- {},
- {
- pulsate: true,
- fakeElement: true,
- },
- cb,
- );
- });
-
- expect(queryAllActiveRipples()).to.have.lengthOf(1);
- expect(queryAllStoppingRipples()).to.have.lengthOf(0);
- });
-
- it('should ignore a mousedown event after a touchstart event', () => {
- const { instance, queryAllActiveRipples, queryAllStoppingRipples } = renderTouchRipple();
-
- act(() => {
- instance.start({ type: 'touchstart' }, cb);
- instance.start({ type: 'mousedown' }, cb);
- });
-
- expect(queryAllActiveRipples()).to.have.lengthOf(1);
- expect(queryAllStoppingRipples()).to.have.lengthOf(0);
- });
-
- it('should create a specific ripple', () => {
- const { instance, queryAllActiveRipples, queryAllStoppingRipples, queryRipple } =
- renderTouchRipple({
- center: true,
- });
- const clientX = 1;
- const clientY = 1;
-
- act(() => {
- instance.start({ clientX, clientY }, { fakeElement: true }, cb);
- });
-
- expect(queryAllActiveRipples()).to.have.lengthOf(1);
- expect(queryAllStoppingRipples()).to.have.lengthOf(0);
- expect(queryRipple()).toHaveInlineStyle({ top: '-0.5px' });
- expect(queryRipple()).toHaveInlineStyle({ left: '-0.5px' });
- });
- });
-
- describe('mobile', () => {
- clock.withFakeTimers();
-
- it('should delay the display of the ripples', () => {
- const { instance, queryAllActiveRipples, queryAllStoppingRipples } = renderTouchRipple();
-
- expect(queryAllActiveRipples()).to.have.lengthOf(0);
- expect(queryAllStoppingRipples()).to.have.lengthOf(0);
-
- act(() => {
- instance.start({ touches: [], clientX: 0, clientY: 0 }, { fakeElement: true }, cb);
- });
-
- expect(queryAllActiveRipples()).to.have.lengthOf(0);
- expect(queryAllStoppingRipples()).to.have.lengthOf(0);
-
- clock.tick(DELAY_RIPPLE);
-
- expect(queryAllActiveRipples()).to.have.lengthOf(1);
- expect(queryAllStoppingRipples()).to.have.lengthOf(0);
-
- clock.tick(DELAY_RIPPLE);
- act(() => {
- instance.stop({ type: 'touchend' }, cb);
- });
-
- expect(queryAllActiveRipples()).to.have.lengthOf(0);
- expect(queryAllStoppingRipples()).to.have.lengthOf(1);
- });
-
- it('should trigger the ripple for short touch interactions', () => {
- const { instance, queryAllActiveRipples, queryAllStoppingRipples } = renderTouchRipple();
-
- expect(queryAllActiveRipples()).to.have.lengthOf(0);
- expect(queryAllStoppingRipples()).to.have.lengthOf(0);
-
- act(() => {
- instance.start({ touches: [], clientX: 0, clientY: 0 }, { fakeElement: true }, cb);
- });
-
- expect(queryAllActiveRipples()).to.have.lengthOf(0);
- expect(queryAllStoppingRipples()).to.have.lengthOf(0);
-
- clock.tick(DELAY_RIPPLE / 2);
-
- expect(queryAllActiveRipples()).to.have.lengthOf(0);
- expect(queryAllStoppingRipples()).to.have.lengthOf(0);
-
- act(() => {
- instance.stop({ type: 'touchend' }, cb);
- });
-
- expect(queryAllActiveRipples()).to.have.lengthOf(1);
- expect(queryAllStoppingRipples()).to.have.lengthOf(0);
-
- clock.tick(1);
-
- expect(queryAllActiveRipples()).to.have.lengthOf(0);
- expect(queryAllStoppingRipples()).to.have.lengthOf(1);
- });
-
- it('should interrupt the ripple schedule', () => {
- const { instance, queryAllActiveRipples, queryAllStoppingRipples } = renderTouchRipple();
-
- expect(queryAllActiveRipples()).to.have.lengthOf(0);
- expect(queryAllStoppingRipples()).to.have.lengthOf(0);
-
- instance.start({ touches: [], clientX: 0, clientY: 0 }, { fakeElement: true }, cb);
- expect(queryAllActiveRipples()).to.have.lengthOf(0);
- expect(queryAllStoppingRipples()).to.have.lengthOf(0);
-
- clock.tick(DELAY_RIPPLE / 2);
- expect(queryAllActiveRipples()).to.have.lengthOf(0);
- expect(queryAllStoppingRipples()).to.have.lengthOf(0);
-
- instance.stop({ type: 'touchmove' });
- clock.tick(DELAY_RIPPLE);
- expect(queryAllActiveRipples()).to.have.lengthOf(0);
- expect(queryAllStoppingRipples()).to.have.lengthOf(0);
- });
-
- it('should not leak on multi-touch', function multiTouchTest() {
- const { instance, unmount } = renderTouchRipple();
-
- instance.start({ type: 'touchstart', touches: [{}] }, () => {});
- instance.start({ type: 'touchstart', touches: [{}] }, () => {});
- unmount();
-
- // expect this to run gracefully without
- // "react state update on an unmounted component"
- clock.runAll();
- });
- });
-});
diff --git a/packages/mui-material-next/src/Button/buttonClasses.ts b/packages/mui-material-next/src/Button/buttonClasses.ts
index 72aa7d02cb9143..6c5ecf49c3b724 100644
--- a/packages/mui-material-next/src/Button/buttonClasses.ts
+++ b/packages/mui-material-next/src/Button/buttonClasses.ts
@@ -6,54 +6,26 @@ export interface ButtonClasses {
root: string;
/** Styles applied to the root element if `variant="text"`. */
text: string;
- /** Styles applied to the root element if `variant="text"` and `color="inherit"`. */
- textInherit: string;
- /** Styles applied to the root element if `variant="text"` and `color="primary"`. */
- textPrimary: string;
- /** Styles applied to the root element if `variant="text"` and `color="secondary"`. */
- textSecondary: string;
/** Styles applied to the root element if `variant="outlined"`. */
outlined: string;
- /** Styles applied to the root element if `variant="outlined"` and `color="inherit"`. */
- outlinedInherit: string;
- /** Styles applied to the root element if `variant="outlined"` and `color="primary"`. */
- outlinedPrimary: string;
- /** Styles applied to the root element if `variant="outlined"` and `color="secondary"`. */
- outlinedSecondary: string;
- /** Styles applied to the root element if `variant="contained"`. */
- contained: string;
- /** Styles applied to the root element if `variant="contained"` and `color="inherit"`. */
- containedInherit: string;
- /** Styles applied to the root element if `variant="contained"` and `color="primary"`. */
- containedPrimary: string;
- /** Styles applied to the root element if `variant="contained"` and `color="secondary"`. */
- containedSecondary: string;
+ /** Styles applied to the root element if `variant="filled"`. */
+ filled: string;
+ /** Styles applied to the root element if `variant="filledTonal"`. */
+ filledTonal: string;
+ /** Styles applied to the root element if `variant="elevated"`. */
+ elevated: string;
/** Styles applied to the root element if `disableElevation={true}`. */
disableElevation: string;
/** State class applied to the ButtonBase root element if the button is keyboard focused. */
focusVisible: string;
/** State class applied to the root element if `disabled={true}`. */
disabled: string;
- /** Styles applied to the root element if `color="inherit"`. */
- colorInherit: string;
- /** Styles applied to the root element if `size="small"` and `variant="text"`. */
- textSizeSmall: string;
- /** Styles applied to the root element if `size="medium"` and `variant="text"`. */
- textSizeMedium: string;
- /** Styles applied to the root element if `size="large"` and `variant="text"`. */
- textSizeLarge: string;
- /** Styles applied to the root element if `size="small"` and `variant="outlined"`. */
- outlinedSizeSmall: string;
- /** Styles applied to the root element if `size="medium"` and `variant="outlined"`. */
- outlinedSizeMedium: string;
- /** Styles applied to the root element if `size="large"` and `variant="outlined"`. */
- outlinedSizeLarge: string;
- /** Styles applied to the root element if `size="small"` and `variant="contained"`. */
- containedSizeSmall: string;
- /** Styles applied to the root element if `size="small"` and `variant="contained"`. */
- containedSizeMedium: string;
- /** Styles applied to the root element if `size="large"` and `variant="contained"`. */
- containedSizeLarge: string;
+ /** Styles applied to the root element if `color="primary"`. */
+ colorPrimary: string;
+ /** Styles applied to the root element if `color="secondary"`. */
+ colorSecondary: string;
+ /** Styles applied to the root element if `color="tertiary"`. */
+ colorTertiary: string;
/** Styles applied to the root element if `size="small"`. */
sizeSmall: string;
/** Styles applied to the root element if `size="medium"`. */
@@ -83,32 +55,19 @@ export function getButtonUtilityClass(slot: string): string {
const buttonClasses: ButtonClasses = generateUtilityClasses('MuiButton', [
'root',
'text',
- 'textInherit',
- 'textPrimary',
- 'textSecondary',
'outlined',
- 'outlinedInherit',
- 'outlinedPrimary',
- 'outlinedSecondary',
- 'contained',
- 'containedInherit',
- 'containedPrimary',
- 'containedSecondary',
+ 'filled',
+ 'filledTonal',
+ 'elevated',
+ 'colorPrimary',
+ 'colorSecondary',
+ 'colorTertiary',
'disableElevation',
'focusVisible',
'disabled',
'colorInherit',
- 'textSizeSmall',
- 'textSizeMedium',
- 'textSizeLarge',
- 'outlinedSizeSmall',
- 'outlinedSizeMedium',
- 'outlinedSizeLarge',
- 'containedSizeSmall',
- 'containedSizeMedium',
- 'containedSizeLarge',
- 'sizeMedium',
'sizeSmall',
+ 'sizeMedium',
'sizeLarge',
'fullWidth',
'startIcon',
diff --git a/packages/mui-material-next/src/Button/index.ts b/packages/mui-material-next/src/Button/index.ts
index 8946132d8d8701..176aaa81470666 100644
--- a/packages/mui-material-next/src/Button/index.ts
+++ b/packages/mui-material-next/src/Button/index.ts
@@ -1,5 +1,7 @@
export { default } from './Button';
export * from './Button';
+export * from './Button.types';
+
export { default as buttonClasses } from './buttonClasses';
export * from './buttonClasses';
diff --git a/packages/mui-material-next/src/Button/touchRippleClasses.ts b/packages/mui-material-next/src/Button/touchRippleClasses.ts
deleted file mode 100644
index 7975f4f7dd036c..00000000000000
--- a/packages/mui-material-next/src/Button/touchRippleClasses.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import generateUtilityClass from '@mui/material/generateUtilityClass';
-import generateUtilityClasses from '@mui/material/generateUtilityClasses';
-
-export interface TouchRippleClasses {
- /** Styles applied to the root element. */
- root: string;
- /** Styles applied to the internal `Ripple` components `ripple` class. */
- ripple: string;
- /** Styles applied to the internal `Ripple` components `rippleVisible` class. */
- rippleVisible: string;
- /** Styles applied to the internal `Ripple` components `ripplePulsate` class. */
- ripplePulsate: string;
- /** Styles applied to the internal `Ripple` components `child` class. */
- child: string;
- /** Styles applied to the internal `Ripple` components `childLeaving` class. */
- childLeaving: string;
- /** Styles applied to the internal `Ripple` components `childPulsate` class. */
- childPulsate: string;
-}
-
-export type TouchRippleClassKey = keyof TouchRippleClasses;
-
-export function getTouchRippleUtilityClass(slot: string): string {
- return generateUtilityClass('MuiTouchRipple', slot);
-}
-
-const touchRippleClasses: TouchRippleClasses = generateUtilityClasses('MuiTouchRipple', [
- 'root',
- 'ripple',
- 'rippleVisible',
- 'ripplePulsate',
- 'child',
- 'childLeaving',
- 'childPulsate',
-]);
-
-export default touchRippleClasses;
diff --git a/packages/mui-material-next/src/styles/CssVarsProvider.tsx b/packages/mui-material-next/src/styles/CssVarsProvider.tsx
new file mode 100644
index 00000000000000..05a9aef2bb47a2
--- /dev/null
+++ b/packages/mui-material-next/src/styles/CssVarsProvider.tsx
@@ -0,0 +1,35 @@
+import { unstable_createCssVarsProvider as createCssVarsProvider } from '@mui/system';
+import {
+ SupportedColorScheme,
+ private_createTypography as createTypography,
+ private_excludeVariablesFromRoot as excludeVariablesFromRoot,
+} from '@mui/material/styles';
+import defaultTheme from './defaultTheme';
+
+const shouldSkipGeneratingVar = (keys: string[]) =>
+ !!keys[0].match(/(typography|mixins|breakpoints|direction|transitions)/) ||
+ (keys[0] === 'palette' && !!keys[1]?.match(/(mode|contrastThreshold|tonalOffset)/));
+
+const { CssVarsProvider, useColorScheme, getInitColorSchemeScript } =
+ createCssVarsProvider({
+ theme: defaultTheme,
+ attribute: 'data-mui-color-scheme',
+ modeStorageKey: 'mui-mode',
+ colorSchemeStorageKey: 'mui-color-scheme',
+ defaultColorScheme: {
+ light: 'light',
+ dark: 'dark',
+ },
+ resolveTheme: (theme) => {
+ const newTheme = {
+ ...theme,
+ typography: createTypography(theme.palette, theme.typography),
+ };
+
+ return newTheme;
+ },
+ shouldSkipGeneratingVar,
+ excludeVariablesFromRoot,
+ });
+
+export { useColorScheme, getInitColorSchemeScript, shouldSkipGeneratingVar, CssVarsProvider };
diff --git a/packages/mui-material-next/src/styles/Theme.types.ts b/packages/mui-material-next/src/styles/Theme.types.ts
new file mode 100644
index 00000000000000..5db38f25017d04
--- /dev/null
+++ b/packages/mui-material-next/src/styles/Theme.types.ts
@@ -0,0 +1,197 @@
+import { SxProps as SystemSxProps } from '@mui/system';
+import {
+ CssVarsTheme as MD2Theme,
+ SupportedColorScheme,
+ ColorSystemOptions as MD2ColorSystemOptions,
+ CssVarsThemeOptions as MD2CssVarsThemeOptions,
+} from '@mui/material/styles';
+
+export interface MD3Tones {
+ 0: string;
+ 10: string;
+ 20: string;
+ 30: string;
+ 40: string;
+ 50: string;
+ 60: string;
+ 70: string;
+ 80: string;
+ 90: string;
+ 95: string;
+ 99: string;
+ 100: string;
+}
+export interface MD3Palettes {
+ primary: MD3Tones;
+ secondary: MD3Tones;
+ tertiary: MD3Tones;
+ neutral: MD3Tones;
+ neutralVariant: MD3Tones;
+ error: MD3Tones;
+ common: {
+ black: string;
+ white: string;
+ };
+}
+
+export interface MD3ColorSchemeTokens {
+ primary: string;
+ onPrimary: string;
+ primaryContainer: string;
+ onPrimaryContainer: string;
+
+ secondary: string;
+ onSecondary: string;
+ secondaryContainer: string;
+ onSecondaryContainer: string;
+
+ tertiary: string;
+ onTertiary: string;
+ tertiaryContainer: string;
+ onTertiaryContainer: string;
+
+ error: string;
+ onError: string;
+ errorContainer: string;
+ onErrorContainer: string;
+
+ background: string;
+ onBackground: string;
+
+ surface: string;
+ onSurface: string;
+ surfaceVariant: string;
+ onSurfaceVariant: string;
+
+ inverseSurface: string;
+ inverseOnSurface: string;
+ inversePrimary: string;
+ surfaceTint?: string;
+
+ outline: string;
+ shadow: string;
+
+ // channels
+ primaryChannel: string;
+ secondaryChannel: string;
+ tertiaryChannel: string;
+ onSurfaceChannel: string;
+ secondaryContainerChannel: string;
+}
+
+export interface MD3Typeface {
+ plain: string;
+ brand: string;
+ weight: {
+ bold: string;
+ medium: string;
+ regular: string;
+ };
+}
+
+export interface MD3States {
+ hover: {
+ stateLayerOpacity: number;
+ };
+ focus: {
+ stateLayerOpacity: number;
+ };
+ pressed: {
+ stateLayerOpacity: number;
+ };
+ dragged: {
+ stateLayerOpacity: number;
+ };
+}
+
+export interface TypescaleValue {
+ small: {
+ family: string;
+ weight: string;
+ };
+ medium: {
+ family: string;
+ weight: string;
+ };
+ large: {
+ family: string;
+ weight: string;
+ lineHeight: number;
+ size: number;
+ tracking: number;
+ };
+}
+
+export interface MD3Typescale {
+ label: TypescaleValue;
+ body: TypescaleValue;
+ headline: TypescaleValue;
+ display: TypescaleValue;
+}
+
+export interface Shapes {
+ borderRadius: number;
+}
+
+export interface MD3CssVarsThemeOptions extends Omit {
+ md3?: {
+ shape?: Partial;
+ };
+ ref?: {
+ typeface?: Partial;
+ };
+ sys?: {
+ typescale?: Partial;
+ state?: Partial;
+ };
+}
+
+export interface ColorSystemOptions extends MD2ColorSystemOptions {
+ ref?: {
+ palette?: Partial;
+ };
+ sys?: {
+ color?: Partial;
+ };
+}
+
+export interface CssVarsThemeOptions extends Omit {
+ /**
+ * Color schemes configuration
+ */
+ colorSchemes?: Partial>;
+}
+
+export interface Theme extends Omit {
+ useMaterialYou?: boolean;
+ ref: {
+ palette: MD3Palettes;
+ typeface: MD3Typeface;
+ };
+ sys: {
+ color: MD3ColorSchemeTokens;
+ typescale: MD3Typescale;
+ state: MD3States;
+ };
+ md3: {
+ shape: Shapes;
+ };
+ palette: MD2Theme['palette'];
+ vars: MD2Theme['vars'] & {
+ palette: MD2Theme['vars']['palette'];
+ ref: {
+ palette: MD3Palettes;
+ typeface: any;
+ };
+ sys: {
+ color: MD3ColorSchemeTokens;
+ typescale: MD3Typescale;
+ state: MD3States;
+ };
+ md3: {
+ shape: Shapes;
+ };
+ };
+}
+
+export type SxProps = SystemSxProps;
diff --git a/packages/mui-material-next/src/styles/createDarkColorScheme.ts b/packages/mui-material-next/src/styles/createDarkColorScheme.ts
new file mode 100644
index 00000000000000..55ffd8e5707df4
--- /dev/null
+++ b/packages/mui-material-next/src/styles/createDarkColorScheme.ts
@@ -0,0 +1,37 @@
+import { MD3Palettes } from './Theme.types';
+// convert all these values to CSS vars
+const createDarkColorScheme = (
+ getCssVar: (cssVar: string, defaultVal: string) => string,
+ palette: MD3Palettes,
+) => ({
+ surfaceTint: getCssVar('ref-palette-primary-40', palette.primary[40]),
+ onErrorContainer: getCssVar('ref-palette-error-80', palette.error[80]),
+ onError: getCssVar('ref-palette-error-20', palette.error[20]),
+ errorContainer: getCssVar('ref-palette-error-30', palette.error[30]),
+ onTertiaryContainer: getCssVar('ref-palette-tertiary-90', palette.tertiary[90]),
+ onTertiary: getCssVar('ref-palette-tertiary-20', palette.tertiary[20]),
+ tertiaryContainer: getCssVar('ref-palette-tertiary-30', palette.tertiary[30]),
+ tertiary: getCssVar('ref-palette-tertiary-80', palette.tertiary[80]),
+ shadow: getCssVar('ref-palette-common-black', palette.common.black),
+ error: getCssVar('ref-palette-error-80', palette.error[80]),
+ outline: getCssVar('ref-palette-neutralVariant-60', palette.neutralVariant[60]),
+ onBackground: getCssVar('ref-palette-neutral-90', palette.neutral[90]),
+ background: getCssVar('ref-palette-neutral-10', palette.neutral[10]),
+ inverseOnSurface: getCssVar('ref-palette-neutral-20', palette.neutral[20]),
+ inverseSurface: getCssVar('ref-palette-neutral-90', palette.neutral[90]),
+ onSurfaceVariant: getCssVar('ref-palette-neutralVariant-80', palette.neutralVariant[80]),
+ onSurface: getCssVar('ref-palette-neutral-90', palette.neutral[90]),
+ surfaceVariant: getCssVar('ref-palette-neutralVariant-30', palette.neutralVariant[30]),
+ surface: getCssVar('ref-palette-neutral-10', palette.neutral[10]),
+ onSecondaryContainer: getCssVar('ref-palette-secondary-90', palette.secondary[90]),
+ onSecondary: getCssVar('ref-palette-secondary-20', palette.secondary[20]),
+ secondaryContainer: getCssVar('ref-palette-secondary-30', palette.secondary[30]),
+ secondary: getCssVar('ref-palette-secondary-80', palette.secondary[80]),
+ inversePrimary: getCssVar('ref-palette-primary-40', palette.primary[40]),
+ onPrimaryContainer: getCssVar('ref-palette-primary-90', palette.primary[90]),
+ onPrimary: getCssVar('ref-palette-primary-20', palette.primary[20]),
+ primaryContainer: getCssVar('ref-palette-primary-30', palette.primary[30]),
+ primary: getCssVar('ref-palette-primary-80', palette.primary[80]),
+});
+
+export default createDarkColorScheme;
diff --git a/packages/mui-material-next/src/styles/createLightColorScheme.ts b/packages/mui-material-next/src/styles/createLightColorScheme.ts
new file mode 100644
index 00000000000000..b5894088a3de0e
--- /dev/null
+++ b/packages/mui-material-next/src/styles/createLightColorScheme.ts
@@ -0,0 +1,37 @@
+import { MD3Palettes } from './Theme.types';
+
+const createLightColorScheme = (
+ getCssVar: (cssVar: string, defaultVal: string) => string,
+ palette: MD3Palettes,
+) => ({
+ surfaceTint: getCssVar('ref-palette-primary-40', palette.primary[40]),
+ onErrorContainer: getCssVar('ref-palette-error-10', palette.error[10]),
+ onError: getCssVar('ref-palette-error-100', palette.error[100]),
+ errorContainer: getCssVar('ref-palette-error-90', palette.error[90]),
+ onTertiaryContainer: getCssVar('ref-palette-tertiary-10', palette.tertiary[10]),
+ onTertiary: getCssVar('ref-palette-tertiary-100', palette.tertiary[100]),
+ tertiaryContainer: getCssVar('ref-palette-tertiary-90', palette.tertiary[90]),
+ tertiary: getCssVar('ref-palette-tertiary-40', palette.tertiary[40]),
+ shadow: getCssVar('ref-palette-common-black', palette.common.black),
+ error: getCssVar('ref-palette-error-40', palette.error[40]),
+ outline: getCssVar('ref-palette-neutralVariant-50', palette.neutralVariant[50]),
+ onBackground: getCssVar('ref-palette-neutral-10', palette.neutral[10]),
+ background: getCssVar('ref-palette-neutral-99', palette.neutral[99]),
+ inverseOnSurface: getCssVar('ref-palette-neutral-95', palette.neutral[95]),
+ inverseSurface: getCssVar('ref-palette-neutral-20', palette.neutral[20]),
+ onSurfaceVariant: getCssVar('ref-palette-neutralVariant-30', palette.neutralVariant[30]),
+ onSurface: getCssVar('ref-palette-neutral-10', palette.neutral[10]),
+ surfaceVariant: getCssVar('ref-palette-neutralVariant-90', palette.neutralVariant[90]),
+ surface: getCssVar('ref-palette-neutral-99', palette.neutral[99]),
+ onSecondaryContainer: getCssVar('ref-palette-secondary-10', palette.secondary[10]),
+ onSecondary: getCssVar('ref-palette-secondary-100', palette.secondary[100]),
+ secondaryContainer: getCssVar('ref-palette-secondary-90', palette.secondary[90]),
+ secondary: getCssVar('ref-palette-secondary-40', palette.secondary[40]),
+ inversePrimary: getCssVar('ref-palette-primary-80', palette.primary[80]),
+ onPrimaryContainer: getCssVar('ref-palette-primary-10', palette.primary[10]),
+ onPrimary: getCssVar('ref-palette-primary-100', palette.primary[100]),
+ primaryContainer: getCssVar('ref-palette-primary-90', palette.primary[90]),
+ primary: getCssVar('ref-palette-primary-40', palette.primary[40]),
+});
+
+export default createLightColorScheme;
diff --git a/packages/mui-material-next/src/styles/defaultTheme.ts b/packages/mui-material-next/src/styles/defaultTheme.ts
new file mode 100644
index 00000000000000..394ec08f05c481
--- /dev/null
+++ b/packages/mui-material-next/src/styles/defaultTheme.ts
@@ -0,0 +1,81 @@
+import { deepmerge } from '@mui/utils';
+import extendTheme from './extendTheme';
+import type { Theme, CssVarsThemeOptions, ColorSystemOptions } from './Theme.types';
+
+export const getThemeWithVars = (
+ themeInput?: Omit & ColorSystemOptions,
+) => {
+ const {
+ colorSchemes,
+ opacity,
+ overlays,
+ shape,
+ md3,
+ ref,
+ sys,
+ palette: paletteInput,
+ ...restTheme
+ } = extendTheme(themeInput);
+ const colorSchemePalette = deepmerge(
+ colorSchemes[paletteInput?.colorScheme || 'light'].palette,
+ paletteInput,
+ );
+
+ const {
+ mode = 'light',
+ colorScheme = 'light',
+ // @ts-ignore md3 specific token
+ ref: colorSchemeRef,
+ // @ts-ignore md3 specific token
+ sys: colorSchemeSys,
+ ...palette
+ } = colorSchemePalette;
+
+ return {
+ opacity,
+ overlays,
+ shape,
+ md3,
+ ref: {
+ ...ref,
+ ...colorSchemeRef,
+ },
+ sys: {
+ ...sys,
+ ...colorSchemeSys,
+ },
+ ...restTheme,
+ colorSchemes: {
+ ...colorSchemes,
+ [colorScheme]: {
+ palette,
+ ref: colorSchemeRef,
+ sys: colorSchemeSys,
+ },
+ },
+ palette: {
+ ...palette,
+ mode,
+ colorScheme,
+ },
+ vars: {
+ opacity,
+ overlays,
+ shape,
+ ref: {
+ ...ref,
+ ...colorSchemeRef,
+ },
+ sys: {
+ ...sys,
+ ...colorSchemeSys,
+ },
+ md3,
+ palette,
+ },
+ } as unknown as Theme;
+};
+
+const defaultTheme = getThemeWithVars();
+
+export default defaultTheme;
diff --git a/packages/mui-material-next/src/styles/extendTheme.ts b/packages/mui-material-next/src/styles/extendTheme.ts
new file mode 100644
index 00000000000000..90df3bac3eefaf
--- /dev/null
+++ b/packages/mui-material-next/src/styles/extendTheme.ts
@@ -0,0 +1,424 @@
+import { deepmerge } from '@mui/utils';
+import {
+ colorChannel,
+ alpha,
+ darken,
+ lighten,
+ emphasize,
+ unstable_createGetCssVar as systemCreateGetCssVar,
+} from '@mui/system';
+import {
+ createTheme as createThemeWithoutVars,
+ getOverlayAlpha,
+ SupportedColorScheme,
+ ColorSystem as MD2ColorSystem,
+ Overlays,
+} from '@mui/material/styles';
+import { Theme, MD3Palettes, MD3ColorSchemeTokens, CssVarsThemeOptions } from './Theme.types';
+import md3CommonPalette from './palette';
+import createMd3LightColorScheme from './createLightColorScheme';
+import createMd3DarkColorScheme from './createDarkColorScheme';
+import md3Typescale from './typescale';
+import md3Typeface from './typeface';
+import md3State from './states';
+
+const defaultLightOverlays: Overlays = [...Array(25)].map(() => undefined) as Overlays;
+const defaultDarkOverlays: Overlays = [...Array(25)].map((_, index) => {
+ if (index === 0) {
+ return undefined;
+ }
+ const overlay = getOverlayAlpha(index);
+ return `linear-gradient(rgba(255 255 255 / ${overlay}), rgba(255 255 255 / ${overlay}))`;
+}) as Overlays;
+
+function assignNode(obj: any, keys: string[]) {
+ keys.forEach((k) => {
+ if (!obj[k]) {
+ obj[k] = {};
+ }
+ });
+}
+
+function setColor(obj: any, key: string, defaultValue: any) {
+ obj[key] = obj[key] || defaultValue;
+}
+
+export const createGetCssVar = (cssVarPrefix = 'mui') => systemCreateGetCssVar(cssVarPrefix);
+
+export default function extendTheme(options: CssVarsThemeOptions = {}, ...args: any[]) {
+ const { colorSchemes: colorSchemesInput = {}, cssVarPrefix = 'mui', ...input } = options;
+ const getCssVar = createGetCssVar(cssVarPrefix);
+
+ const md3LightColors = createMd3LightColorScheme(getCssVar, md3CommonPalette);
+ const md3DarkColors = createMd3DarkColorScheme(getCssVar, md3CommonPalette);
+
+ const {
+ palette: lightPalette,
+ // @ts-ignore - sys is md3 specific token
+ sys: lightSys,
+ // @ts-ignore - ref is md3 specific token
+ ref: lightRef,
+ ...muiTheme
+ } = createThemeWithoutVars({
+ ...input,
+ // Material You specific tokens
+ // @ts-ignore - it's fine, everything that is not supported will be spread
+ useMaterialYou: true,
+ ref: {
+ ...input.ref,
+ typeface: { ...md3Typeface, ...input.ref?.typeface },
+ palette: deepmerge(md3CommonPalette, colorSchemesInput.light?.ref?.palette),
+ },
+ sys: {
+ ...input.sys,
+ typescale: { ...md3Typescale, ...input.sys?.typescale },
+ state: { ...md3State, ...input.sys?.state },
+ color: { ...md3LightColors, ...colorSchemesInput.light?.sys?.color },
+ },
+ md3: {
+ shape: {
+ borderRadius: 100,
+ ...input?.shape,
+ },
+ },
+ palette: {
+ ...(colorSchemesInput.light && colorSchemesInput.light?.palette),
+ },
+ });
+
+ const {
+ palette: darkPalette,
+ // @ts-ignore sys is md3 specific tokens
+ sys: darkSys,
+ // @ts-ignore ref is md3 specific tokens
+ ref: darkRef,
+ } = createThemeWithoutVars({
+ palette: {
+ mode: 'dark',
+ ...colorSchemesInput.dark?.palette,
+ },
+ // @ts-ignore - it's fine, everything that is not supported will be spread
+ ref: {
+ ...input.ref,
+ typeface: { ...md3Typeface, ...input.ref?.typeface },
+ palette: deepmerge(md3CommonPalette, colorSchemesInput.dark?.ref?.palette),
+ },
+ sys: {
+ ...input.sys,
+ typescale: { ...md3Typescale, ...input.sys?.typescale },
+ state: { ...md3State, ...input.sys?.state },
+ color: { ...md3DarkColors, ...colorSchemesInput.dark?.sys?.color },
+ },
+ });
+
+ const { color: lightSysColor } = lightSys;
+ const { palette: lightRefPalette } = lightRef;
+
+ const { color: darkSysColor } = darkSys;
+ const { palette: darkRefPalette } = darkRef;
+
+ let theme: Theme = {
+ ...muiTheme,
+ cssVarPrefix,
+ getCssVar,
+ sys: lightSys,
+ ref: lightRef,
+ colorSchemes: {
+ ...colorSchemesInput,
+ light: {
+ ...colorSchemesInput.light,
+ // @ts-ignore they are added below
+ palette: lightPalette,
+ opacity: {
+ inputPlaceholder: 0.42,
+ inputUnderline: 0.42,
+ switchTrackDisabled: 0.12,
+ switchTrack: 0.38,
+ ...colorSchemesInput.light?.opacity,
+ },
+ overlays: colorSchemesInput.light?.overlays || defaultLightOverlays,
+ sys: { color: lightSysColor },
+ ref: { palette: lightRefPalette },
+ },
+ dark: {
+ ...colorSchemesInput.dark,
+ // @ts-ignore they are added below
+ palette: darkPalette,
+ opacity: {
+ inputPlaceholder: 0.5,
+ inputUnderline: 0.7,
+ switchTrackDisabled: 0.2,
+ switchTrack: 0.3,
+ ...colorSchemesInput.dark?.opacity,
+ },
+ overlays: colorSchemesInput.dark?.overlays || defaultDarkOverlays,
+ sys: { color: darkSysColor },
+ ref: { palette: darkRefPalette },
+ },
+ },
+ };
+
+ Object.keys(theme.colorSchemes).forEach((key) => {
+ const palette = theme.colorSchemes[key as SupportedColorScheme]
+ .palette as MD2ColorSystem['palette'] & {
+ md3: MD3Palettes & { colors: MD3ColorSchemeTokens };
+ };
+
+ // @ts-ignore sys is md3 specific token
+ const colorSchemeSys = theme.colorSchemes[key as SupportedColorScheme].sys;
+ // @ts-ignore ref is md3 specific token
+ const colorSchemeRef = theme.colorSchemes[key as SupportedColorScheme].ref;
+
+ // attach black & white channels to common node
+ if (key === 'light') {
+ setColor(palette.common, 'background', '#fff');
+ setColor(palette.common, 'onBackground', '#000');
+ } else {
+ setColor(palette.common, 'background', '#000');
+ setColor(palette.common, 'onBackground', '#fff');
+ }
+
+ // assign component variables
+ assignNode(palette, [
+ 'Alert',
+ 'AppBar',
+ 'Avatar',
+ 'Chip',
+ 'FilledInput',
+ 'LinearProgress',
+ 'Skeleton',
+ 'Slider',
+ 'SnackbarContent',
+ 'SpeedDialAction',
+ 'StepConnector',
+ 'StepContent',
+ 'Switch',
+ 'TableCell',
+ 'Tooltip',
+ ]);
+ if (key === 'light') {
+ setColor(palette.Alert, 'errorColor', darken(palette.error.light, 0.6));
+ setColor(palette.Alert, 'infoColor', darken(palette.info.light, 0.6));
+ setColor(palette.Alert, 'successColor', darken(palette.success.light, 0.6));
+ setColor(palette.Alert, 'warningColor', darken(palette.warning.light, 0.6));
+ setColor(palette.Alert, 'errorFilledBg', getCssVar('palette-error-main'));
+ setColor(palette.Alert, 'infoFilledBg', getCssVar('palette-info-main'));
+ setColor(palette.Alert, 'successFilledBg', getCssVar('palette-success-main'));
+ setColor(palette.Alert, 'warningFilledBg', getCssVar('palette-warning-main'));
+ setColor(palette.Alert, 'errorFilledColor', lightPalette.getContrastText(palette.error.main));
+ setColor(palette.Alert, 'infoFilledColor', lightPalette.getContrastText(palette.info.main));
+ setColor(
+ palette.Alert,
+ 'successFilledColor',
+ lightPalette.getContrastText(palette.success.main),
+ );
+ setColor(
+ palette.Alert,
+ 'warningFilledColor',
+ lightPalette.getContrastText(palette.warning.main),
+ );
+ setColor(palette.Alert, 'errorStandardBg', lighten(palette.error.light, 0.9));
+ setColor(palette.Alert, 'infoStandardBg', lighten(palette.info.light, 0.9));
+ setColor(palette.Alert, 'successStandardBg', lighten(palette.success.light, 0.9));
+ setColor(palette.Alert, 'warningStandardBg', lighten(palette.warning.light, 0.9));
+ setColor(palette.Alert, 'errorIconColor', getCssVar('palette-error-light'));
+ setColor(palette.Alert, 'infoIconColor', getCssVar('palette-info-light'));
+ setColor(palette.Alert, 'successIconColor', getCssVar('palette-success-light'));
+ setColor(palette.Alert, 'warningIconColor', getCssVar('palette-warning-light'));
+ setColor(palette.AppBar, 'defaultBg', getCssVar('palette-grey-100'));
+ setColor(palette.Avatar, 'defaultBg', getCssVar('palette-grey-400'));
+ setColor(palette.Chip, 'defaultBorder', getCssVar('palette-grey-400'));
+ setColor(palette.Chip, 'defaultAvatarColor', getCssVar('palette-grey-700'));
+ setColor(palette.Chip, 'defaultIconColor', getCssVar('palette-grey-700'));
+ setColor(palette.FilledInput, 'bg', 'rgba(0, 0, 0, 0.06)');
+ setColor(palette.FilledInput, 'hoverBg', 'rgba(0, 0, 0, 0.09)');
+ setColor(palette.FilledInput, 'disabledBg', 'rgba(0, 0, 0, 0.12)');
+ setColor(palette.LinearProgress, 'primaryBg', lighten(palette.primary.main, 0.62));
+ setColor(palette.LinearProgress, 'secondaryBg', lighten(palette.secondary.main, 0.62));
+ setColor(palette.LinearProgress, 'errorBg', lighten(palette.error.main, 0.62));
+ setColor(palette.LinearProgress, 'infoBg', lighten(palette.info.main, 0.62));
+ setColor(palette.LinearProgress, 'successBg', lighten(palette.success.main, 0.62));
+ setColor(palette.LinearProgress, 'warningBg', lighten(palette.warning.main, 0.62));
+ setColor(palette.Skeleton, 'bg', `rgba(${getCssVar('palette-text-primaryChannel')} / 0.11)`);
+ setColor(palette.Slider, 'primaryTrack', lighten(palette.primary.main, 0.62));
+ setColor(palette.Slider, 'secondaryTrack', lighten(palette.secondary.main, 0.62));
+ setColor(palette.Slider, 'errorTrack', lighten(palette.error.main, 0.62));
+ setColor(palette.Slider, 'infoTrack', lighten(palette.info.main, 0.62));
+ setColor(palette.Slider, 'successTrack', lighten(palette.success.main, 0.62));
+ setColor(palette.Slider, 'warningTrack', lighten(palette.warning.main, 0.62));
+ const snackbarContentBackground = emphasize(palette.background.default, 0.8);
+ setColor(palette.SnackbarContent, 'bg', snackbarContentBackground);
+ setColor(
+ palette.SnackbarContent,
+ 'color',
+ lightPalette.getContrastText(snackbarContentBackground),
+ );
+ setColor(palette.SpeedDialAction, 'fabHoverBg', emphasize(palette.background.paper, 0.15));
+ setColor(palette.StepConnector, 'border', getCssVar('palette-grey-400'));
+ setColor(palette.StepContent, 'border', getCssVar('palette-grey-400'));
+ setColor(palette.Switch, 'defaultColor', getCssVar('palette-common-white'));
+ setColor(palette.Switch, 'defaultDisabledColor', getCssVar('palette-grey-100'));
+ setColor(palette.Switch, 'primaryDisabledColor', lighten(palette.primary.main, 0.62));
+ setColor(palette.Switch, 'secondaryDisabledColor', lighten(palette.secondary.main, 0.62));
+ setColor(palette.Switch, 'errorDisabledColor', lighten(palette.error.main, 0.62));
+ setColor(palette.Switch, 'infoDisabledColor', lighten(palette.info.main, 0.62));
+ setColor(palette.Switch, 'successDisabledColor', lighten(palette.success.main, 0.62));
+ setColor(palette.Switch, 'warningDisabledColor', lighten(palette.warning.main, 0.62));
+ setColor(palette.TableCell, 'border', lighten(alpha(palette.divider, 1), 0.88));
+ setColor(palette.Tooltip, 'bg', alpha(palette.grey[700], 0.92));
+ } else {
+ setColor(palette.Alert, 'errorColor', lighten(palette.error.light, 0.6));
+ setColor(palette.Alert, 'infoColor', lighten(palette.info.light, 0.6));
+ setColor(palette.Alert, 'successColor', lighten(palette.success.light, 0.6));
+ setColor(palette.Alert, 'warningColor', lighten(palette.warning.light, 0.6));
+ setColor(palette.Alert, 'errorFilledBg', getCssVar('palette-error-dark'));
+ setColor(palette.Alert, 'infoFilledBg', getCssVar('palette-info-dark'));
+ setColor(palette.Alert, 'successFilledBg', getCssVar('palette-success-dark'));
+ setColor(palette.Alert, 'warningFilledBg', getCssVar('palette-warning-dark'));
+ setColor(palette.Alert, 'errorFilledColor', darkPalette.getContrastText(palette.error.dark));
+ setColor(palette.Alert, 'infoFilledColor', darkPalette.getContrastText(palette.info.dark));
+ setColor(
+ palette.Alert,
+ 'successFilledColor',
+ darkPalette.getContrastText(palette.success.dark),
+ );
+ setColor(
+ palette.Alert,
+ 'warningFilledColor',
+ darkPalette.getContrastText(palette.warning.dark),
+ );
+ setColor(palette.Alert, 'errorStandardBg', darken(palette.error.light, 0.9));
+ setColor(palette.Alert, 'infoStandardBg', darken(palette.info.light, 0.9));
+ setColor(palette.Alert, 'successStandardBg', darken(palette.success.light, 0.9));
+ setColor(palette.Alert, 'warningStandardBg', darken(palette.warning.light, 0.9));
+ setColor(palette.Alert, 'errorIconColor', getCssVar('palette-error-main'));
+ setColor(palette.Alert, 'infoIconColor', getCssVar('palette-info-main'));
+ setColor(palette.Alert, 'successIconColor', getCssVar('palette-success-main'));
+ setColor(palette.Alert, 'warningIconColor', getCssVar('palette-warning-main'));
+ setColor(palette.AppBar, 'defaultBg', getCssVar('palette-grey-900'));
+ setColor(palette.AppBar, 'darkBg', getCssVar('palette-background-paper')); // specific for dark mode
+ setColor(palette.AppBar, 'darkColor', getCssVar('palette-text-primary')); // specific for dark mode
+ setColor(palette.Avatar, 'defaultBg', getCssVar('palette-grey-600'));
+ setColor(palette.Chip, 'defaultBorder', getCssVar('palette-grey-700'));
+ setColor(palette.Chip, 'defaultAvatarColor', getCssVar('palette-grey-300'));
+ setColor(palette.Chip, 'defaultIconColor', getCssVar('palette-grey-300'));
+ setColor(palette.FilledInput, 'bg', 'rgba(255, 255, 255, 0.09)');
+ setColor(palette.FilledInput, 'hoverBg', 'rgba(255, 255, 255, 0.13)');
+ setColor(palette.FilledInput, 'disabledBg', 'rgba(255, 255, 255, 0.12)');
+ setColor(palette.LinearProgress, 'primaryBg', darken(palette.primary.main, 0.5));
+ setColor(palette.LinearProgress, 'secondaryBg', darken(palette.secondary.main, 0.5));
+ setColor(palette.LinearProgress, 'errorBg', darken(palette.error.main, 0.5));
+ setColor(palette.LinearProgress, 'infoBg', darken(palette.info.main, 0.5));
+ setColor(palette.LinearProgress, 'successBg', darken(palette.success.main, 0.5));
+ setColor(palette.LinearProgress, 'warningBg', darken(palette.warning.main, 0.5));
+ setColor(palette.Skeleton, 'bg', `rgba(${getCssVar('palette-text-primaryChannel')} / 0.13)`);
+ setColor(palette.Slider, 'primaryTrack', darken(palette.primary.main, 0.5));
+ setColor(palette.Slider, 'secondaryTrack', darken(palette.secondary.main, 0.5));
+ setColor(palette.Slider, 'errorTrack', darken(palette.error.main, 0.5));
+ setColor(palette.Slider, 'infoTrack', darken(palette.info.main, 0.5));
+ setColor(palette.Slider, 'successTrack', darken(palette.success.main, 0.5));
+ setColor(palette.Slider, 'warningTrack', darken(palette.warning.main, 0.5));
+ const snackbarContentBackground = emphasize(palette.background.default, 0.98);
+ setColor(palette.SnackbarContent, 'bg', snackbarContentBackground);
+ setColor(
+ palette.SnackbarContent,
+ 'color',
+ darkPalette.getContrastText(snackbarContentBackground),
+ );
+ setColor(palette.SpeedDialAction, 'fabHoverBg', emphasize(palette.background.paper, 0.15));
+ setColor(palette.StepConnector, 'border', getCssVar('palette-grey-600'));
+ setColor(palette.StepContent, 'border', getCssVar('palette-grey-600'));
+ setColor(palette.Switch, 'defaultColor', getCssVar('palette-grey-300'));
+ setColor(palette.Switch, 'defaultDisabledColor', getCssVar('palette-grey-600'));
+ setColor(palette.Switch, 'primaryDisabledColor', darken(palette.primary.main, 0.55));
+ setColor(palette.Switch, 'secondaryDisabledColor', darken(palette.secondary.main, 0.55));
+ setColor(palette.Switch, 'errorDisabledColor', darken(palette.error.main, 0.55));
+ setColor(palette.Switch, 'infoDisabledColor', darken(palette.info.main, 0.55));
+ setColor(palette.Switch, 'successDisabledColor', darken(palette.success.main, 0.55));
+ setColor(palette.Switch, 'warningDisabledColor', darken(palette.warning.main, 0.55));
+ setColor(palette.TableCell, 'border', darken(alpha(palette.divider, 1), 0.68));
+ setColor(palette.Tooltip, 'bg', alpha(palette.grey[700], 0.92));
+ }
+
+ palette.common.backgroundChannel = colorChannel(palette.common.background);
+ palette.common.onBackgroundChannel = colorChannel(palette.common.onBackground);
+
+ palette.dividerChannel = colorChannel(palette.divider);
+
+ Object.keys(palette).forEach((c) => {
+ const color = c as keyof MD2ColorSystem['palette'];
+ const colors: any = palette[color];
+
+ // Color palettes: primary, secondary, error, info, success, and warning
+ if (colors.main) {
+ // @ts-ignore
+ palette[color].mainChannel = colorChannel(colors.main);
+ }
+ if (colors.light) {
+ // @ts-ignore
+ palette[color].lightChannel = colorChannel(colors.light);
+ }
+ if (colors.dark) {
+ // @ts-ignore
+ palette[color].darkChannel = colorChannel(colors.dark);
+ }
+ if (colors.contrastText) {
+ // @ts-ignore
+ palette[color].contrastTextChannel = colorChannel(colors.contrastText);
+ }
+
+ // Text colors: text.primary, text.secondary
+ if (colors.primary && typeof colors.primary === 'string') {
+ // @ts-ignore
+ palette[color].primaryChannel = colorChannel(colors.primary);
+ }
+ if (colors.secondary && typeof colors.primary === 'string') {
+ // @ts-ignore
+ palette[color].secondaryChannel = colorChannel(colors.secondary);
+ }
+
+ // Action colors: action.active, action.selected
+ if (colors.active) {
+ // @ts-ignore
+ palette[color].activeChannel = colorChannel(colors.active);
+ }
+ if (colors.selected) {
+ // @ts-ignore
+ palette[color].selectedChannel = colorChannel(colors.selected);
+ }
+ });
+
+ // Material You specific channels
+ if (key === 'light') {
+ colorSchemeSys.color.primaryChannel = colorChannel(colorSchemeRef.palette.primary['40']);
+ colorSchemeSys.color.onPrimaryChannel = colorChannel(colorSchemeRef.palette.primary['100']);
+ colorSchemeSys.color.secondaryChannel = colorChannel(colorSchemeRef.palette.secondary['40']);
+ colorSchemeSys.color.onSecondaryChannel = colorChannel(
+ colorSchemeRef.palette.secondary['100'],
+ );
+ colorSchemeSys.color.tertiaryChannel = colorChannel(colorSchemeRef.palette.tertiary['40']);
+ colorSchemeSys.color.onTertiaryChannel = colorChannel(colorSchemeRef.palette.tertiary['100']);
+ colorSchemeSys.color.secondaryContainerChannel = colorChannel(
+ colorSchemeRef.palette.secondary['90'],
+ );
+ colorSchemeSys.color.onSurfaceChannel = colorChannel(colorSchemeRef.palette.neutral['10']);
+ } else {
+ colorSchemeSys.color.primaryChannel = colorChannel(colorSchemeRef.palette.primary['80']);
+ colorSchemeSys.color.onPrimaryChannel = colorChannel(colorSchemeRef.palette.primary['20']);
+ colorSchemeSys.color.secondaryChannel = colorChannel(colorSchemeRef.palette.secondary['80']);
+ colorSchemeSys.color.onSecondaryChannel = colorChannel(
+ colorSchemeRef.palette.secondary['20'],
+ );
+ colorSchemeSys.color.tertiaryChannel = colorChannel(colorSchemeRef.palette.tertiary['80']);
+ colorSchemeSys.color.onTertiaryChannel = colorChannel(colorSchemeRef.palette.tertiary['20']);
+ colorSchemeSys.color.secondaryContainerChannel = colorChannel(
+ colorSchemeRef.palette.secondary['30'],
+ );
+ colorSchemeSys.color.onSurfaceChannel = colorChannel(colorSchemeRef.palette.neutral['90']);
+ }
+ });
+
+ theme = args.reduce((acc, argument) => deepmerge(acc, argument), theme);
+
+ return theme;
+}
diff --git a/packages/mui-material-next/src/styles/index.ts b/packages/mui-material-next/src/styles/index.ts
new file mode 100644
index 00000000000000..dc8c6c812476a1
--- /dev/null
+++ b/packages/mui-material-next/src/styles/index.ts
@@ -0,0 +1,6 @@
+export * from './Theme.types';
+export * from './extendTheme';
+export { default as extendTheme } from './extendTheme';
+export { default as styled } from './styled';
+export { default as defaultTheme } from './defaultTheme';
+export * from './CssVarsProvider';
diff --git a/packages/mui-material-next/src/styles/palette.ts b/packages/mui-material-next/src/styles/palette.ts
new file mode 100644
index 00000000000000..d5ebb888545f44
--- /dev/null
+++ b/packages/mui-material-next/src/styles/palette.ts
@@ -0,0 +1,98 @@
+const mdRefPalette = {
+ error: {
+ 0: '#000000',
+ 10: '#410e0b',
+ 20: '#601410',
+ 30: '#8c1d18',
+ 40: '#b3261e',
+ 50: '#dc362e',
+ 60: '#e46962',
+ 70: '#ec928e',
+ 80: '#f2b8b5',
+ 90: '#f9dedc',
+ 95: '#fceeee',
+ 99: '#fffbf9',
+ 100: '#ffffff',
+ },
+ tertiary: {
+ 0: '#000000',
+ 10: '#31111d',
+ 20: '#492532',
+ 30: '#633b48',
+ 40: '#7d5260',
+ 50: '#986977',
+ 60: '#b58392',
+ 70: '#d29dac',
+ 80: '#efb8c8',
+ 90: '#ffd8e4',
+ 95: '#ffecf1',
+ 99: '#fffbfa',
+ 100: '#ffffff',
+ },
+ secondary: {
+ 0: '#000000',
+ 10: '#1d192b',
+ 20: '#332d41',
+ 30: '#4a4458',
+ 40: '#625b71',
+ 50: '#7a7289',
+ 60: '#958da5',
+ 70: '#b0a7c0',
+ 80: '#ccc2dc',
+ 90: '#e8def8',
+ 95: '#f6edff',
+ 99: '#fffbfe',
+ 100: '#ffffff',
+ },
+ primary: {
+ 0: '#000000',
+ 10: '#21005d',
+ 20: '#381e72',
+ 30: '#4f378b',
+ 40: '#6750a4',
+ 50: '#7f67be',
+ 60: '#9a82db',
+ 70: '#b69df8',
+ 80: '#d0bcff',
+ 90: '#eaddff',
+ 95: '#f6edff',
+ 99: '#fffbfe',
+ 100: '#ffffff',
+ },
+ neutralVariant: {
+ 0: '#000000',
+ 10: '#1d1a22',
+ 20: '#322f37',
+ 30: '#49454f',
+ 40: '#605d66',
+ 50: '#79747e',
+ 60: '#938f99',
+ 70: '#aea9b4',
+ 80: '#cac4d0',
+ 90: '#e7e0ec',
+ 95: '#f5eefa',
+ 99: '#fffbfe',
+ 100: '#ffffff',
+ },
+ neutral: {
+ 0: '#000000',
+ 10: '#1c1b1f',
+ 20: '#313033',
+ 30: '#484649',
+ 40: '#605d62',
+ 50: '#787579',
+ 60: '#939094',
+ 70: '#aeaaae',
+ 80: '#c9c5ca',
+ 90: '#e6e1e5',
+ 95: '#f4eff4',
+ 99: '#fffbfe',
+ 100: '#ffffff',
+ },
+ common: {
+ black: '#000000',
+ white: '#ffffff',
+ },
+};
+
+export default mdRefPalette;
diff --git a/packages/mui-material-next/src/styles/states.ts b/packages/mui-material-next/src/styles/states.ts
new file mode 100644
index 00000000000000..13f07fc543a7d2
--- /dev/null
+++ b/packages/mui-material-next/src/styles/states.ts
@@ -0,0 +1,16 @@
+const mdSysState = {
+ hover: {
+ stateLayerOpacity: 0.08,
+ },
+ focus: {
+ stateLayerOpacity: 0.12,
+ },
+ pressed: {
+ stateLayerOpacity: 0.12,
+ },
+ dragged: {
+ stateLayerOpacity: 0.16,
+ },
+};
+
+export default mdSysState;
diff --git a/packages/mui-material-next/src/styles/styleFunctionSx.ts b/packages/mui-material-next/src/styles/styleFunctionSx.ts
new file mode 100644
index 00000000000000..9160b29d1bd000
--- /dev/null
+++ b/packages/mui-material-next/src/styles/styleFunctionSx.ts
@@ -0,0 +1,153 @@
+import {
+ Interpolation,
+ unstable_createStyleFunctionSx,
+ compose,
+ getPath,
+ getStyleValue as getValue,
+ display,
+ flexbox,
+ grid,
+ positions,
+ sizing,
+ spacing,
+ border,
+ borderTop,
+ borderRight,
+ borderBottom,
+ borderLeft,
+ borderTopColor,
+ borderRightColor,
+ borderBottomColor,
+ borderLeftColor,
+ createUnaryUnit,
+ handleBreakpoints,
+ responsivePropType,
+ typography,
+} from '@mui/system';
+import { SxProps, Theme } from './Theme.types';
+
+interface PaletteStyleOptions {
+ prop: string;
+ cssProperty?: string | boolean;
+}
+
+function paletteStyle(options: PaletteStyleOptions = { prop: 'color' }) {
+ const { prop, cssProperty = options.prop } = options;
+
+ const fn = (props: Record) => {
+ if (props[prop] == null) {
+ return null;
+ }
+
+ const propValue: any = props[prop];
+ const theme = props.theme;
+ const colorThemeMapping = getPath(theme, 'sys.color') || {};
+ const paletteThemeMapping = getPath(theme, 'ref.palette') || {};
+
+ const styleFromPropValue = (propValueFinal: any) => {
+ // check the value in the color mapping first
+ let value = getValue(colorThemeMapping, undefined, propValueFinal);
+
+ if (propValueFinal === value) {
+ // haven't found value in colors mapping, so we are checking in the palette mapping
+ value = getValue(paletteThemeMapping, undefined, propValueFinal);
+ }
+
+ if (cssProperty === false) {
+ return value;
+ }
+
+ return {
+ [cssProperty as string]: value,
+ };
+ };
+
+ return handleBreakpoints(props, propValue, styleFromPropValue);
+ };
+
+ fn.propTypes =
+ process.env.NODE_ENV !== 'production'
+ ? {
+ [prop]: responsivePropType,
+ }
+ : {};
+
+ fn.filterProps = [prop];
+
+ return fn;
+}
+
+// Palette values should reference the color tokens
+export const color = paletteStyle({
+ prop: 'color',
+});
+
+export const bgcolor = paletteStyle({
+ prop: 'bgcolor',
+ cssProperty: 'backgroundColor',
+});
+
+export const backgroundColor = paletteStyle({
+ prop: 'backgroundColor',
+});
+
+const palette = compose(color, bgcolor, backgroundColor);
+
+// Border radius should mapa to md3.shape
+export const borderRadius = (props: any) => {
+ if (props.borderRadius !== undefined && props.borderRadius !== null) {
+ const transformer = createUnaryUnit(props.theme, 'md3.shape.borderRadius', 4, 'borderRadius');
+ const styleFromPropValue = (propValue: any) => ({
+ // @ts-ignore
+ borderRadius: getValue(transformer, propValue),
+ });
+ return handleBreakpoints(props, props.borderRadius, styleFromPropValue);
+ }
+
+ return null;
+};
+
+borderRadius.propTypes =
+ process.env.NODE_ENV !== 'production' ? { borderRadius: responsivePropType } : {};
+
+const borderColor = paletteStyle({
+ prop: 'borderColor',
+});
+
+borderRadius.filterProps = ['borderRadius'];
+const borders = compose(
+ border,
+ borderTop,
+ borderRight,
+ borderBottom,
+ borderLeft,
+ borderColor,
+ borderTopColor,
+ borderRightColor,
+ borderBottomColor,
+ borderLeftColor,
+ borderRadius,
+);
+
+const styleFunctionMapping = {
+ borders,
+ display,
+ flexbox,
+ grid,
+ positions,
+ palette,
+ sizing,
+ spacing,
+ typography,
+};
+
+const styleFunctionSx = unstable_createStyleFunctionSx(styleFunctionMapping);
+
+styleFunctionSx.filterProps = ['sx'];
+
+export const sx = (styles: SxProps) => {
+ return ({ theme }: { theme: Theme }) =>
+ styleFunctionSx({ sx: styles, theme }) as Interpolation<{ theme: Theme }>;
+};
+
+export default styleFunctionSx;
diff --git a/packages/mui-material-next/src/styles/styled.ts b/packages/mui-material-next/src/styles/styled.ts
new file mode 100644
index 00000000000000..2e8f6c5a463872
--- /dev/null
+++ b/packages/mui-material-next/src/styles/styled.ts
@@ -0,0 +1,8 @@
+import { createStyled } from '@mui/system';
+import { Theme } from './Theme.types';
+import defaultTheme from './defaultTheme';
+import styleFunctionSx from './styleFunctionSx';
+
+const styled = createStyled({ defaultTheme, styleFunctionSx });
+
+export default styled;
diff --git a/packages/mui-material-next/src/styles/typeface.ts b/packages/mui-material-next/src/styles/typeface.ts
new file mode 100644
index 00000000000000..31ff63f106ccaa
--- /dev/null
+++ b/packages/mui-material-next/src/styles/typeface.ts
@@ -0,0 +1,11 @@
+const mdRefTypeface = {
+ plain: 'Roboto',
+ brand: 'Roboto',
+ weight: {
+ bold: '700',
+ medium: '500',
+ regular: '400',
+ },
+};
+
+export default mdRefTypeface;
diff --git a/packages/mui-material-next/src/styles/typescale.ts b/packages/mui-material-next/src/styles/typescale.ts
new file mode 100644
index 00000000000000..89d0aca6fd0fef
--- /dev/null
+++ b/packages/mui-material-next/src/styles/typescale.ts
@@ -0,0 +1,84 @@
+const mdSysTypescale = {
+ label: {
+ small: {
+ family: 'Roboto',
+ weight: '500',
+ tracking: 0.5,
+ },
+ medium: {
+ family: 'Roboto',
+ weight: '500',
+ tracking: 0.5,
+ },
+ large: {
+ family: 'Roboto',
+ weight: '500',
+ lineHeight: 20,
+ size: 14,
+ tracking: 0.1,
+ },
+ },
+ body: {
+ small: {
+ family: 'Roboto',
+ weight: '400',
+ tracking: 0.4,
+ },
+ medium: {
+ family: 'Roboto',
+ weight: '400',
+ tracking: 0.25,
+ },
+ large: {
+ family: 'Roboto',
+ weight: '400',
+ tracking: 0.15,
+ },
+ },
+ title: {
+ small: {
+ family: 'Roboto',
+ weight: '500',
+ tracking: 0.1,
+ },
+ medium: {
+ family: 'Roboto',
+ weight: '500',
+ tracking: 0.15,
+ },
+ large: {
+ family: 'Roboto',
+ weight: '400',
+ },
+ },
+ headline: {
+ small: {
+ family: 'Roboto',
+ weight: '400',
+ },
+ medium: {
+ family: 'Roboto',
+ weight: '400',
+ },
+ large: {
+ family: 'Roboto',
+ weight: '400',
+ },
+ },
+ display: {
+ small: {
+ family: 'Roboto',
+ weight: '400',
+ },
+ medium: {
+ family: 'Roboto',
+ weight: '400',
+ },
+ large: {
+ family: 'Roboto',
+ weight: '400',
+ },
+ },
+};
+
+export default mdSysTypescale;
diff --git a/packages/mui-material/src/Paper/Paper.js b/packages/mui-material/src/Paper/Paper.js
index deab7b6fa7cb80..515f641d02bdcf 100644
--- a/packages/mui-material/src/Paper/Paper.js
+++ b/packages/mui-material/src/Paper/Paper.js
@@ -5,21 +5,11 @@ import { chainPropTypes, integerPropType } from '@mui/utils';
import { unstable_composeClasses as composeClasses } from '@mui/base';
import { alpha } from '@mui/system';
import styled from '../styles/styled';
+import getOverlayAlpha from '../styles/getOverlayAlpha';
import useThemeProps from '../styles/useThemeProps';
import useTheme from '../styles/useTheme';
import { getPaperUtilityClass } from './paperClasses';
-// Inspired by https://github.com/material-components/material-components-ios/blob/bca36107405594d5b7b16265a5b0ed698f85a5ee/components/Elevation/src/UIColor%2BMaterialElevation.m#L61
-export const getOverlayAlpha = (elevation) => {
- let alphaValue;
- if (elevation < 1) {
- alphaValue = 5.11916 * elevation ** 2;
- } else {
- alphaValue = 4.5 * Math.log(elevation + 1) + 2;
- }
- return (alphaValue / 100).toFixed(2);
-};
-
const useUtilityClasses = (ownerState) => {
const { square, elevation, variant, classes } = ownerState;
diff --git a/packages/mui-material/src/styles/experimental_extendTheme.d.ts b/packages/mui-material/src/styles/experimental_extendTheme.d.ts
index 5848c35e7f962e..3ade54bca13098 100644
--- a/packages/mui-material/src/styles/experimental_extendTheme.d.ts
+++ b/packages/mui-material/src/styles/experimental_extendTheme.d.ts
@@ -34,6 +34,8 @@ export type SupportedColorScheme = DefaultColorScheme | ExtendedColorScheme;
export interface Opacity {
inputPlaceholder: number;
inputUnderline: number;
+ switchTrackDisabled: number;
+ switchTrack: number;
}
export type Overlays = [
@@ -389,6 +391,16 @@ export interface CssVarsTheme extends ColorSystem {
vars: ThemeVars;
getCssVar: (field: ThemeCssVar, ...vars: ThemeCssVar[]) => string;
getColorSchemeSelector: (colorScheme: SupportedColorScheme) => string;
+
+ // Default theme tokens
+ spacing: Theme['spacing'];
+ breakpints: Theme['breakpoints'];
+ shape: Theme['shape'];
+ typography: Theme['typography'];
+ transitions: Theme['transitions'];
+ shadows: Theme['shadows'];
+ mixins: Theme['mixins'];
+ zIndex: Theme['zIndex'];
}
/**
diff --git a/packages/mui-material/src/styles/experimental_extendTheme.js b/packages/mui-material/src/styles/experimental_extendTheme.js
index 14ad3990b18de0..85709a0825d77d 100644
--- a/packages/mui-material/src/styles/experimental_extendTheme.js
+++ b/packages/mui-material/src/styles/experimental_extendTheme.js
@@ -8,7 +8,7 @@ import {
unstable_createGetCssVar as systemCreateGetCssVar,
} from '@mui/system';
import createThemeWithoutVars from './createTheme';
-import { getOverlayAlpha } from '../Paper/Paper';
+import getOverlayAlpha from './getOverlayAlpha';
const defaultDarkOverlays = [...Array(25)].map((_, index) => {
if (index === 0) {
diff --git a/packages/mui-material/src/styles/getOverlayAlpha.ts b/packages/mui-material/src/styles/getOverlayAlpha.ts
new file mode 100644
index 00000000000000..c528a101adfe16
--- /dev/null
+++ b/packages/mui-material/src/styles/getOverlayAlpha.ts
@@ -0,0 +1,12 @@
+// Inspired by https://github.com/material-components/material-components-ios/blob/bca36107405594d5b7b16265a5b0ed698f85a5ee/components/Elevation/src/UIColor%2BMaterialElevation.m#L61
+const getOverlayAlpha = (elevation: number) => {
+ let alphaValue;
+ if (elevation < 1) {
+ alphaValue = 5.11916 * elevation ** 2;
+ } else {
+ alphaValue = 4.5 * Math.log(elevation + 1) + 2;
+ }
+ return (alphaValue / 100).toFixed(2);
+};
+
+export default getOverlayAlpha;
diff --git a/packages/mui-material/src/styles/index.d.ts b/packages/mui-material/src/styles/index.d.ts
index 1d6095898de49f..bb2e23b2f89e26 100644
--- a/packages/mui-material/src/styles/index.d.ts
+++ b/packages/mui-material/src/styles/index.d.ts
@@ -128,4 +128,10 @@ export type {
ThemeVars,
ThemeCssVar,
ThemeCssVarOverrides,
+ ColorSystemOptions,
} from './experimental_extendTheme';
+export { default as getOverlayAlpha } from './getOverlayAlpha';
+
+// Private methods for creating parts of the theme
+export { default as private_createTypography } from './createTypography';
+export { default as private_excludeVariablesFromRoot } from './excludeVariablesFromRoot';
diff --git a/packages/mui-material/src/styles/index.js b/packages/mui-material/src/styles/index.js
index ef07e9db94e444..011245f31907b7 100644
--- a/packages/mui-material/src/styles/index.js
+++ b/packages/mui-material/src/styles/index.js
@@ -35,3 +35,8 @@ export { default as withTheme } from './withTheme';
export * from './CssVarsProvider';
export { default as experimental_extendTheme } from './experimental_extendTheme';
+export { default as getOverlayAlpha } from './getOverlayAlpha';
+
+// Private methods for creating parts of the theme
+export { default as private_createTypography } from './createTypography';
+export { default as private_excludeVariablesFromRoot } from './excludeVariablesFromRoot';
diff --git a/packages/mui-system/src/cssVars/createCssVarsProvider.js b/packages/mui-system/src/cssVars/createCssVarsProvider.js
index 933e0e907926c7..076771096c80af 100644
--- a/packages/mui-system/src/cssVars/createCssVarsProvider.js
+++ b/packages/mui-system/src/cssVars/createCssVarsProvider.js
@@ -123,7 +123,7 @@ export default function createCssVarsProvider(options) {
} = cssVarsParser(restThemeProp, { prefix: cssVarPrefix, shouldSkipGeneratingVar });
// 3. Start composing the theme object
- let theme = {
+ const theme = {
...parsedTheme,
components,
colorSchemes,
@@ -149,7 +149,17 @@ export default function createCssVarsProvider(options) {
theme.vars = deepmerge(theme.vars, vars);
if (key === calculatedColorScheme) {
// 4.1 Merge the selected color scheme to the theme
- theme = { ...theme, ...parsedScheme };
+ Object.keys(parsedScheme).forEach((schemeKey) => {
+ if (parsedScheme[schemeKey] && typeof parsedScheme[schemeKey] === 'object') {
+ // shallow merge the 1st level structure of the theme.
+ theme[schemeKey] = {
+ ...theme[schemeKey],
+ ...parsedScheme[schemeKey],
+ };
+ } else {
+ theme[schemeKey] = parsedScheme[schemeKey];
+ }
+ });
if (theme.palette) {
theme.palette.colorScheme = key;
}
diff --git a/packages/mui-system/src/index.d.ts b/packages/mui-system/src/index.d.ts
index c2dc1bdda444e8..a89703928d4a5a 100644
--- a/packages/mui-system/src/index.d.ts
+++ b/packages/mui-system/src/index.d.ts
@@ -165,6 +165,8 @@ export { default as unstable_createCssVarsProvider, CreateCssVarsProviderResult
export { default as unstable_createGetCssVar } from './cssVars/createGetCssVar';
export * from './cssVars';
+export { default as responsivePropType } from './responsivePropType';
+
export { default as createContainer } from './Container/createContainer';
export * from './Container/createContainer';
diff --git a/packages/mui-system/src/index.js b/packages/mui-system/src/index.js
index b6a85cbacdce68..62c21b57d11bef 100644
--- a/packages/mui-system/src/index.js
+++ b/packages/mui-system/src/index.js
@@ -22,7 +22,7 @@ export { default as sizing } from './sizing';
export * from './sizing';
export { default as spacing } from './spacing';
export * from './spacing';
-export { default as style, getPath } from './style';
+export { default as style, getPath, getStyleValue } from './style';
export { default as typography } from './typography';
export * from './typography';
export {
@@ -48,6 +48,7 @@ export * from './colorManipulator';
export { default as ThemeProvider } from './ThemeProvider';
export { default as unstable_createCssVarsProvider } from './cssVars/createCssVarsProvider';
export { default as unstable_createGetCssVar } from './cssVars/createGetCssVar';
+export { default as responsivePropType } from './responsivePropType';
/** ----------------- */
/** Layout components */
diff --git a/packages/mui-system/src/responsivePropType.d.ts b/packages/mui-system/src/responsivePropType.d.ts
new file mode 100644
index 00000000000000..e6d62effc86140
--- /dev/null
+++ b/packages/mui-system/src/responsivePropType.d.ts
@@ -0,0 +1,3 @@
+declare const responsivePropType: object;
+
+export default responsivePropType;
diff --git a/packages/mui-system/src/spacing.d.ts b/packages/mui-system/src/spacing.d.ts
index 9157d8c676d615..d5587dc06beeb9 100644
--- a/packages/mui-system/src/spacing.d.ts
+++ b/packages/mui-system/src/spacing.d.ts
@@ -10,6 +10,20 @@ export function createUnarySpacing(theme: { spacing: Spacing }): Spacin
: // warns in Dev
() => undefined;
+export function createUnaryUnit(
+ theme: { spacing: Spacing },
+ themeKey: string,
+ defaultValue: Spacing,
+ propName: string,
+): Spacing extends number
+ ? (abs: number | string) => number | number
+ : Spacing extends any[]
+ ? (abs: Index | string) => Spacing[Index] | string
+ : Spacing extends (...args: unknown[]) => unknown
+ ? Spacing
+ : // warns in Dev
+ () => undefined;
+
export const margin: SimpleStyleFunction<
| 'm'
| 'mt'
diff --git a/packages/mui-system/src/style.d.ts b/packages/mui-system/src/style.d.ts
index 6cd7220f7ec578..a631aa6c57d4a0 100644
--- a/packages/mui-system/src/style.d.ts
+++ b/packages/mui-system/src/style.d.ts
@@ -17,3 +17,9 @@ export function style(
options: StyleOptions,
): StyleFunction<{ [K in PropKey]?: unknown } & { theme?: Theme }> & { filterProps: string[] };
export function getPath(obj: T, path: string | undefined, checkVars?: boolean): null | unknown;
+export function getStyleValue(
+ themeMapping: object | ((val: any) => any),
+ transform?: (val: any, userVal: any) => any,
+ propValueFinal?: any,
+ userValue?: any,
+): any;
diff --git a/packages/mui-system/src/style.js b/packages/mui-system/src/style.js
index 3886763eae031a..a07880366b47f2 100644
--- a/packages/mui-system/src/style.js
+++ b/packages/mui-system/src/style.js
@@ -24,7 +24,7 @@ export function getPath(obj, path, checkVars = true) {
}, obj);
}
-function getValue(themeMapping, transform, propValueFinal, userValue = propValueFinal) {
+export function getStyleValue(themeMapping, transform, propValueFinal, userValue = propValueFinal) {
let value;
if (typeof themeMapping === 'function') {
@@ -54,11 +54,11 @@ function style(options) {
const theme = props.theme;
const themeMapping = getPath(theme, themeKey) || {};
const styleFromPropValue = (propValueFinal) => {
- let value = getValue(themeMapping, transform, propValueFinal);
+ let value = getStyleValue(themeMapping, transform, propValueFinal);
if (propValueFinal === value && typeof propValueFinal === 'string') {
// Haven't found value
- value = getValue(
+ value = getStyleValue(
themeMapping,
transform,
`${prop}${propValueFinal === 'default' ? '' : capitalize(propValueFinal)}`,
diff --git a/test/regressions/fixtures/ButtonNext/IconLabelButtonsNext.js b/test/regressions/fixtures/ButtonNext/IconLabelButtonsNext.js
index dc7748b6c95ec3..5bf13d5f356586 100644
--- a/test/regressions/fixtures/ButtonNext/IconLabelButtonsNext.js
+++ b/test/regressions/fixtures/ButtonNext/IconLabelButtonsNext.js
@@ -6,19 +6,19 @@ import SendIcon from '@mui/icons-material/Send';
export default function IconLabelButtonsNext() {
return (
- }>
+ }>
Send
- send}>
+ send}>
Send
- }>
+ }>
Send
- }>
+ }>
Send
- }>
+ }>
Send
diff --git a/test/regressions/fixtures/ButtonNext/MultilineButtonNext.js b/test/regressions/fixtures/ButtonNext/MultilineButtonNext.js
index 43a827747edf0b..546607ecc4db32 100644
--- a/test/regressions/fixtures/ButtonNext/MultilineButtonNext.js
+++ b/test/regressions/fixtures/ButtonNext/MultilineButtonNext.js
@@ -3,7 +3,7 @@ import Button from '@mui/material-next/Button';
export default function MultilineButtonNext() {
return (
-
+
{[
'Contained buttons are rectangular-shaped buttons.',
'They may be used inline.',