+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
+ veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
+ commodo consequat.
+
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
+ veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
+ commodo consequat.
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/docs/data/joy/main-features/color-inversion/ColorInversionNavigation.js b/docs/data/joy/main-features/color-inversion/ColorInversionNavigation.js
index 135ee4f88b092a..57596bd9a5438d 100644
--- a/docs/data/joy/main-features/color-inversion/ColorInversionNavigation.js
+++ b/docs/data/joy/main-features/color-inversion/ColorInversionNavigation.js
@@ -86,7 +86,13 @@ export default function ColorInversionNavigation() {
Chat
-
+
5
diff --git a/docs/data/joy/main-features/color-inversion/ColorInversionNavigation.tsx b/docs/data/joy/main-features/color-inversion/ColorInversionNavigation.tsx
index a12eb37f9881a6..9bbfc84c38a28d 100644
--- a/docs/data/joy/main-features/color-inversion/ColorInversionNavigation.tsx
+++ b/docs/data/joy/main-features/color-inversion/ColorInversionNavigation.tsx
@@ -86,7 +86,13 @@ export default function ColorInversionNavigation() {
Chat
-
+
5
diff --git a/docs/data/joy/main-features/color-inversion/ColorInversionOverview.js b/docs/data/joy/main-features/color-inversion/ColorInversionOverview.js
index e13b479c70d6ed..bce6170d920826 100644
--- a/docs/data/joy/main-features/color-inversion/ColorInversionOverview.js
+++ b/docs/data/joy/main-features/color-inversion/ColorInversionOverview.js
@@ -29,9 +29,7 @@ export default function ColorInversionOverview() {
>
-
- Learn how to build super fast websites.
-
+ Learn how to build super fast websites.
}>
Read more
diff --git a/docs/data/joy/main-features/color-inversion/ColorInversionOverview.tsx b/docs/data/joy/main-features/color-inversion/ColorInversionOverview.tsx
index e13b479c70d6ed..bce6170d920826 100644
--- a/docs/data/joy/main-features/color-inversion/ColorInversionOverview.tsx
+++ b/docs/data/joy/main-features/color-inversion/ColorInversionOverview.tsx
@@ -29,9 +29,7 @@ export default function ColorInversionOverview() {
>
-
- Learn how to build super fast websites.
-
+ Learn how to build super fast websites.
}>
Read more
diff --git a/docs/data/joy/main-features/color-inversion/ColorInversionSkip.js b/docs/data/joy/main-features/color-inversion/ColorInversionSkip.js
new file mode 100644
index 00000000000000..cad9a69a1b9556
--- /dev/null
+++ b/docs/data/joy/main-features/color-inversion/ColorInversionSkip.js
@@ -0,0 +1,62 @@
+import * as React from 'react';
+import AspectRatio from '@mui/joy/AspectRatio';
+import Card from '@mui/joy/Card';
+import SvgIcon from '@mui/joy/SvgIcon';
+import IconButton from '@mui/joy/IconButton';
+import Typography from '@mui/joy/Typography';
+import ArrowForward from '@mui/icons-material/ArrowForward';
+
+export default function ColorInversionSkip() {
+ return (
+
+
+
+
+
+
+
+
+
+ Design Thinking
+
+ How to apply design thinking to your problem in order to generate innovative
+ and user-centric solutions.
+
+
+
+
+
+
+ );
+}
diff --git a/docs/data/joy/main-features/color-inversion/ColorInversionSkip.tsx b/docs/data/joy/main-features/color-inversion/ColorInversionSkip.tsx
new file mode 100644
index 00000000000000..cad9a69a1b9556
--- /dev/null
+++ b/docs/data/joy/main-features/color-inversion/ColorInversionSkip.tsx
@@ -0,0 +1,62 @@
+import * as React from 'react';
+import AspectRatio from '@mui/joy/AspectRatio';
+import Card from '@mui/joy/Card';
+import SvgIcon from '@mui/joy/SvgIcon';
+import IconButton from '@mui/joy/IconButton';
+import Typography from '@mui/joy/Typography';
+import ArrowForward from '@mui/icons-material/ArrowForward';
+
+export default function ColorInversionSkip() {
+ return (
+
+
+
+
+
+
+
+
+
+ Design Thinking
+
+ How to apply design thinking to your problem in order to generate innovative
+ and user-centric solutions.
+
+
+
+
+
+
+ );
+}
diff --git a/docs/data/joy/main-features/color-inversion/ColorInversionSurface.js b/docs/data/joy/main-features/color-inversion/ColorInversionSurface.js
new file mode 100644
index 00000000000000..b7e0b94a246de4
--- /dev/null
+++ b/docs/data/joy/main-features/color-inversion/ColorInversionSurface.js
@@ -0,0 +1,73 @@
+import * as React from 'react';
+import Box from '@mui/joy/Box';
+import Card from '@mui/joy/Card';
+import CardContent from '@mui/joy/CardContent';
+import Typography from '@mui/joy/Typography';
+import SvgIcon from '@mui/joy/SvgIcon';
+
+export default function ColorInversionSurface() {
+ const creditCard = (
+
+
+
+ $4,236
+
+ CREDIT
+
+
+
+
+
+
+
+ •••• •••• •••• 1212
+
+
+
+
+ CARD NAME
+
+
+ JOHN DOE
+
+
+
+
+ EXPIRE
+
+
+ 07/25
+
+
+
+
+ );
+
+ return (
+
+ {creditCard}
+ {React.cloneElement(creditCard, { variant: 'soft' })}
+
+ );
+}
diff --git a/docs/data/joy/main-features/color-inversion/ColorInversionSurface.tsx b/docs/data/joy/main-features/color-inversion/ColorInversionSurface.tsx
new file mode 100644
index 00000000000000..3f49510152aaf3
--- /dev/null
+++ b/docs/data/joy/main-features/color-inversion/ColorInversionSurface.tsx
@@ -0,0 +1,72 @@
+import * as React from 'react';
+import Box from '@mui/joy/Box';
+import Card from '@mui/joy/Card';
+import CardContent from '@mui/joy/CardContent';
+import Typography from '@mui/joy/Typography';
+import SvgIcon from '@mui/joy/SvgIcon';
+
+export default function ColorInversionSurface() {
+ const creditCard = (
+
+
+
+ $4,236
+
+ CREDIT
+
+
+
+
+
+
+
+ •••• •••• •••• 1212
+
+
+
+
+ CARD NAME
+
+
+ JOHN DOE
+
+
+
+
+ EXPIRE
+
+
+ 07/25
+
+
+
+
+ );
+ return (
+
+ {creditCard}
+ {React.cloneElement(creditCard, { variant: 'soft' })}
+
+ );
+}
diff --git a/docs/data/joy/main-features/color-inversion/ColorInversionSurface.tsx.preview b/docs/data/joy/main-features/color-inversion/ColorInversionSurface.tsx.preview
new file mode 100644
index 00000000000000..f91cb4f688a1e8
--- /dev/null
+++ b/docs/data/joy/main-features/color-inversion/ColorInversionSurface.tsx.preview
@@ -0,0 +1,2 @@
+{creditCard}
+{React.cloneElement(creditCard, { variant: 'soft' })}
\ No newline at end of file
diff --git a/docs/data/joy/main-features/color-inversion/color-inversion.md b/docs/data/joy/main-features/color-inversion/color-inversion.md
index 7e6bd76d9bb694..835b020e3422a1 100644
--- a/docs/data/joy/main-features/color-inversion/color-inversion.md
+++ b/docs/data/joy/main-features/color-inversion/color-inversion.md
@@ -21,21 +21,13 @@ The color inversion is implemented to solve this issue, keeping the global varia
## Overview
-When color inversion is enabled on the parent component, the children with implicit color will invert their styles to match the parent's background by keeping the same hierarchy of importance based on their variants.
+When color inversion is enabled on the parent component, all of Joy UI children will invert their styles regardless of its color prop to match the parent's background by keeping the same hierarchy of importance based on their variants.
{{"demo": "ColorInversionOverview.js"}}
-**Implicit** color refers to components that don't have the `color` prop specified.
-
-Color inversion has no effect on children that have an **explicit** `color` prop.
-
-```js
-// implicit color. The styles change when color inversion is enabled.
-…
-
-// explicit color. Color inversion has no effect.
-…
-```
+:::info
+The color inversion feature is only available for `soft` and `solid` variants because the rest of the global variants don't have background by default.
+:::
### Benefits
@@ -45,7 +37,7 @@ Color inversion has no effect on children that have an **explicit** `color` prop
- It works for both light and dark mode.
- It can be disabled at any time without impacting the structure of the components.
- It is an opt-in feature. If you don't use it, the extra CSS variables won't be included in the production style sheet.
-- It doesn't alter the styles of the children if you explicitly specify the `color` prop on them.
+- Some children can be excluded from the color inversion, see ["skip color inversion on a child"](#skip-color-inversion-on-a-child) section.
### Trade-offs
@@ -54,13 +46,11 @@ Color inversion has no effect on children that have an **explicit** `color` prop
## Usage
-To enable color inversion, use the `invertedColors` prop. Note that this prop only works when the components have the `solid` or `soft` global variant applied.
+### Surface components
-```js
-…
+Surface components, including the Alert, Card, Drawer, ModalDialog, Menu, and Sheet, have the `invertedColors` prop to enable color inversion for `solid` and `soft` variants.
-…
-```
+{{"demo": "ColorInversionSurface.js"}}
### Portal popup
@@ -73,18 +63,71 @@ The popup slot of the Select component has `disablePortal` set to true by defaul
{{"demo": "ColorInversionPopup.js"}}
-## How it works
+### Skip color inversion on a child
+
+To skip color inversion on a specific child, set `data-skip-inverted-colors` attribute to the component.
+
+The component with `data-skip-inverted-colors` and its children will be excluded from the color inversion.
+
+{{"demo": "ColorInversionSkip.js"}}
-### Parent component
+### Apply color inversion to any parent
-When `invertedColors` is set to true on the surface component, a set of CSS variables is applied to it.
-The values of those variables come from `theme.colorInversion[variant][color]`, where `variant` and `color` are the component's props.
-The surface component also creates a React context to tell the children to update their styles.
+Use `applySolidInversion` or `applySoftInversion` utilities to apply color inversion to any parent component.
+
+They are used internally by the surface components, e.g. [Card](/joy-ui/react-card/#inverted-colors), when `invertedColors` prop is set to true.
+
+```js
+import { applySolidInversion, applySoftInversion } from '@mui/joy/colorInversion';
+```
+
+Example usage for `sx` prop and `styled` API:
+
+- `sx` prop
+
+ ```js
+ import { applySolidInversion } from '@mui/joy/colorInversion';
+
+ ({
+ display: 'flex',
+ alignItems: 'center',
+ background: theme.vars.palette.neutral[900],
+ }),
+ applySolidInversion('neutral'),
+ ]}
+ >
+ …
+ ;
+ ```
+
+- `styled` API
+
+ ```js
+ import { styled } from '@mui/joy/styles';
+ import { applySoftInversion } from '@mui/joy/colorInversion';
+
+ const StyledBox = styled(Box)(
+ ({ theme }) => ({
+ display: 'flex',
+ alignItems: 'center',
+ ...theme.variants.soft.primary,
+ }),
+ applySoftInversion('primary'),
+ );
+ ```
+
+{{"demo": "ColorInversionAnyParent.js"}}
+
+## How it works
+
+When `invertedColors` is set to true or the utility is used on the parent component, a set of CSS variables is applied to it. There is no [React context](https://react.dev/learn/passing-data-deeply-with-context) involved in this feature.
```jsx
-// The component style sheet
+// The parent's style sheet
{
// the values of these variables depends on the parent's variant and color.
--variant-softColor: …;
@@ -93,29 +136,26 @@ The surface component also creates a React context to tell the children to updat
--variant-softHoverBg: …;
--variant-softActiveBg: …;
… // other variants
+ --joy-palette-text-primary: …;
+ --joy-palette-text-secondary: …;
+ --joy-palette-text-tertiary: …;
+ --joy-palette-background-surface: …;
+ … // other theme palette tokens
}
```
-### Child component
-
-All Joy UI components that support global variants check the React context that contains the color inversion flag.
-If the flag is true and the child has an implicit color, the internal `color` value will switch to `context` and apply the styles from `theme.variants[variant].context`.
-
-The styles will match the `--variant-*` variables that the parent has.
+As a result, the children will use those CSS variables instead of the theme.
```jsx
-
-
-// Component style sheet
+// The children style sheet
+// The values of these variables are inherited from the parent.
{
- background-color: var(--variant-softBg);
- color: var(--variant-softColor);
+ color: var(--joy-palette-text-primary);
+ background: var(--joy-palette-background-surface);
+ …
}
```
-In summary, the parent creates a React context to tell the children that the feature is enabled, and generates CSS variables that will be used by the children.
-The children with an implicit color switch their default color value to `context` to get the styles from the theme.
-
## Common examples
### Header
diff --git a/packages/mui-joy/src/AccordionGroup/AccordionGroup.test.tsx b/packages/mui-joy/src/AccordionGroup/AccordionGroup.test.tsx
index 50dbfbd9de058d..4eec48f22eb501 100644
--- a/packages/mui-joy/src/AccordionGroup/AccordionGroup.test.tsx
+++ b/packages/mui-joy/src/AccordionGroup/AccordionGroup.test.tsx
@@ -1,10 +1,6 @@
import * as React from 'react';
import { expect } from 'chai';
-import {
- createRenderer,
- describeConformance,
- describeJoyColorInversion,
-} from '@mui-internal/test-utils';
+import { createRenderer, describeConformance } from '@mui-internal/test-utils';
import { ThemeProvider } from '@mui/joy/styles';
import AccordionGroup, { accordionGroupClasses as classes } from '@mui/joy/AccordionGroup';
@@ -77,8 +73,6 @@ describe('', () => {
expect(getByTestId('root')).to.have.class(colorConfig.class);
});
});
-
- describeJoyColorInversion(, { muiName: 'JoyAccordionGroup', classes });
});
it('should not warn when using custom color, variant, size', () => {
diff --git a/packages/mui-joy/src/AccordionGroup/AccordionGroup.tsx b/packages/mui-joy/src/AccordionGroup/AccordionGroup.tsx
index 73df8a92bf487f..8ec72d53de2cc9 100644
--- a/packages/mui-joy/src/AccordionGroup/AccordionGroup.tsx
+++ b/packages/mui-joy/src/AccordionGroup/AccordionGroup.tsx
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import { unstable_capitalize as capitalize } from '@mui/utils';
import { unstable_composeClasses as composeClasses } from '@mui/base';
import { OverridableComponent } from '@mui/types';
-import { useColorInversion, useThemeProps } from '../styles';
+import { useThemeProps } from '../styles';
import styled from '../styles/styled';
import { getAccordionGroupUtilityClass } from './accordionGroupClasses';
import {
@@ -84,7 +84,7 @@ const AccordionGroup = React.forwardRef(function AccordionGroup(inProps, ref) {
const {
component = 'div',
- color: colorProp = 'neutral',
+ color = 'neutral',
children,
disableDivider = false,
variant = 'plain',
@@ -97,9 +97,6 @@ const AccordionGroup = React.forwardRef(function AccordionGroup(inProps, ref) {
const externalForwardedProps = { ...other, component, slots, slotProps };
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, colorProp);
-
const ownerState = {
...props,
component,
diff --git a/packages/mui-joy/src/Alert/Alert.test.tsx b/packages/mui-joy/src/Alert/Alert.test.tsx
index 58003f6ebe9363..c0249786597255 100644
--- a/packages/mui-joy/src/Alert/Alert.test.tsx
+++ b/packages/mui-joy/src/Alert/Alert.test.tsx
@@ -1,10 +1,6 @@
import * as React from 'react';
import { expect } from 'chai';
-import {
- createRenderer,
- describeConformance,
- describeJoyColorInversion,
-} from '@mui-internal/test-utils';
+import { createRenderer, describeConformance } from '@mui-internal/test-utils';
import { unstable_capitalize as capitalize } from '@mui/utils';
import { ThemeProvider } from '@mui/joy/styles';
import Alert, { AlertClassKey, alertClasses as classes } from '@mui/joy/Alert';
@@ -30,8 +26,6 @@ describe('', () => {
skip: ['classesRoot', 'componentsProp'],
}));
- describeJoyColorInversion(, { muiName: 'JoyAlert', classes });
-
describe('prop: variant', () => {
it('soft by default', () => {
const { getByRole } = render();
diff --git a/packages/mui-joy/src/Alert/Alert.tsx b/packages/mui-joy/src/Alert/Alert.tsx
index ae19e140577ce0..b9aef4756b24b5 100644
--- a/packages/mui-joy/src/Alert/Alert.tsx
+++ b/packages/mui-joy/src/Alert/Alert.tsx
@@ -5,9 +5,9 @@ import clsx from 'clsx';
import { unstable_composeClasses as composeClasses } from '@mui/base';
import { OverridableComponent } from '@mui/types';
import { unstable_capitalize as capitalize } from '@mui/utils';
+import { applySolidInversion, applySoftInversion } from '../colorInversion';
import styled from '../styles/styled';
import useThemeProps from '../styles/useThemeProps';
-import { ColorInversionProvider, useColorInversion } from '../styles/ColorInversion';
import useSlot from '../utils/useSlot';
import { getAlertUtilityClass } from './alertClasses';
import { AlertProps, AlertOwnerState, AlertTypeMap } from './AlertProps';
@@ -76,11 +76,16 @@ const AlertRoot = styled('div', {
borderRadius: 'var(--Alert-radius)',
...theme.typography[`body-${({ sm: 'xs', md: 'sm', lg: 'md' } as const)[ownerState.size!]}`],
fontWeight: theme.vars.fontWeight.md,
+ ...(ownerState.variant === 'solid' &&
+ ownerState.color &&
+ ownerState.invertedColors &&
+ applySolidInversion(ownerState.color)(theme)),
+ ...(ownerState.variant === 'soft' &&
+ ownerState.color &&
+ ownerState.invertedColors &&
+ applySoftInversion(ownerState.color)(theme)),
...theme.variants[ownerState.variant!]?.[ownerState.color!],
} as const,
- ownerState.color !== 'context' &&
- ownerState.invertedColors &&
- theme.colorInversion[ownerState.variant!]?.[ownerState.color!],
p !== undefined && { '--Alert-padding': p },
padding !== undefined && { '--Alert-padding': padding },
borderRadius !== undefined && { '--Alert-radius': borderRadius },
@@ -124,7 +129,7 @@ const Alert = React.forwardRef(function Alert(inProps, ref) {
const {
children,
className,
- color: colorProp = 'neutral',
+ color = 'neutral',
invertedColors = false,
role = 'alert',
variant = 'soft',
@@ -136,12 +141,11 @@ const Alert = React.forwardRef(function Alert(inProps, ref) {
slotProps = {},
...other
} = props;
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, colorProp);
const ownerState = {
...props,
color,
+ invertedColors,
variant,
size,
};
@@ -174,24 +178,14 @@ const Alert = React.forwardRef(function Alert(inProps, ref) {
ownerState,
});
- const result = (
-
+ return (
+
{startDecorator && (
{startDecorator}
)}
{children}
{endDecorator && {endDecorator}}
-
- );
-
- return (
-
- {invertedColors ? (
- {result}
- ) : (
- result
- )}
);
}) as OverridableComponent;
diff --git a/packages/mui-joy/src/AspectRatio/AspectRatio.test.tsx b/packages/mui-joy/src/AspectRatio/AspectRatio.test.tsx
index 855482a1a59530..5798d0dc8d6c30 100644
--- a/packages/mui-joy/src/AspectRatio/AspectRatio.test.tsx
+++ b/packages/mui-joy/src/AspectRatio/AspectRatio.test.tsx
@@ -1,10 +1,6 @@
import * as React from 'react';
import { expect } from 'chai';
-import {
- createRenderer,
- describeConformance,
- describeJoyColorInversion,
-} from '@mui-internal/test-utils';
+import { createRenderer, describeConformance } from '@mui-internal/test-utils';
import { unstable_capitalize as capitalize } from '@mui/utils';
import { ThemeProvider } from '@mui/joy/styles';
import AspectRatio, {
@@ -32,11 +28,6 @@ describe('', () => {
skip: ['classesRoot', 'componentsProp'],
}));
- describeJoyColorInversion(
- ,
- { muiName: 'JoyAlert', classes },
- );
-
describe('prop: variant', () => {
it('plain by default', () => {
const { getByTestId } = render(Hello World);
diff --git a/packages/mui-joy/src/AspectRatio/AspectRatio.tsx b/packages/mui-joy/src/AspectRatio/AspectRatio.tsx
index 7d0323b1529abc..75d4d0e9fe9345 100644
--- a/packages/mui-joy/src/AspectRatio/AspectRatio.tsx
+++ b/packages/mui-joy/src/AspectRatio/AspectRatio.tsx
@@ -7,7 +7,6 @@ import { unstable_capitalize as capitalize } from '@mui/utils';
import useThemeProps from '../styles/useThemeProps';
import useSlot from '../utils/useSlot';
import styled from '../styles/styled';
-import { useColorInversion } from '../styles/ColorInversion';
import { getAspectRatioUtilityClass } from './aspectRatioClasses';
import { AspectRatioProps, AspectRatioOwnerState, AspectRatioTypeMap } from './AspectRatioProps';
@@ -111,7 +110,7 @@ const AspectRatio = React.forwardRef(function AspectRatio(inProps, ref) {
minHeight,
maxHeight,
objectFit = 'cover',
- color: colorProp = 'neutral',
+ color = 'neutral',
variant = 'soft',
component,
flex = false,
@@ -119,8 +118,6 @@ const AspectRatio = React.forwardRef(function AspectRatio(inProps, ref) {
slotProps = {},
...other
} = props;
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, colorProp);
const ownerState = {
...props,
diff --git a/packages/mui-joy/src/Autocomplete/Autocomplete.test.tsx b/packages/mui-joy/src/Autocomplete/Autocomplete.test.tsx
index 4fbe3908ea0834..8701cac0d7c202 100644
--- a/packages/mui-joy/src/Autocomplete/Autocomplete.test.tsx
+++ b/packages/mui-joy/src/Autocomplete/Autocomplete.test.tsx
@@ -9,7 +9,6 @@ import {
act,
fireEvent,
strictModeDoubleLoggingSuppressed,
- describeJoyColorInversion,
} from '@mui-internal/test-utils';
import Autocomplete, {
autocompleteClasses as classes,
@@ -78,12 +77,6 @@ describe('Joy ', () => {
},
}));
- describeJoyColorInversion(, {
- muiName: 'JoyAutocomplete',
- classes,
- portalSlot: 'listbox',
- });
-
it('should be customizable in the theme', () => {
render(
, 'onChange' | 'defaultValue'>;
@@ -346,12 +345,10 @@ const Autocomplete = React.forwardRef(function Autocomplete(
} = props;
const other = excludeUseAutocompleteParams(otherProps);
- const { getColor } = useColorInversion(variant);
const formControl = React.useContext(FormControlContext);
const error = inProps.error ?? formControl?.error ?? errorProp;
const size = inProps.size ?? formControl?.size ?? sizeProp;
- const rootColor = inProps.color ?? (error ? 'danger' : formControl?.color ?? colorProp);
- const color = getColor(inProps.color, rootColor);
+ const color = inProps.color ?? (error ? 'danger' : formControl?.color ?? colorProp);
const disabled = disabledProp ?? formControl?.disabled ?? false;
const {
@@ -386,6 +383,7 @@ const Autocomplete = React.forwardRef(function Autocomplete(
// If you modify this, make sure to keep the `AutocompleteOwnerState` type in sync.
const ownerState = {
+ instanceColor: inProps.color,
...props,
value,
disabled,
@@ -426,7 +424,7 @@ const Autocomplete = React.forwardRef(function Autocomplete(
key={index}
size={size}
variant="soft"
- color={color === 'context' ? undefined : 'neutral'}
+ color="neutral"
endDecorator={}
>
{getOptionLabel(option)}
@@ -535,9 +533,8 @@ const Autocomplete = React.forwardRef(function Autocomplete(
ownerState,
getSlotOwnerState: (mergedProps) => ({
size: mergedProps.size || size,
- variant:
- mergedProps.variant || getChildVariantAndColor(variant, rootColor).variant || 'plain',
- color: mergedProps.color || getChildVariantAndColor(variant, rootColor).color || 'neutral',
+ variant: mergedProps.variant || getChildVariantAndColor(variant, color).variant || 'plain',
+ color: mergedProps.color || getChildVariantAndColor(variant, color).color || 'neutral',
disableColorInversion: !!inProps.color,
}),
additionalProps: {
@@ -554,9 +551,8 @@ const Autocomplete = React.forwardRef(function Autocomplete(
ownerState,
getSlotOwnerState: (mergedProps) => ({
size: mergedProps.size || size,
- variant:
- mergedProps.variant || getChildVariantAndColor(variant, rootColor).variant || 'plain',
- color: mergedProps.color || getChildVariantAndColor(variant, rootColor).color || 'neutral',
+ variant: mergedProps.variant || getChildVariantAndColor(variant, color).variant || 'plain',
+ color: mergedProps.color || getChildVariantAndColor(variant, color).color || 'neutral',
disableColorInversion: !!inProps.color,
}),
additionalProps: {
@@ -576,7 +572,7 @@ const Autocomplete = React.forwardRef(function Autocomplete(
getSlotOwnerState: (mergedProps) => ({
size: mergedProps.size || size,
variant: mergedProps.variant || variant,
- color: mergedProps.color || (!mergedProps.disablePortal ? rootColor : color),
+ color: mergedProps.color || color,
disableColorInversion: !mergedProps.disablePortal,
}),
additionalProps: {
@@ -636,9 +632,8 @@ const Autocomplete = React.forwardRef(function Autocomplete(
externalForwardedProps,
ownerState,
getSlotOwnerState: (mergedProps) => ({
- variant:
- mergedProps.variant || getChildVariantAndColor(variant, rootColor).variant || 'plain',
- color: mergedProps.color || getChildVariantAndColor(variant, rootColor).color || 'neutral',
+ variant: mergedProps.variant || getChildVariantAndColor(variant, color).variant || 'plain',
+ color: mergedProps.color || getChildVariantAndColor(variant, color).color || 'neutral',
disableColorInversion: !listboxProps.disablePortal,
}),
additionalProps: {
@@ -680,14 +675,11 @@ const Autocomplete = React.forwardRef(function Autocomplete(
let popup = null;
if (anchorEl) {
popup = (
-
+
);
-
- if (!listboxProps.disablePortal) {
- // For portal popup, the children should not inherit color inversion from the upper parent.
- popup = {popup};
- }
}
return (
diff --git a/packages/mui-joy/src/AutocompleteListbox/AutocompleteListbox.test.tsx b/packages/mui-joy/src/AutocompleteListbox/AutocompleteListbox.test.tsx
index d2aa9408bbc062..2cc52aedc4263a 100644
--- a/packages/mui-joy/src/AutocompleteListbox/AutocompleteListbox.test.tsx
+++ b/packages/mui-joy/src/AutocompleteListbox/AutocompleteListbox.test.tsx
@@ -1,10 +1,6 @@
import * as React from 'react';
import { expect } from 'chai';
-import {
- describeConformance,
- createRenderer,
- describeJoyColorInversion,
-} from '@mui-internal/test-utils';
+import { describeConformance, createRenderer } from '@mui-internal/test-utils';
import { ThemeProvider } from '@mui/joy/styles';
import AutocompleteListbox, {
autocompleteListboxClasses as classes,
@@ -30,11 +26,6 @@ describe('Joy ', () => {
},
}));
- describeJoyColorInversion(, {
- muiName: 'JoyAutocompleteListbox',
- classes,
- });
-
it('should have ul tag', () => {
const { container } = render();
expect(container.firstChild).to.have.tagName('ul');
diff --git a/packages/mui-joy/src/AutocompleteListbox/AutocompleteListbox.tsx b/packages/mui-joy/src/AutocompleteListbox/AutocompleteListbox.tsx
index 4170a446629a1a..ca5381f029b203 100644
--- a/packages/mui-joy/src/AutocompleteListbox/AutocompleteListbox.tsx
+++ b/packages/mui-joy/src/AutocompleteListbox/AutocompleteListbox.tsx
@@ -16,7 +16,6 @@ import {
import listItemClasses from '../ListItem/listItemClasses';
import listClasses from '../List/listClasses';
import { scopedVariables } from '../List/ListProvider';
-import { useColorInversion } from '../styles/ColorInversion';
import useSlot from '../utils/useSlot';
const useUtilityClasses = (ownerState: AutocompleteListboxOwnerState) => {
@@ -107,15 +106,13 @@ const AutocompleteListbox = React.forwardRef(function AutocompleteListbox(inProp
children,
className,
component,
- color: colorProp = 'neutral',
+ color = 'neutral',
variant = 'outlined',
size = 'md',
slots = {},
slotProps = {},
...otherProps
} = props;
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, colorProp);
const ownerState = {
...props,
@@ -144,7 +141,7 @@ const AutocompleteListbox = React.forwardRef(function AutocompleteListbox(inProp
});
return (
-
+ {children}
);
diff --git a/packages/mui-joy/src/AutocompleteOption/AutocompleteOption.test.tsx b/packages/mui-joy/src/AutocompleteOption/AutocompleteOption.test.tsx
index 2e3cbd1519b956..590d7e2cbb3b6d 100644
--- a/packages/mui-joy/src/AutocompleteOption/AutocompleteOption.test.tsx
+++ b/packages/mui-joy/src/AutocompleteOption/AutocompleteOption.test.tsx
@@ -1,10 +1,6 @@
import * as React from 'react';
import { expect } from 'chai';
-import {
- describeConformance,
- createRenderer,
- describeJoyColorInversion,
-} from '@mui-internal/test-utils';
+import { describeConformance, createRenderer } from '@mui-internal/test-utils';
import { ThemeProvider } from '@mui/joy/styles';
import AutocompleteOption, {
autocompleteOptionClasses as classes,
@@ -30,8 +26,6 @@ describe('Joy ', () => {
},
}));
- describeJoyColorInversion(, { muiName: 'JoyAutocompleteOption', classes });
-
it('should have li tag', () => {
const { getByRole } = render();
expect(getByRole('option')).to.have.tagName('li');
diff --git a/packages/mui-joy/src/AutocompleteOption/AutocompleteOption.tsx b/packages/mui-joy/src/AutocompleteOption/AutocompleteOption.tsx
index 85e4ceba027655..1d2f6562644a49 100644
--- a/packages/mui-joy/src/AutocompleteOption/AutocompleteOption.tsx
+++ b/packages/mui-joy/src/AutocompleteOption/AutocompleteOption.tsx
@@ -10,7 +10,6 @@ import { styled, useThemeProps } from '../styles';
import { useVariantColor } from '../styles/variantColorInheritance';
import { getAutocompleteOptionUtilityClass } from './autocompleteOptionClasses';
import { AutocompleteOptionOwnerState, AutocompleteOptionTypeMap } from './AutocompleteOptionProps';
-import { useColorInversion } from '../styles/ColorInversion';
import useSlot from '../utils/useSlot';
const useUtilityClasses = (ownerState: AutocompleteOptionOwnerState) => {
@@ -68,12 +67,10 @@ const AutocompleteOption = React.forwardRef(function AutocompleteOption(inProps,
slotProps = {},
...other
} = props;
- const { variant = variantProp, color: inheritedColor = colorProp } = useVariantColor(
+ const { variant = variantProp, color = colorProp } = useVariantColor(
inProps.variant,
inProps.color,
);
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, inheritedColor);
const ownerState = {
...props,
diff --git a/packages/mui-joy/src/Avatar/Avatar.test.tsx b/packages/mui-joy/src/Avatar/Avatar.test.tsx
index c1d9bf1be2aabf..48ca2a24a8f36a 100644
--- a/packages/mui-joy/src/Avatar/Avatar.test.tsx
+++ b/packages/mui-joy/src/Avatar/Avatar.test.tsx
@@ -1,12 +1,7 @@
import * as React from 'react';
import { expect } from 'chai';
import { spy } from 'sinon';
-import {
- createRenderer,
- describeConformance,
- describeJoyColorInversion,
- fireEvent,
-} from '@mui-internal/test-utils';
+import { createRenderer, describeConformance, fireEvent } from '@mui-internal/test-utils';
import { unstable_capitalize as capitalize } from '@mui/utils';
import { ThemeProvider } from '@mui/joy/styles';
import Avatar, { AvatarClassKey, avatarClasses as classes } from '@mui/joy/Avatar';
@@ -33,8 +28,6 @@ describe('', () => {
skip: ['classesRoot', 'componentsProp'],
}));
- describeJoyColorInversion(, { muiName: 'JoyAvatar', classes });
-
describe('prop: variant', () => {
it('soft by default', () => {
const { getByTestId } = render();
diff --git a/packages/mui-joy/src/Avatar/Avatar.tsx b/packages/mui-joy/src/Avatar/Avatar.tsx
index fd2eb21fa1e240..aa8f54fe1c9667 100644
--- a/packages/mui-joy/src/Avatar/Avatar.tsx
+++ b/packages/mui-joy/src/Avatar/Avatar.tsx
@@ -7,7 +7,6 @@ import { unstable_capitalize as capitalize } from '@mui/utils';
import { useThemeProps } from '../styles';
import useSlot from '../utils/useSlot';
import styled from '../styles/styled';
-import { useColorInversion } from '../styles/ColorInversion';
import Person from '../internal/svg-icons/Person';
import { getAvatarUtilityClass } from './avatarClasses';
import { AvatarProps, AvatarOwnerState, AvatarTypeMap } from './AvatarProps';
@@ -169,9 +168,7 @@ const Avatar = React.forwardRef(function Avatar(inProps, ref) {
...other
} = props;
const variant = inProps.variant || groupContext?.variant || variantProp;
- const { getColor } = useColorInversion(variant);
- const colorFromContext = inProps.color || groupContext?.color;
- const color = colorFromContext !== 'context' ? getColor(colorFromContext, colorProp) : colorProp;
+ const color = inProps.color || groupContext?.color || colorProp;
const size = inProps.size || groupContext?.size || sizeProp;
let children = null;
diff --git a/packages/mui-joy/src/Badge/Badge.test.tsx b/packages/mui-joy/src/Badge/Badge.test.tsx
index 672b81c87d3ae2..51d2693b54cb22 100644
--- a/packages/mui-joy/src/Badge/Badge.test.tsx
+++ b/packages/mui-joy/src/Badge/Badge.test.tsx
@@ -1,10 +1,6 @@
import * as React from 'react';
import { expect } from 'chai';
-import {
- createRenderer,
- describeConformance,
- describeJoyColorInversion,
-} from '@mui-internal/test-utils';
+import { createRenderer, describeConformance } from '@mui-internal/test-utils';
import { unstable_capitalize as capitalize } from '@mui/utils';
import { ThemeProvider } from '@mui/joy/styles';
import Badge, { BadgeClassKey, BadgeOrigin, badgeClasses as classes } from '@mui/joy/Badge';
@@ -46,13 +42,6 @@ describe('', () => {
}),
);
- describeJoyColorInversion(
-
-
- ,
- { muiName: 'JoyBadge', classes },
- );
-
it('renders children and badgeContent', () => {
const children = ;
const badge = ;
diff --git a/packages/mui-joy/src/Badge/Badge.tsx b/packages/mui-joy/src/Badge/Badge.tsx
index 3bff899460b790..b81f1eed1d3b68 100644
--- a/packages/mui-joy/src/Badge/Badge.tsx
+++ b/packages/mui-joy/src/Badge/Badge.tsx
@@ -6,7 +6,6 @@ import { unstable_capitalize as capitalize, usePreviousProps } from '@mui/utils'
import { unstable_composeClasses as composeClasses } from '@mui/base';
import styled from '../styles/styled';
import useThemeProps from '../styles/useThemeProps';
-import { useColorInversion } from '../styles/ColorInversion';
import useSlot from '../utils/useSlot';
import badgeClasses, { getBadgeUtilityClass } from './badgeClasses';
import { BadgeProps, BadgeOwnerState, BadgeTypeMap } from './BadgeProps';
@@ -184,17 +183,22 @@ const Badge = React.forwardRef(function Badge(inProps, ref) {
}
const {
- color: internalColor = colorProp,
+ color = colorProp,
size = sizeProp,
anchorOrigin = anchorOriginProp,
variant = variantProp,
badgeInset = badgeInsetProp,
} = invisible ? prevProps : props;
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, internalColor);
-
- const ownerState = { ...props, anchorOrigin, badgeInset, variant, invisible, color, size };
+ const ownerState = {
+ ...props,
+ anchorOrigin,
+ badgeInset,
+ variant,
+ invisible,
+ color,
+ size,
+ };
const classes = useUtilityClasses(ownerState);
const externalForwardedProps = { ...other, component, slots, slotProps };
let displayValue =
diff --git a/packages/mui-joy/src/Button/Button.test.tsx b/packages/mui-joy/src/Button/Button.test.tsx
index 12f0a16724c096..2ff9cd065d847d 100644
--- a/packages/mui-joy/src/Button/Button.test.tsx
+++ b/packages/mui-joy/src/Button/Button.test.tsx
@@ -1,10 +1,6 @@
import * as React from 'react';
import { expect } from 'chai';
-import {
- describeConformance,
- createRenderer,
- describeJoyColorInversion,
-} from '@mui-internal/test-utils';
+import { describeConformance, createRenderer } from '@mui-internal/test-utils';
import Button, { buttonClasses as classes } from '@mui/joy/Button';
import { ThemeProvider } from '@mui/joy/styles';
@@ -33,8 +29,6 @@ describe('Joy ', () => {
}),
);
- describeJoyColorInversion(, { muiName: 'JoyButton', classes });
-
it('by default, should render with the root, variantSolid, sizeMd and colorPrimary classes', () => {
const { getByRole } = render();
const button = getByRole('button');
diff --git a/packages/mui-joy/src/Button/Button.tsx b/packages/mui-joy/src/Button/Button.tsx
index b1077f1186525b..2735eb215ac9e4 100644
--- a/packages/mui-joy/src/Button/Button.tsx
+++ b/packages/mui-joy/src/Button/Button.tsx
@@ -6,7 +6,6 @@ import { unstable_composeClasses as composeClasses } from '@mui/base/composeClas
import { Interpolation } from '@mui/system';
import { unstable_capitalize as capitalize, unstable_useForkRef as useForkRef } from '@mui/utils';
import { styled, Theme, useThemeProps } from '../styles';
-import { useColorInversion } from '../styles/ColorInversion';
import useSlot from '../utils/useSlot';
import CircularProgress from '../CircularProgress';
import buttonClasses, { getButtonUtilityClass } from './buttonClasses';
@@ -214,8 +213,7 @@ const Button = React.forwardRef(function Button(inProps, ref) {
const variant = inProps.variant || buttonGroup.variant || variantProp;
const size = inProps.size || buttonGroup.size || sizeProp;
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, buttonGroup.color || colorProp);
+ const color = inProps.color || buttonGroup.color || colorProp;
const disabled =
(inProps.disabled || inProps.loading) ?? (buttonGroup.disabled || disabledProp || loading);
@@ -229,10 +227,7 @@ const Button = React.forwardRef(function Button(inProps, ref) {
});
const loadingIndicator = loadingIndicatorProp ?? (
-
+
);
React.useImperativeHandle(
diff --git a/packages/mui-joy/src/ButtonGroup/ButtonGroup.tsx b/packages/mui-joy/src/ButtonGroup/ButtonGroup.tsx
index 110e772e5089ed..8dfda1b3ae7c8f 100644
--- a/packages/mui-joy/src/ButtonGroup/ButtonGroup.tsx
+++ b/packages/mui-joy/src/ButtonGroup/ButtonGroup.tsx
@@ -64,14 +64,15 @@ export const StyledButtonGroup = styled('div')<{ ownerState: ButtonGroupOwnerSta
}
},
);
+ const outlinedStyle = theme.variants.outlined?.[ownerState.color!];
+ const outlinedDisabledStyle = theme.variants.outlinedDisabled?.[ownerState.color!];
+ const outlinedHoverStyle = theme.variants.outlinedHover?.[ownerState.color!];
return [
{
'--ButtonGroup-separatorSize':
ownerState.variant === 'outlined' ? '1px' : 'calc(var(--ButtonGroup-connected) * 1px)',
- ...(ownerState.color !== 'context' && {
- '--ButtonGroup-separatorColor': theme.vars.palette[ownerState.color!]?.outlinedBorder,
- }),
+ '--ButtonGroup-separatorColor': outlinedStyle?.borderColor,
'--ButtonGroup-radius': theme.vars.radius.sm,
'--Divider-inset': '0.5rem',
'--unstable_childRadius':
@@ -131,19 +132,14 @@ export const StyledButtonGroup = styled('div')<{ ownerState: ButtonGroupOwnerSta
'&:not(:disabled)': {
zIndex: 1, // to make borders appear above disabled buttons.
},
- ...(ownerState.color !== 'context' && {
- '&:disabled': {
- '--ButtonGroup-separatorColor':
- theme.vars.palette[ownerState.color!]?.outlinedDisabledBorder,
+ '&:disabled': {
+ '--ButtonGroup-separatorColor': outlinedDisabledStyle?.borderColor,
+ },
+ ...(ownerState.variant === 'outlined' && {
+ '&:hover': {
+ '--ButtonGroup-separatorColor': outlinedHoverStyle?.borderColor,
},
}),
- ...(ownerState.variant === 'outlined' &&
- ownerState.color !== 'context' && {
- '&:hover': {
- '--ButtonGroup-separatorColor':
- theme.vars.palette[ownerState.color!]?.outlinedHoverBorder,
- },
- }),
[`&:hover, ${theme.focus.selector}`]: {
zIndex: 2, // to make borders appear above sibling.
},
diff --git a/packages/mui-joy/src/Card/Card.test.tsx b/packages/mui-joy/src/Card/Card.test.tsx
index 88272f6688a1c5..a95e7f9a6bf134 100644
--- a/packages/mui-joy/src/Card/Card.test.tsx
+++ b/packages/mui-joy/src/Card/Card.test.tsx
@@ -1,10 +1,6 @@
import * as React from 'react';
import { expect } from 'chai';
-import {
- createRenderer,
- describeConformance,
- describeJoyColorInversion,
-} from '@mui-internal/test-utils';
+import { createRenderer, describeConformance } from '@mui-internal/test-utils';
import { unstable_capitalize as capitalize } from '@mui/utils';
import { ThemeProvider } from '@mui/joy/styles';
import Card, { cardClasses as classes, CardClassKey } from '@mui/joy/Card';
@@ -30,8 +26,6 @@ describe('', () => {
},
}));
- describeJoyColorInversion(, { muiName: 'JoyCard', classes });
-
describe('prop: variant', () => {
it('outlined by default', () => {
const { getByTestId } = render(Hello World);
diff --git a/packages/mui-joy/src/Card/Card.tsx b/packages/mui-joy/src/Card/Card.tsx
index 48d3d77fbf904b..c2cf9288631133 100644
--- a/packages/mui-joy/src/Card/Card.tsx
+++ b/packages/mui-joy/src/Card/Card.tsx
@@ -9,8 +9,8 @@ import {
unstable_isMuiElement as isMuiElement,
} from '@mui/utils';
import { useThemeProps } from '../styles';
+import { applySolidInversion, applySoftInversion } from '../colorInversion';
import styled from '../styles/styled';
-import { ColorInversionProvider, useColorInversion } from '../styles/ColorInversion';
import { getCardUtilityClass } from './cardClasses';
import { CardProps, CardOwnerState, CardTypeMap } from './CardProps';
import { resolveSxValue } from '../styles/styleUtils';
@@ -83,11 +83,16 @@ export const StyledCardRoot = styled('div')<{ ownerState: CardOwnerState }>(
display: 'flex',
flexDirection: ownerState.orientation === 'horizontal' ? 'row' : 'column',
...theme.typography[`body-${ownerState.size!}`],
+ ...(ownerState.variant === 'solid' &&
+ ownerState.color &&
+ ownerState.invertedColors &&
+ applySolidInversion(ownerState.color)(theme)),
+ ...(ownerState.variant === 'soft' &&
+ ownerState.color &&
+ ownerState.invertedColors &&
+ applySoftInversion(ownerState.color)(theme)),
...theme.variants[ownerState.variant!]?.[ownerState.color!],
} as const,
- ownerState.color !== 'context' &&
- ownerState.invertedColors &&
- theme.colorInversion[ownerState.variant!]?.[ownerState.color!],
p !== undefined && { '--Card-padding': p },
padding !== undefined && { '--Card-padding': padding },
borderRadius !== undefined && { '--Card-radius': borderRadius },
@@ -99,7 +104,7 @@ const CardRoot = styled(StyledCardRoot, {
name: 'JoyCard',
slot: 'Root',
overridesResolver: (props, styles) => styles.root,
-})({});
+})<{ ownerState: CardOwnerState }>({});
/**
*
@@ -119,7 +124,7 @@ const Card = React.forwardRef(function Card(inProps, ref) {
const {
className,
- color: colorProp = 'neutral',
+ color = 'neutral',
component = 'div',
invertedColors = false,
size = 'md',
@@ -130,8 +135,6 @@ const Card = React.forwardRef(function Card(inProps, ref) {
slotProps = {},
...other
} = props;
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, colorProp);
const ownerState = {
...props,
@@ -140,6 +143,7 @@ const Card = React.forwardRef(function Card(inProps, ref) {
orientation,
size,
variant,
+ invertedColors,
};
const classes = useUtilityClasses(ownerState);
@@ -153,7 +157,7 @@ const Card = React.forwardRef(function Card(inProps, ref) {
ownerState,
});
- const result = (
+ return (
{React.Children.map(children, (child, index) => {
if (!React.isValidElement(child)) {
@@ -185,11 +189,6 @@ const Card = React.forwardRef(function Card(inProps, ref) {
})}
);
-
- if (invertedColors) {
- return {result};
- }
- return result;
}) as OverridableComponent;
Card.propTypes /* remove-proptypes */ = {
diff --git a/packages/mui-joy/src/CardOverflow/CardOverflow.test.tsx b/packages/mui-joy/src/CardOverflow/CardOverflow.test.tsx
index 33619311553c68..ca34945d506f57 100644
--- a/packages/mui-joy/src/CardOverflow/CardOverflow.test.tsx
+++ b/packages/mui-joy/src/CardOverflow/CardOverflow.test.tsx
@@ -1,10 +1,6 @@
import * as React from 'react';
import { expect } from 'chai';
-import {
- createRenderer,
- describeConformance,
- describeJoyColorInversion,
-} from '@mui-internal/test-utils';
+import { createRenderer, describeConformance } from '@mui-internal/test-utils';
import { unstable_capitalize as capitalize } from '@mui/utils';
import { ThemeProvider } from '@mui/joy/styles';
import CardOverflow, {
@@ -33,8 +29,6 @@ describe('', () => {
},
}));
- describeJoyColorInversion(, { muiName: 'JoyCardOverflow', classes });
-
describe('prop: variant', () => {
it('plain by default', () => {
const { getByTestId } = render(Hello World);
diff --git a/packages/mui-joy/src/CardOverflow/CardOverflow.tsx b/packages/mui-joy/src/CardOverflow/CardOverflow.tsx
index 17ed565de6d0dd..0303c760586743 100644
--- a/packages/mui-joy/src/CardOverflow/CardOverflow.tsx
+++ b/packages/mui-joy/src/CardOverflow/CardOverflow.tsx
@@ -7,7 +7,6 @@ import { OverridableComponent } from '@mui/types';
import { unstable_capitalize as capitalize } from '@mui/utils';
import { useThemeProps } from '../styles';
import styled from '../styles/styled';
-import { useColorInversion } from '../styles/ColorInversion';
import { getCardOverflowUtilityClass } from './cardOverflowClasses';
import {
CardOverflowProps,
@@ -128,14 +127,12 @@ const CardOverflow = React.forwardRef(function CardOverflow(inProps, ref) {
className,
component = 'div',
children,
- color: colorProp = 'neutral',
+ color = 'neutral',
variant = 'plain',
slots = {},
slotProps = {},
...other
} = props;
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, colorProp);
const ownerState = {
...props,
diff --git a/packages/mui-joy/src/Checkbox/Checkbox.test.tsx b/packages/mui-joy/src/Checkbox/Checkbox.test.tsx
index a52ee390e075a6..5e615272322fd7 100644
--- a/packages/mui-joy/src/Checkbox/Checkbox.test.tsx
+++ b/packages/mui-joy/src/Checkbox/Checkbox.test.tsx
@@ -1,12 +1,6 @@
import * as React from 'react';
import { expect } from 'chai';
-import {
- describeConformance,
- act,
- createRenderer,
- fireEvent,
- describeJoyColorInversion,
-} from '@mui-internal/test-utils';
+import { describeConformance, act, createRenderer, fireEvent } from '@mui-internal/test-utils';
import Checkbox, { checkboxClasses as classes } from '@mui/joy/Checkbox';
import { ThemeProvider } from '@mui/joy/styles';
import CloseIcon from '../internal/svg-icons/Close';
@@ -33,8 +27,6 @@ describe('', () => {
skip: ['componentProp', 'componentsProp', 'classesRoot', 'propsSpread', 'themeVariants'],
}));
- describeJoyColorInversion(, { muiName: 'JoyCheckbox', classes });
-
it('should have the classes required for Checkbox', () => {
expect(classes).to.include.all.keys(['root', 'checked', 'disabled']);
});
diff --git a/packages/mui-joy/src/Checkbox/Checkbox.tsx b/packages/mui-joy/src/Checkbox/Checkbox.tsx
index 159085a3530a6e..91d0ebad97383b 100644
--- a/packages/mui-joy/src/Checkbox/Checkbox.tsx
+++ b/packages/mui-joy/src/Checkbox/Checkbox.tsx
@@ -6,7 +6,6 @@ import { unstable_useId as useId, unstable_capitalize as capitalize } from '@mui
import { unstable_composeClasses as composeClasses } from '@mui/base';
import { useSwitch } from '@mui/base/useSwitch';
import { styled, useThemeProps } from '../styles';
-import { useColorInversion } from '../styles/ColorInversion';
import useSlot from '../utils/useSlot';
import checkboxClasses, { getCheckboxUtilityClass } from './checkboxClasses';
import { CheckboxOwnerState, CheckboxTypeMap } from './CheckboxProps';
@@ -270,11 +269,7 @@ const Checkbox = React.forwardRef(function Checkbox(inProps, ref) {
const activeVariant = variantProp || 'solid';
const inactiveVariant = variantProp || 'outlined';
const variant = isCheckboxActive ? activeVariant : inactiveVariant;
- const { getColor } = useColorInversion(variant);
- const color = getColor(
- inProps.color,
- formControl?.error ? 'danger' : formControl?.color ?? colorProp,
- );
+ const color = inProps.color || (formControl?.error ? 'danger' : formControl?.color ?? colorProp);
const activeColor = color || 'primary';
const inactiveColor = color || 'neutral';
diff --git a/packages/mui-joy/src/Chip/Chip.test.tsx b/packages/mui-joy/src/Chip/Chip.test.tsx
index 952079d7dd80a6..35b1d18dd5147f 100644
--- a/packages/mui-joy/src/Chip/Chip.test.tsx
+++ b/packages/mui-joy/src/Chip/Chip.test.tsx
@@ -1,12 +1,7 @@
import * as React from 'react';
import { expect } from 'chai';
import { spy } from 'sinon';
-import {
- createRenderer,
- describeConformance,
- describeJoyColorInversion,
- fireEvent,
-} from '@mui-internal/test-utils';
+import { createRenderer, describeConformance, fireEvent } from '@mui-internal/test-utils';
import { unstable_capitalize as capitalize } from '@mui/utils';
import { ThemeProvider } from '@mui/joy/styles';
import Chip, { ChipClassKey, chipClasses as classes } from '@mui/joy/Chip';
@@ -39,8 +34,6 @@ describe('', () => {
}),
);
- describeJoyColorInversion(, { muiName: 'JoyChip', classes });
-
it('renders children', () => {
const { getByText } = render(
diff --git a/packages/mui-joy/src/Chip/Chip.tsx b/packages/mui-joy/src/Chip/Chip.tsx
index 19793643c40d7c..557126e0b5c80f 100644
--- a/packages/mui-joy/src/Chip/Chip.tsx
+++ b/packages/mui-joy/src/Chip/Chip.tsx
@@ -9,7 +9,6 @@ import { unstable_capitalize as capitalize, unstable_useId as useId } from '@mui
import { useThemeProps } from '../styles';
import styled from '../styles/styled';
import { VariantColorProvider } from '../styles/variantColorInheritance';
-import { useColorInversion } from '../styles/ColorInversion';
import { resolveSxValue } from '../styles/styleUtils';
import chipClasses, { getChipUtilityClass } from './chipClasses';
import { ChipProps, ChipOwnerState, ChipTypeMap } from './ChipProps';
@@ -224,7 +223,7 @@ const Chip = React.forwardRef(function Chip(inProps, ref) {
const {
children,
className,
- color: colorProp = 'neutral',
+ color = 'neutral',
onClick,
disabled = false,
size = 'md',
@@ -236,8 +235,6 @@ const Chip = React.forwardRef(function Chip(inProps, ref) {
slotProps = {},
...other
} = props;
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, colorProp);
const clickable = !!onClick || !!slotProps.action;
const ownerState: ChipOwnerState = {
@@ -313,7 +310,7 @@ const Chip = React.forwardRef(function Chip(inProps, ref) {
return (
-
+
{clickable && }
diff --git a/packages/mui-joy/src/ChipDelete/ChipDelete.test.tsx b/packages/mui-joy/src/ChipDelete/ChipDelete.test.tsx
index b6ff05e0483838..d10ee7b52b5a70 100644
--- a/packages/mui-joy/src/ChipDelete/ChipDelete.test.tsx
+++ b/packages/mui-joy/src/ChipDelete/ChipDelete.test.tsx
@@ -1,13 +1,7 @@
import * as React from 'react';
import { expect } from 'chai';
import { spy } from 'sinon';
-import {
- createRenderer,
- describeConformance,
- describeJoyColorInversion,
- act,
- fireEvent,
-} from '@mui-internal/test-utils';
+import { createRenderer, describeConformance, act, fireEvent } from '@mui-internal/test-utils';
import { ThemeProvider } from '@mui/joy/styles';
import Chip from '@mui/joy/Chip';
import ChipDelete, { chipDeleteClasses as classes } from '@mui/joy/ChipDelete';
@@ -33,8 +27,6 @@ describe('', () => {
},
}));
- describeJoyColorInversion(, { muiName: 'JoyChipDelete', classes });
-
describe('Chip context', () => {
it('disabled', () => {
const { getByRole } = render(
diff --git a/packages/mui-joy/src/ChipDelete/ChipDelete.tsx b/packages/mui-joy/src/ChipDelete/ChipDelete.tsx
index 162e729b5e4b7c..e08d729e3e16f2 100644
--- a/packages/mui-joy/src/ChipDelete/ChipDelete.tsx
+++ b/packages/mui-joy/src/ChipDelete/ChipDelete.tsx
@@ -8,7 +8,6 @@ import { useButton } from '@mui/base/useButton';
import { useThemeProps } from '../styles';
import styled from '../styles/styled';
import { useVariantColor } from '../styles/variantColorInheritance';
-import { useColorInversion } from '../styles/ColorInversion';
import Cancel from '../internal/svg-icons/Cancel';
import { getChipDeleteUtilityClass } from './chipDeleteClasses';
import { ChipDeleteProps, ChipDeleteOwnerState, ChipDeleteTypeMap } from './ChipDeleteProps';
@@ -83,8 +82,7 @@ const ChipDelete = React.forwardRef(function ChipDelete(inProps, ref) {
inProps.color,
true,
);
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, inheritedColor);
+ const color = inProps.color || inheritedColor;
const disabled = disabledProp ?? chipContext.disabled;
const buttonRef = React.useRef(null);
diff --git a/packages/mui-joy/src/CircularProgress/CircularProgress.test.tsx b/packages/mui-joy/src/CircularProgress/CircularProgress.test.tsx
index 8316f1454bb014..bc2b03624d0c32 100644
--- a/packages/mui-joy/src/CircularProgress/CircularProgress.test.tsx
+++ b/packages/mui-joy/src/CircularProgress/CircularProgress.test.tsx
@@ -1,10 +1,6 @@
import * as React from 'react';
import { expect } from 'chai';
-import {
- createRenderer,
- describeConformance,
- describeJoyColorInversion,
-} from '@mui-internal/test-utils';
+import { createRenderer, describeConformance } from '@mui-internal/test-utils';
import { unstable_capitalize as capitalize } from '@mui/utils';
import { ThemeProvider } from '@mui/joy/styles';
import CircularProgress, { circularProgressClasses as classes } from '@mui/joy/CircularProgress';
@@ -38,8 +34,6 @@ describe('', () => {
skip: ['classesRoot', 'componentsProp'],
}));
- describeJoyColorInversion(, { muiName: 'JoyCircularProgress', classes });
-
describe('prop: determinate', () => {
it('should render a determinate circular progress', () => {
const { getByRole } = render();
diff --git a/packages/mui-joy/src/CircularProgress/CircularProgress.tsx b/packages/mui-joy/src/CircularProgress/CircularProgress.tsx
index d53022c549a2a8..7b71e59c98ab72 100644
--- a/packages/mui-joy/src/CircularProgress/CircularProgress.tsx
+++ b/packages/mui-joy/src/CircularProgress/CircularProgress.tsx
@@ -8,7 +8,6 @@ import { unstable_composeClasses as composeClasses } from '@mui/base';
import { css, keyframes } from '@mui/system';
import styled from '../styles/styled';
import useThemeProps from '../styles/useThemeProps';
-import { useColorInversion } from '../styles/ColorInversion';
import useSlot from '../utils/useSlot';
import { getCircularProgressUtilityClass } from './circularProgressClasses';
import {
@@ -213,7 +212,7 @@ const CircularProgress = React.forwardRef(function CircularProgress(inProps, ref
const {
children,
className,
- color: colorProp = 'primary',
+ color = 'primary',
size = 'md',
variant = 'soft',
thickness,
@@ -224,8 +223,6 @@ const CircularProgress = React.forwardRef(function CircularProgress(inProps, ref
slotProps = {},
...other
} = props;
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, colorProp);
const ownerState = {
...props,
diff --git a/packages/mui-joy/src/DialogTitle/DialogTitle.tsx b/packages/mui-joy/src/DialogTitle/DialogTitle.tsx
index 45c3a04bba3a40..8fb74285138d3b 100644
--- a/packages/mui-joy/src/DialogTitle/DialogTitle.tsx
+++ b/packages/mui-joy/src/DialogTitle/DialogTitle.tsx
@@ -6,7 +6,6 @@ import { unstable_composeClasses as composeClasses } from '@mui/base';
import { OverridableComponent } from '@mui/types';
import { useThemeProps } from '../styles';
import styled from '../styles/styled';
-import { useColorInversion } from '../styles/ColorInversion';
import { getDialogTitleUtilityClass } from './dialogTitleClasses';
import { DialogTitleProps, DialogTitleOwnerState, DialogTitleTypeMap } from './DialogTitleProps';
import cardOverflowClasses from '../CardOverflow/cardOverflowClasses';
@@ -89,8 +88,7 @@ const DialogTitle = React.forwardRef(function DialogTitle(inProps, ref) {
...other
} = props;
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, variant ? colorProp ?? 'neutral' : colorProp);
+ const color = inProps.color || (variant ? colorProp ?? 'neutral' : colorProp);
const externalForwardedProps = { ...other, component, slots, slotProps };
diff --git a/packages/mui-joy/src/Drawer/Drawer.tsx b/packages/mui-joy/src/Drawer/Drawer.tsx
index 80d5570da4faa2..c5b365418f8172 100644
--- a/packages/mui-joy/src/Drawer/Drawer.tsx
+++ b/packages/mui-joy/src/Drawer/Drawer.tsx
@@ -11,7 +11,8 @@ import { OverridableComponent } from '@mui/types';
import { unstable_useModal as useModal } from '@mui/base/unstable_useModal';
import { Portal } from '@mui/base/Portal';
import { FocusTrap } from '@mui/base/FocusTrap';
-import { useThemeProps, styled, ColorInversionProvider, useColorInversion } from '../styles';
+import { useThemeProps, styled } from '../styles';
+import { applySoftInversion, applySolidInversion } from '../colorInversion';
import { StyledModalBackdrop, StyledModalRoot } from '../Modal/Modal';
import CloseModalContext from '../Modal/CloseModalContext';
import useSlot from '../utils/useSlot';
@@ -43,7 +44,7 @@ const DrawerRoot = styled(StyledModalRoot as unknown as 'div', {
name: 'JoyDrawer',
slot: 'Root',
overridesResolver: (props, styles) => styles.root,
-})<{ ownerState: DrawerOwnerState }>(({ ownerState }) => ({
+})<{ ownerState: DrawerOwnerState }>(({ ownerState, theme }) => ({
'--Drawer-transitionDuration': '0.3s',
'--Drawer-transitionFunction': 'ease',
'--ModalClose-radius':
@@ -71,6 +72,15 @@ const DrawerRoot = styled(StyledModalRoot as unknown as 'div', {
...(!ownerState.open && {
visibility: 'hidden',
}),
+ ...(ownerState.variant === 'solid' &&
+ ownerState.color &&
+ ownerState.invertedColors &&
+ applySolidInversion(ownerState.color)(theme)),
+ ...(ownerState.variant === 'soft' &&
+ ownerState.color &&
+ ownerState.invertedColors &&
+ applySoftInversion(ownerState.color)(theme)),
+ ...theme.variants[ownerState.variant!]?.[ownerState.color!],
}));
const DrawerBackdrop = styled(StyledModalBackdrop as unknown as 'div', {
@@ -155,7 +165,7 @@ const Drawer = React.forwardRef(function Drawer(inProps, ref) {
disableRestoreFocus = false,
disableScrollLock = false,
hideBackdrop = false,
- color: colorProp = 'neutral',
+ color = 'neutral',
variant = 'plain',
invertedColors = false,
size = 'md',
@@ -168,9 +178,6 @@ const Drawer = React.forwardRef(function Drawer(inProps, ref) {
...other
} = props;
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, colorProp);
-
const ownerState = {
...props,
anchor,
@@ -181,6 +188,7 @@ const Drawer = React.forwardRef(function Drawer(inProps, ref) {
disableRestoreFocus,
disableScrollLock,
hideBackdrop,
+ invertedColors,
color,
variant,
size,
@@ -198,7 +206,7 @@ const Drawer = React.forwardRef(function Drawer(inProps, ref) {
const labelledBy = useId();
const describedBy = useId();
const contextValue = React.useMemo(
- () => ({ variant, color: color === 'context' ? undefined : color, labelledBy, describedBy }),
+ () => ({ variant, color, labelledBy, describedBy }),
[color, variant, labelledBy, describedBy],
);
@@ -233,7 +241,7 @@ const Drawer = React.forwardRef(function Drawer(inProps, ref) {
ownerState,
});
- const result = (
+ return (
@@ -261,11 +269,6 @@ const Drawer = React.forwardRef(function Drawer(inProps, ref) {
);
-
- if (invertedColors) {
- return {result};
- }
- return result;
}) as OverridableComponent;
Drawer.propTypes /* remove-proptypes */ = {
diff --git a/packages/mui-joy/src/FormControl/FormControl.tsx b/packages/mui-joy/src/FormControl/FormControl.tsx
index 73ac96bb0ff231..e44bfb5e0e0d56 100644
--- a/packages/mui-joy/src/FormControl/FormControl.tsx
+++ b/packages/mui-joy/src/FormControl/FormControl.tsx
@@ -60,20 +60,16 @@ export const FormControlRoot = styled('div', {
'--FormHelperText-fontSize': theme.vars.fontSize.sm,
'--FormHelperText-lineHeight': theme.vars.lineHeight.sm,
}),
- ...(ownerState.color &&
- ownerState.color !== 'context' && {
- '--FormHelperText-color': theme.vars.palette[ownerState.color]?.[500],
- }),
+ ...(ownerState.color && {
+ '--FormHelperText-color': theme.vars.palette[ownerState.color]?.[500],
+ }),
'--FormHelperText-margin': '0.375rem 0 0 0',
[`&.${formControlClasses.error}`]: {
'--FormHelperText-color': theme.vars.palette.danger[500],
},
[`&.${formControlClasses.disabled}`]: {
- ...(ownerState.color !== 'context' && {
- '--FormLabel-color': theme.vars.palette[ownerState.color || 'neutral']?.plainDisabledColor,
- '--FormHelperText-color':
- theme.vars.palette[ownerState.color || 'neutral']?.plainDisabledColor,
- }),
+ '--FormLabel-color': theme.variants.plainDisabled?.[ownerState.color || 'neutral']?.color,
+ '--FormHelperText-color': theme.variants.plainDisabled?.[ownerState.color || 'neutral']?.color,
},
display: 'flex',
position: 'relative', // for keeping the control action area, e.g. Switch
diff --git a/packages/mui-joy/src/IconButton/IconButton.test.tsx b/packages/mui-joy/src/IconButton/IconButton.test.tsx
index 358c1fb4e89a80..b7a76e2ad9e583 100644
--- a/packages/mui-joy/src/IconButton/IconButton.test.tsx
+++ b/packages/mui-joy/src/IconButton/IconButton.test.tsx
@@ -1,10 +1,6 @@
import * as React from 'react';
import { expect } from 'chai';
-import {
- describeConformance,
- createRenderer,
- describeJoyColorInversion,
-} from '@mui-internal/test-utils';
+import { describeConformance, createRenderer } from '@mui-internal/test-utils';
import IconButton, { iconButtonClasses as classes } from '@mui/joy/IconButton';
import { ThemeProvider } from '@mui/joy/styles';
@@ -25,8 +21,6 @@ describe('Joy ', () => {
skip: ['propsSpread', 'componentsProp', 'classesRoot'],
}));
- describeJoyColorInversion(, { muiName: 'JoyIconButton', classes });
-
it('by default, should render with the root, variantPlain, sizeMd and colorNeutral classes', () => {
const { getByRole } = render(Hello World);
const button = getByRole('button');
diff --git a/packages/mui-joy/src/IconButton/IconButton.tsx b/packages/mui-joy/src/IconButton/IconButton.tsx
index 8a169ab2d456c1..3a9513bf260353 100644
--- a/packages/mui-joy/src/IconButton/IconButton.tsx
+++ b/packages/mui-joy/src/IconButton/IconButton.tsx
@@ -5,7 +5,6 @@ import { unstable_capitalize as capitalize, unstable_useForkRef as useForkRef }
import { useButton } from '@mui/base/useButton';
import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses';
import { styled, useThemeProps } from '../styles';
-import { useColorInversion } from '../styles/ColorInversion';
import useSlot from '../utils/useSlot';
import { getIconButtonUtilityClass } from './iconButtonClasses';
import { IconButtonOwnerState, IconButtonTypeMap, ExtendIconButton } from './IconButtonProps';
@@ -143,8 +142,7 @@ const IconButton = React.forwardRef(function IconButton(inProps, ref) {
const buttonGroup = React.useContext(ButtonGroupContext);
const variant = inProps.variant || buttonGroup.variant || variantProp;
const size = inProps.size || buttonGroup.size || sizeProp;
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, buttonGroup.color || colorProp);
+ const color = inProps.color || buttonGroup.color || colorProp;
const disabled = inProps.disabled ?? (buttonGroup.disabled || disabledProp);
const buttonRef = React.useRef(null);
diff --git a/packages/mui-joy/src/Input/Input.test.tsx b/packages/mui-joy/src/Input/Input.test.tsx
index 839ab09b864861..0cf5aaccba0c4f 100644
--- a/packages/mui-joy/src/Input/Input.test.tsx
+++ b/packages/mui-joy/src/Input/Input.test.tsx
@@ -3,7 +3,6 @@ import { expect } from 'chai';
import { spy } from 'sinon';
import {
describeConformance,
- describeJoyColorInversion,
createRenderer,
screen,
act,
@@ -33,8 +32,6 @@ describe('Joy ', () => {
skip: ['propsSpread', 'componentsProp', 'classesRoot'],
}));
- describeJoyColorInversion(, { muiName: 'JoyInput', classes });
-
it('should have placeholder', () => {
const { getByPlaceholderText } = render();
expect(getByPlaceholderText('Placeholder')).toBeVisible();
diff --git a/packages/mui-joy/src/Input/Input.tsx b/packages/mui-joy/src/Input/Input.tsx
index d925e827bf5496..00789d7dded579 100644
--- a/packages/mui-joy/src/Input/Input.tsx
+++ b/packages/mui-joy/src/Input/Input.tsx
@@ -5,11 +5,11 @@ import { unstable_capitalize as capitalize } from '@mui/utils';
import { OverridableComponent } from '@mui/types';
import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses';
import { styled, useThemeProps } from '../styles';
-import { useColorInversion } from '../styles/ColorInversion';
import useSlot from '../utils/useSlot';
import { InputTypeMap, InputProps, InputOwnerState } from './InputProps';
import inputClasses, { getInputUtilityClass } from './inputClasses';
import useForwardedInput from './useForwardedInput';
+import { INVERTED_COLORS_ATTR } from '../colorInversion/colorInversionUtils';
const useUtilityClasses = (ownerState: InputOwnerState) => {
const { disabled, fullWidth, variant, color, size } = ownerState;
@@ -43,16 +43,17 @@ export const StyledInputRoot = styled('div')<{ ownerState: InputOwnerState }>(
'--Input-decoratorColor': theme.vars.palette.text.icon,
'--Input-focused': '0',
'--Input-focusedThickness': theme.vars.focus.thickness,
- ...(ownerState.color === 'context'
- ? {
- '--Input-focusedHighlight': theme.vars.palette.focusVisible,
- }
- : {
- '--Input-focusedHighlight':
- theme.vars.palette[
- ownerState.color === 'neutral' ? 'primary' : ownerState.color!
- ]?.[500],
- }),
+ '--Input-focusedHighlight':
+ theme.vars.palette[ownerState.color === 'neutral' ? 'primary' : ownerState.color!]?.[500],
+ [`&:not([${INVERTED_COLORS_ATTR}])`]: {
+ ...(ownerState.instanceColor && {
+ '--_Input-focusedHighlight':
+ theme.vars.palette[
+ ownerState.instanceColor === 'neutral' ? 'primary' : ownerState.instanceColor
+ ]?.[500],
+ }),
+ '--Input-focusedHighlight': `var(--_Input-focusedHighlight, ${theme.vars.palette.focusVisible})`,
+ },
...(ownerState.size === 'sm' && {
'--Input-minHeight': '2rem',
'--Input-paddingInline': '0.5rem',
@@ -280,10 +281,10 @@ const Input = React.forwardRef(function Input(inProps, ref) {
const error = inProps.error ?? formControl?.error ?? errorProp;
const size = inProps.size ?? formControl?.size ?? sizeProp;
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, error ? 'danger' : formControl?.color ?? colorProp);
+ const color = inProps.color ?? (error ? 'danger' : formControl?.color ?? colorProp);
const ownerState = {
+ instanceColor: error ? 'danger' : inProps.color,
...props,
fullWidth,
color,
diff --git a/packages/mui-joy/src/Input/InputProps.ts b/packages/mui-joy/src/Input/InputProps.ts
index e19444406e535f..390029ee5aeea9 100644
--- a/packages/mui-joy/src/Input/InputProps.ts
+++ b/packages/mui-joy/src/Input/InputProps.ts
@@ -124,4 +124,8 @@ export interface InputOwnerState extends ApplyColorInversion {
* If `true`, the input is focused.
*/
focused: boolean;
+ /**
+ * @internal
+ */
+ instanceColor?: OverridableStringUnion;
}
diff --git a/packages/mui-joy/src/LinearProgress/LinearProgress.test.tsx b/packages/mui-joy/src/LinearProgress/LinearProgress.test.tsx
index 1cdac10d1daab2..5a976b191e5386 100644
--- a/packages/mui-joy/src/LinearProgress/LinearProgress.test.tsx
+++ b/packages/mui-joy/src/LinearProgress/LinearProgress.test.tsx
@@ -1,10 +1,6 @@
import * as React from 'react';
import { expect } from 'chai';
-import {
- createRenderer,
- describeConformance,
- describeJoyColorInversion,
-} from '@mui-internal/test-utils';
+import { createRenderer, describeConformance } from '@mui-internal/test-utils';
import { unstable_capitalize as capitalize } from '@mui/utils';
import { ThemeProvider } from '@mui/joy/styles';
import LinearProgress, { linearProgressClasses as classes } from '@mui/joy/LinearProgress';
@@ -28,8 +24,6 @@ describe('', () => {
},
}));
- describeJoyColorInversion(, { muiName: 'JoyLinearProgress', classes });
-
describe('prop: determinate', () => {
it('should render a determinate circular progress', () => {
const { getByRole } = render();
diff --git a/packages/mui-joy/src/LinearProgress/LinearProgress.tsx b/packages/mui-joy/src/LinearProgress/LinearProgress.tsx
index af9fa127fda8a0..ee7d2d03f8ec9b 100644
--- a/packages/mui-joy/src/LinearProgress/LinearProgress.tsx
+++ b/packages/mui-joy/src/LinearProgress/LinearProgress.tsx
@@ -8,7 +8,6 @@ import { unstable_composeClasses as composeClasses } from '@mui/base';
import { css, keyframes } from '@mui/system';
import styled from '../styles/styled';
import useThemeProps from '../styles/useThemeProps';
-import { useColorInversion } from '../styles/ColorInversion';
import { getLinearProgressUtilityClass } from './linearProgressClasses';
import {
LinearProgressOwnerState,
@@ -167,7 +166,7 @@ const LinearProgress = React.forwardRef(function LinearProgress(inProps, ref) {
children,
className,
component,
- color: colorProp = 'primary',
+ color = 'primary',
size = 'md',
variant = 'soft',
thickness,
@@ -178,8 +177,6 @@ const LinearProgress = React.forwardRef(function LinearProgress(inProps, ref) {
slotProps = {},
...other
} = props;
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, colorProp);
const ownerState = {
...props,
diff --git a/packages/mui-joy/src/Link/Link.test.tsx b/packages/mui-joy/src/Link/Link.test.tsx
index f5ec486606602e..5271fe9a682422 100644
--- a/packages/mui-joy/src/Link/Link.test.tsx
+++ b/packages/mui-joy/src/Link/Link.test.tsx
@@ -1,13 +1,7 @@
import * as React from 'react';
import { expect } from 'chai';
import { SinonSpy, spy } from 'sinon';
-import {
- act,
- createRenderer,
- fireEvent,
- describeConformance,
- describeJoyColorInversion,
-} from '@mui-internal/test-utils';
+import { act, createRenderer, fireEvent, describeConformance } from '@mui-internal/test-utils';
import { unstable_capitalize as capitalize } from '@mui/utils';
import Link, { LinkClassKey, linkClasses as classes } from '@mui/joy/Link';
import Typography from '@mui/joy/Typography';
@@ -46,8 +40,6 @@ describe('', () => {
}),
);
- describeJoyColorInversion(, { muiName: 'JoyLink', classes });
-
it('should render children', () => {
const { queryByText } = render(Home);
diff --git a/packages/mui-joy/src/Link/Link.tsx b/packages/mui-joy/src/Link/Link.tsx
index d2dfc19f4c69db..c3f813d60ddac1 100644
--- a/packages/mui-joy/src/Link/Link.tsx
+++ b/packages/mui-joy/src/Link/Link.tsx
@@ -12,7 +12,6 @@ import {
import { unstable_extendSxProp as extendSxProp } from '@mui/system';
import styled from '../styles/styled';
import useThemeProps from '../styles/useThemeProps';
-import { useColorInversion } from '../styles/ColorInversion';
import useSlot from '../utils/useSlot';
import linkClasses, { getLinkUtilityClass } from './linkClasses';
import { LinkProps, LinkOwnerState, LinkTypeMap } from './LinkProps';
@@ -109,11 +108,9 @@ const LinkRoot = styled('a', {
borderRadius: theme.vars.radius.xs,
padding: 0, // Remove the padding in Firefox
cursor: 'pointer',
- ...(ownerState.color !== 'context' && {
- textDecorationColor: `rgba(${
- theme.vars.palette[ownerState.color!]?.mainChannel
- } / var(--Link-underlineOpacity, 0.72))`,
- }),
+ textDecorationColor: `var(--variant-outlinedBorder, rgba(${
+ theme.vars.palette[ownerState.color!]?.mainChannel
+ } / var(--Link-underlineOpacity, 0.72)))`,
...(ownerState.variant
? {
paddingBlock: 'min(0.1em, 4px)',
@@ -123,14 +120,14 @@ const LinkRoot = styled('a', {
}),
}
: {
- ...(ownerState.color !== 'context' && {
- color: `rgba(${theme.vars.palette[ownerState.color!]?.mainChannel} / 1)`,
- }),
+ color: `var(--variant-plainColor, rgba(${
+ theme.vars.palette[ownerState.color!]?.mainChannel
+ } / 1))`,
[`&.${linkClasses.disabled}`]: {
pointerEvents: 'none',
- ...(ownerState.color !== 'context' && {
- color: `rgba(${theme.vars.palette[ownerState.color!]?.mainChannel} / 0.6)`,
- }),
+ color: `var(--variant-plainDisabledColor, rgba(${
+ theme.vars.palette[ownerState.color!]?.mainChannel
+ } / 0.6))`,
},
}),
userSelect: 'none',
@@ -183,7 +180,7 @@ const LinkRoot = styled('a', {
*/
const Link = React.forwardRef(function Link(inProps, ref) {
const {
- color: colorProp = 'primary',
+ color = 'primary',
textColor,
variant,
...themeProps
@@ -192,8 +189,6 @@ const Link = React.forwardRef(function Link(inProps, ref) {
name: 'JoyLink',
});
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, colorProp);
const nesting = React.useContext(TypographyNestedContext);
const inheriting = React.useContext(TypographyInheritContext);
diff --git a/packages/mui-joy/src/List/List.test.tsx b/packages/mui-joy/src/List/List.test.tsx
index 5959b877d3619f..8fe2fa12f5b6a2 100644
--- a/packages/mui-joy/src/List/List.test.tsx
+++ b/packages/mui-joy/src/List/List.test.tsx
@@ -1,11 +1,6 @@
import * as React from 'react';
import { expect } from 'chai';
-import {
- describeConformance,
- createRenderer,
- screen,
- describeJoyColorInversion,
-} from '@mui-internal/test-utils';
+import { describeConformance, createRenderer, screen } from '@mui-internal/test-utils';
import { ThemeProvider } from '@mui/joy/styles';
import List, { listClasses as classes } from '@mui/joy/List';
import ListItem from '@mui/joy/ListItem';
@@ -34,8 +29,6 @@ describe('Joy ', () => {
},
}));
- describeJoyColorInversion(, { muiName: 'JoyList', classes });
-
it('should have root className', () => {
const { container } = render();
expect(container.firstChild).to.have.class(classes.root);
diff --git a/packages/mui-joy/src/List/List.tsx b/packages/mui-joy/src/List/List.tsx
index 19eb0bec8922d3..7f411c1f56ea49 100644
--- a/packages/mui-joy/src/List/List.tsx
+++ b/packages/mui-joy/src/List/List.tsx
@@ -7,7 +7,7 @@ import { OverridableComponent } from '@mui/types';
import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses';
import { styled, useThemeProps } from '../styles';
import { resolveSxValue } from '../styles/styleUtils';
-import { useColorInversion } from '../styles/ColorInversion';
+
import { ListProps, ListOwnerState, ListTypeMap } from './ListProps';
import { getListUtilityClass } from './listClasses';
import NestedListContext from './NestedListContext';
@@ -184,14 +184,12 @@ const List = React.forwardRef(function List(inProps, ref) {
orientation = 'vertical',
wrap = false,
variant = 'plain',
- color: colorProp = 'neutral',
+ color = 'neutral',
role: roleProp,
slots = {},
slotProps = {},
...other
} = props;
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, colorProp);
const size = sizeProp || (inProps.size ?? 'md');
let role;
diff --git a/packages/mui-joy/src/ListItem/ListItem.test.tsx b/packages/mui-joy/src/ListItem/ListItem.test.tsx
index 9275155bcfbd99..e627bc6fa25599 100644
--- a/packages/mui-joy/src/ListItem/ListItem.test.tsx
+++ b/packages/mui-joy/src/ListItem/ListItem.test.tsx
@@ -1,11 +1,6 @@
import * as React from 'react';
import { expect } from 'chai';
-import {
- describeConformance,
- createRenderer,
- screen,
- describeJoyColorInversion,
-} from '@mui-internal/test-utils';
+import { describeConformance, createRenderer, screen } from '@mui-internal/test-utils';
import { ThemeProvider } from '@mui/joy/styles';
import MenuList from '@mui/joy/MenuList';
import List from '@mui/joy/List';
@@ -32,8 +27,6 @@ describe('Joy ', () => {
skip: ['componentsProp', 'classesRoot'],
}));
- describeJoyColorInversion(, { muiName: 'JoyListItem', classes });
-
it('should have root className', () => {
const { container } = render();
expect(container.firstChild).to.have.class(classes.root);
diff --git a/packages/mui-joy/src/ListItem/ListItem.tsx b/packages/mui-joy/src/ListItem/ListItem.tsx
index 69f0a1ed3e8d0d..9b49c821c9c832 100644
--- a/packages/mui-joy/src/ListItem/ListItem.tsx
+++ b/packages/mui-joy/src/ListItem/ListItem.tsx
@@ -9,7 +9,7 @@ import {
import { OverridableComponent } from '@mui/types';
import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses';
import { styled, useThemeProps } from '../styles';
-import { useColorInversion } from '../styles/ColorInversion';
+
import useSlot from '../utils/useSlot';
import { ListItemOwnerState, ListItemTypeMap } from './ListItemProps';
import { getListItemUtilityClass } from './listItemClasses';
@@ -170,7 +170,7 @@ const ListItem = React.forwardRef(function ListItem(inProps, ref) {
nested = false,
sticky = false,
variant = 'plain',
- color: colorProp = 'neutral',
+ color = 'neutral',
startAction,
endAction,
role: roleProp,
@@ -178,8 +178,6 @@ const ListItem = React.forwardRef(function ListItem(inProps, ref) {
slotProps = {},
...other
} = props;
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, colorProp);
const [subheaderId, setSubheaderId] = React.useState('');
diff --git a/packages/mui-joy/src/ListItemButton/ListItemButton.test.tsx b/packages/mui-joy/src/ListItemButton/ListItemButton.test.tsx
index 4d84724100881f..22b43ce0f88bef 100644
--- a/packages/mui-joy/src/ListItemButton/ListItemButton.test.tsx
+++ b/packages/mui-joy/src/ListItemButton/ListItemButton.test.tsx
@@ -1,12 +1,6 @@
import * as React from 'react';
import { expect } from 'chai';
-import {
- describeConformance,
- describeJoyColorInversion,
- createRenderer,
- act,
- fireEvent,
-} from '@mui-internal/test-utils';
+import { describeConformance, createRenderer, act, fireEvent } from '@mui-internal/test-utils';
import { ThemeProvider } from '@mui/joy/styles';
import ListItemButton, { listItemButtonClasses as classes } from '@mui/joy/ListItemButton';
@@ -30,8 +24,6 @@ describe('Joy ', () => {
},
}));
- describeJoyColorInversion(, { muiName: 'JoyListItemButton', classes });
-
it('should render with the selected class', () => {
const { getByRole } = render();
expect(getByRole('button')).to.have.class(classes.selected);
diff --git a/packages/mui-joy/src/ListItemButton/ListItemButton.tsx b/packages/mui-joy/src/ListItemButton/ListItemButton.tsx
index fce1b49843a663..a9712b5cb8a584 100644
--- a/packages/mui-joy/src/ListItemButton/ListItemButton.tsx
+++ b/packages/mui-joy/src/ListItemButton/ListItemButton.tsx
@@ -6,7 +6,7 @@ import { unstable_capitalize as capitalize, unstable_useForkRef as useForkRef }
import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses';
import { useButton } from '@mui/base/useButton';
import { styled, useThemeProps } from '../styles';
-import { useColorInversion } from '../styles/ColorInversion';
+
import {
ListItemButtonOwnerState,
ExtendListItemButton,
@@ -146,16 +146,13 @@ const ListItemButton = React.forwardRef(function ListItemButton(inProps, ref) {
orientation = 'horizontal',
role,
selected = false,
- color: colorProp = 'neutral',
+ color = 'neutral',
variant = 'plain',
slots = {},
slotProps = {},
...other
} = props;
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, colorProp);
-
const buttonRef = React.useRef(null);
const handleRef = useForkRef(buttonRef, ref);
diff --git a/packages/mui-joy/src/ListSubheader/ListSubheader.test.tsx b/packages/mui-joy/src/ListSubheader/ListSubheader.test.tsx
index 0e406e20ed9473..b49ab8229acd86 100644
--- a/packages/mui-joy/src/ListSubheader/ListSubheader.test.tsx
+++ b/packages/mui-joy/src/ListSubheader/ListSubheader.test.tsx
@@ -1,11 +1,7 @@
import * as React from 'react';
import { expect } from 'chai';
import { spy } from 'sinon';
-import {
- describeConformance,
- createRenderer,
- describeJoyColorInversion,
-} from '@mui-internal/test-utils';
+import { describeConformance, createRenderer } from '@mui-internal/test-utils';
import { ThemeProvider } from '@mui/joy/styles';
import ListSubheader, { listSubheaderClasses as classes } from '@mui/joy/ListSubheader';
import ListSubheaderDispatch from './ListSubheaderContext';
@@ -30,11 +26,6 @@ describe('Joy ', () => {
},
}));
- describeJoyColorInversion(, {
- muiName: 'JoyListSubheader',
- classes,
- });
-
it('should have root className', () => {
const { container } = render();
expect(container.firstChild).to.have.class(classes.root);
diff --git a/packages/mui-joy/src/ListSubheader/ListSubheader.tsx b/packages/mui-joy/src/ListSubheader/ListSubheader.tsx
index 2994fe22581af7..489946b6bbf73b 100644
--- a/packages/mui-joy/src/ListSubheader/ListSubheader.tsx
+++ b/packages/mui-joy/src/ListSubheader/ListSubheader.tsx
@@ -6,11 +6,11 @@ import { OverridableComponent } from '@mui/types';
import { unstable_useId as useId, unstable_capitalize as capitalize } from '@mui/utils';
import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses';
import { styled, useThemeProps } from '../styles';
-import { useColorInversion } from '../styles/ColorInversion';
import { ListSubheaderOwnerState, ListSubheaderTypeMap } from './ListSubheaderProps';
import { getListSubheaderUtilityClass } from './listSubheaderClasses';
import ListSubheaderDispatch from './ListSubheaderContext';
import useSlot from '../utils/useSlot';
+import { INVERTED_COLORS_ATTR } from '../colorInversion/colorInversionUtils';
const useUtilityClasses = (ownerState: ListSubheaderOwnerState) => {
const { variant, color, sticky } = ownerState;
@@ -49,10 +49,14 @@ const ListSubheaderRoot = styled('div', {
zIndex: 1,
background: 'var(--ListItem-stickyBackground)',
}),
- color:
- ownerState.color && ownerState.color !== 'context'
- ? `rgba(${theme.vars.palette[ownerState.color!]?.mainChannel} / 1)`
- : theme.vars.palette.text.tertiary,
+ color: ownerState.color
+ ? `var(--_Link-color, rgba(${theme.vars.palette[ownerState.color!]?.mainChannel} / 1))`
+ : theme.vars.palette.text.tertiary,
+ ...(ownerState.instanceColor && {
+ [`&:not([${INVERTED_COLORS_ATTR}])`]: {
+ '--_Link-color': theme.vars.palette.text.secondary,
+ },
+ }),
...theme.variants[ownerState.variant!]?.[ownerState.color!],
}));
/**
@@ -78,13 +82,11 @@ const ListSubheader = React.forwardRef(function ListSubheader(inProps, ref) {
id: idOverride,
sticky = false,
variant,
- color: colorProp,
+ color,
slots = {},
slotProps = {},
...other
} = props;
- const { getColor } = useColorInversion(variant);
- const color = getColor(inProps.color, colorProp);
const id = useId(idOverride);
const setSubheaderId = React.useContext(ListSubheaderDispatch);
@@ -95,6 +97,7 @@ const ListSubheader = React.forwardRef(function ListSubheader(inProps, ref) {
}, [setSubheaderId, id]);
const ownerState = {
+ instanceColor: inProps.color,
...props,
id,
sticky,
diff --git a/packages/mui-joy/src/ListSubheader/ListSubheaderProps.ts b/packages/mui-joy/src/ListSubheader/ListSubheaderProps.ts
index c372583eb5d884..9bda3152718767 100644
--- a/packages/mui-joy/src/ListSubheader/ListSubheaderProps.ts
+++ b/packages/mui-joy/src/ListSubheader/ListSubheaderProps.ts
@@ -57,4 +57,9 @@ export type ListSubheaderProps<
},
> = OverrideProps, D>;
-export interface ListSubheaderOwnerState extends ApplyColorInversion {}
+export interface ListSubheaderOwnerState extends ApplyColorInversion {
+ /**
+ * @internal
+ */
+ instanceColor?: OverridableStringUnion;
+}
diff --git a/packages/mui-joy/src/Menu/Menu.test.tsx b/packages/mui-joy/src/Menu/Menu.test.tsx
index 143b191faab91e..13221bcafb8d49 100644
--- a/packages/mui-joy/src/Menu/Menu.test.tsx
+++ b/packages/mui-joy/src/Menu/Menu.test.tsx
@@ -7,7 +7,6 @@ import {
describeConformance,
screen,
fireEvent,
- describeJoyColorInversion,
} from '@mui-internal/test-utils';
import { Popper as PopperUnstyled } from '@mui/base/Popper';
import { DropdownContext, DropdownContextValue } from '@mui/base/useDropdown';
@@ -62,15 +61,6 @@ describe('Joy ', () => {
const anchorEl = document.createElement('div');
anchorEl.setAttribute('aria-controls', 'test');
- describeJoyColorInversion(
-