@mui/material
Version:
Material UI is an open-source React component library that implements Google's Material Design. It's comprehensive and can be used in production out of the box.
495 lines (494 loc) • 15.5 kB
JavaScript
import accordionSummaryClasses from "../AccordionSummary/accordionSummaryClasses.mjs";
import autocompleteClasses from "../Autocomplete/autocompleteClasses.mjs";
import checkboxClasses from "../Checkbox/checkboxClasses.mjs";
import filledInputClasses from "../FilledInput/filledInputClasses.mjs";
import formControlLabelClasses from "../FormControlLabel/formControlLabelClasses.mjs";
import formHelperTextClasses from "../FormHelperText/formHelperTextClasses.mjs";
import formLabelClasses from "../FormLabel/formLabelClasses.mjs";
import inputClasses from "../Input/inputClasses.mjs";
import listItemButtonClasses from "../ListItemButton/listItemButtonClasses.mjs";
import menuItemClasses from "../MenuItem/menuItemClasses.mjs";
import nativeSelectClasses from "../NativeSelect/nativeSelectClasses.mjs";
import outlinedInputClasses from "../OutlinedInput/outlinedInputClasses.mjs";
import radioClasses from "../Radio/radioClasses.mjs";
import sliderClasses from "../Slider/sliderClasses.mjs";
import toggleButtonClasses from "../ToggleButton/toggleButtonClasses.mjs";
// System color keywords used in forced-colors / high contrast mode.
const defaultHcTokens = {
disabled: 'GrayText',
error: 'ActiveText',
selectedBackground: 'SelectedItem',
selectedText: 'SelectedItemText',
activeBackground: 'Highlight',
activeText: 'HighlightText',
buttonBorder: 'ButtonBorder',
buttonText: 'ButtonText',
canvas: 'Canvas'
};
const HCM = '@media (forced-colors: active)';
/**
* Enhances a theme with styles for Windows High Contrast Mode (forced-colors).
*
* Accepts a fully-created theme, merges in HCM component overrides using arrays
* so that Emotion emits each entry as a separate CSS rule and the browser
* cascade (rather than JS object merging) resolves specificity.
*
* @param themeInput - The theme to enhance.
* @param tokens - Override any of the default system color tokens.
* @returns The enhanced theme (same type as the input).
*
* @example
* // Use defaults
* const theme = enhanceHighContrast(createTheme({ palette: { ... } }));
*
* @example
* // Override specific tokens
* const theme = enhanceHighContrast(createTheme(), { disabled: 'ButtonText' });
*/
export default function enhanceHighContrast(themeInput, tokens) {
const hcTokens = {
disabled: tokens?.disabled ?? defaultHcTokens.disabled,
error: tokens?.error ?? defaultHcTokens.error,
selectedBackground: tokens?.selectedBackground ?? defaultHcTokens.selectedBackground,
selectedText: tokens?.selectedText ?? defaultHcTokens.selectedText,
activeBackground: tokens?.activeBackground ?? defaultHcTokens.activeBackground,
activeText: tokens?.activeText ?? defaultHcTokens.activeText,
buttonBorder: tokens?.buttonBorder ?? defaultHcTokens.buttonBorder,
buttonText: tokens?.buttonText ?? defaultHcTokens.buttonText,
canvas: tokens?.canvas ?? defaultHcTokens.canvas
};
const theme = {
...themeInput
};
const c = theme.components;
theme.components = {
...c,
MuiAccordionSummary: {
...c?.MuiAccordionSummary,
styleOverrides: {
...c?.MuiAccordionSummary?.styleOverrides,
root: [c?.MuiAccordionSummary?.styleOverrides?.root, {
[`&.${accordionSummaryClasses.disabled}`]: {
[HCM]: {
opacity: 1
}
}
}]
}
},
MuiAutocomplete: {
...c?.MuiAutocomplete,
styleOverrides: {
...c?.MuiAutocomplete?.styleOverrides,
listbox: [c?.MuiAutocomplete?.styleOverrides?.listbox, {
[`& .${autocompleteClasses.option}`]: {
'&[aria-disabled="true"]': {
[HCM]: {
color: hcTokens.disabled,
opacity: 1
}
},
[`&.${autocompleteClasses.focused}, &.${autocompleteClasses.focusVisible}`]: {
[HCM]: {
forcedColorAdjust: 'none',
color: hcTokens.activeText,
backgroundColor: hcTokens.activeBackground
}
},
'&[aria-selected="true"]': {
[HCM]: {
forcedColorAdjust: 'none',
color: hcTokens.selectedText,
backgroundColor: hcTokens.selectedBackground
},
[`&.${autocompleteClasses.focused}`]: {
[HCM]: {
color: hcTokens.activeText,
backgroundColor: hcTokens.activeBackground
}
}
}
}
}]
}
},
MuiCheckbox: {
...c?.MuiCheckbox,
styleOverrides: {
...c?.MuiCheckbox?.styleOverrides,
root: [c?.MuiCheckbox?.styleOverrides?.root, {
[`&.${checkboxClasses.disabled}`]: {
[HCM]: {
color: hcTokens.disabled
}
}
}]
}
},
MuiFilledInput: {
...c?.MuiFilledInput,
styleOverrides: {
...c?.MuiFilledInput?.styleOverrides,
root: [c?.MuiFilledInput?.styleOverrides?.root, {
[`&.${filledInputClasses.error}`]: {
'&::before, &::after': {
[HCM]: {
borderBottomColor: hcTokens.error
}
}
},
[`&.${filledInputClasses.disabled}:before`]: {
[HCM]: {
borderBottomStyle: 'solid',
borderBottomColor: hcTokens.disabled
}
},
[`&.${filledInputClasses.disabled}`]: {
[HCM]: {
color: hcTokens.disabled
}
}
}]
}
},
MuiFormControlLabel: {
...c?.MuiFormControlLabel,
styleOverrides: {
...c?.MuiFormControlLabel?.styleOverrides,
root: [c?.MuiFormControlLabel?.styleOverrides?.root, {
[`& .${formControlLabelClasses.label}.${formControlLabelClasses.disabled}`]: {
[HCM]: {
color: hcTokens.disabled
}
}
}]
}
},
MuiFormHelperText: {
...c?.MuiFormHelperText,
styleOverrides: {
...c?.MuiFormHelperText?.styleOverrides,
root: [c?.MuiFormHelperText?.styleOverrides?.root, {
[`&.${formHelperTextClasses.error}`]: {
[HCM]: {
color: hcTokens.error
}
},
[`&.${formHelperTextClasses.disabled}`]: {
[HCM]: {
color: hcTokens.disabled
}
}
}]
}
},
MuiFormLabel: {
...c?.MuiFormLabel,
styleOverrides: {
...c?.MuiFormLabel?.styleOverrides,
root: [c?.MuiFormLabel?.styleOverrides?.root, {
[`&.${formLabelClasses.error}`]: {
[HCM]: {
color: hcTokens.error
}
},
[`&.${formLabelClasses.disabled}`]: {
[HCM]: {
color: hcTokens.disabled
}
}
}]
}
},
MuiInput: {
...c?.MuiInput,
styleOverrides: {
...c?.MuiInput?.styleOverrides,
root: [c?.MuiInput?.styleOverrides?.root, {
[`&.${inputClasses.error}`]: {
'&::before, &::after': {
[HCM]: {
borderBottomColor: hcTokens.error
}
}
},
[`&.${inputClasses.disabled}:before`]: {
[HCM]: {
borderBottomStyle: 'solid',
borderBottomColor: hcTokens.disabled
}
},
[`&.${inputClasses.disabled}`]: {
[HCM]: {
color: hcTokens.disabled
}
}
}]
}
},
MuiLinearProgress: {
...c?.MuiLinearProgress,
styleOverrides: {
...c?.MuiLinearProgress?.styleOverrides,
root: [c?.MuiLinearProgress?.styleOverrides?.root, {
[HCM]: {
forcedColorAdjust: 'none',
outline: `1px solid ${hcTokens.buttonBorder}`,
backgroundColor: hcTokens.canvas
}
}],
bar: [c?.MuiLinearProgress?.styleOverrides?.bar, {
[HCM]: {
backgroundColor: hcTokens.buttonText
}
}],
bar2: [c?.MuiLinearProgress?.styleOverrides?.bar2, {
variants: [{
props: {
variant: 'buffer'
},
style: {
[HCM]: {
backgroundColor: hcTokens.disabled
}
}
}]
}]
}
},
MuiInputBase: {
...c?.MuiInputBase,
styleOverrides: {
...c?.MuiInputBase?.styleOverrides,
input: [c?.MuiInputBase?.styleOverrides?.input, {
[HCM]: {
'&::placeholder': {
opacity: 1
}
}
}]
}
},
MuiMenuItem: {
...c?.MuiMenuItem,
styleOverrides: {
...c?.MuiMenuItem?.styleOverrides,
root: [c?.MuiMenuItem?.styleOverrides?.root, {
[`&.${menuItemClasses.disabled}`]: {
[HCM]: {
color: hcTokens.disabled,
opacity: 1
}
},
[`&.${menuItemClasses.focusVisible}, &:hover`]: {
[HCM]: {
forcedColorAdjust: 'none',
color: hcTokens.activeText,
backgroundColor: hcTokens.activeBackground,
outline: 'none'
}
},
[`&.${menuItemClasses.selected}`]: {
[HCM]: {
forcedColorAdjust: 'none',
color: hcTokens.selectedText,
backgroundColor: hcTokens.selectedBackground
}
},
[`&.${menuItemClasses.selected}.${menuItemClasses.focusVisible}, &.${menuItemClasses.selected}:hover`]: {
[HCM]: {
color: hcTokens.activeText,
backgroundColor: hcTokens.activeBackground
}
}
}]
}
},
MuiListItemIcon: {
...c?.MuiListItemIcon,
styleOverrides: {
...c?.MuiListItemIcon?.styleOverrides,
root: [c?.MuiListItemIcon?.styleOverrides?.root, {
[HCM]: {
color: 'inherit'
}
}]
}
},
MuiListItemButton: {
...c?.MuiListItemButton,
styleOverrides: {
...c?.MuiListItemButton?.styleOverrides,
root: [c?.MuiListItemButton?.styleOverrides?.root, {
[`&.${listItemButtonClasses.focusVisible}, &:hover`]: {
[HCM]: {
forcedColorAdjust: 'none',
color: hcTokens.activeText,
backgroundColor: hcTokens.activeBackground
}
},
[`&.${listItemButtonClasses.selected}`]: {
[HCM]: {
forcedColorAdjust: 'none',
color: hcTokens.selectedText,
backgroundColor: hcTokens.selectedBackground
}
},
[`&.${listItemButtonClasses.selected}.${listItemButtonClasses.focusVisible}, &.${listItemButtonClasses.selected}:hover`]: {
[HCM]: {
color: hcTokens.activeText,
backgroundColor: hcTokens.activeBackground
}
}
}]
}
},
MuiNativeSelect: {
...c?.MuiNativeSelect,
styleOverrides: {
...c?.MuiNativeSelect?.styleOverrides,
icon: [c?.MuiNativeSelect?.styleOverrides?.icon, {
[`&.${nativeSelectClasses.disabled}`]: {
[HCM]: {
color: hcTokens.disabled
}
}
}]
}
},
MuiOutlinedInput: {
...c?.MuiOutlinedInput,
styleOverrides: {
...c?.MuiOutlinedInput?.styleOverrides,
root: [c?.MuiOutlinedInput?.styleOverrides?.root, {
[`&.${outlinedInputClasses.error} .${outlinedInputClasses.notchedOutline}`]: {
[HCM]: {
borderColor: hcTokens.error
}
},
[`&.${outlinedInputClasses.disabled} .${outlinedInputClasses.notchedOutline}`]: {
[HCM]: {
borderColor: hcTokens.disabled
}
},
[`&.${outlinedInputClasses.disabled}`]: {
[HCM]: {
color: hcTokens.disabled
}
}
}]
}
},
MuiRadio: {
...c?.MuiRadio,
styleOverrides: {
...c?.MuiRadio?.styleOverrides,
root: [c?.MuiRadio?.styleOverrides?.root, {
[`&.${radioClasses.disabled}`]: {
[HCM]: {
color: hcTokens.disabled
}
}
}]
}
},
MuiSlider: {
...c?.MuiSlider,
styleOverrides: {
...c?.MuiSlider?.styleOverrides,
// track doesn't receive the disabled class — use ownerState
track: [c?.MuiSlider?.styleOverrides?.track, ({
ownerState
}) => ({
...(ownerState.disabled && {
[HCM]: {
borderColor: hcTokens.disabled
}
})
})],
// thumb receives the disabled class directly
thumb: [c?.MuiSlider?.styleOverrides?.thumb, {
[`&.${sliderClasses.disabled}`]: {
[HCM]: {
borderColor: hcTokens.disabled
}
}
}]
}
},
MuiSwitch: {
...c?.MuiSwitch,
styleOverrides: {
...c?.MuiSwitch?.styleOverrides,
// track and thumb don't receive the disabled class — use ownerState
track: [c?.MuiSwitch?.styleOverrides?.track, ({
ownerState
}) => ({
...(ownerState.disabled && {
[HCM]: {
borderColor: hcTokens.disabled
}
})
})],
thumb: [c?.MuiSwitch?.styleOverrides?.thumb, ({
ownerState
}) => ({
...(ownerState.disabled && {
[HCM]: {
borderColor: hcTokens.disabled
}
})
})]
}
},
MuiButtonBase: {
...c?.MuiButtonBase,
styleOverrides: {
...c?.MuiButtonBase?.styleOverrides,
root: [c?.MuiButtonBase?.styleOverrides?.root, {
// Restore the focus outline in HCM since the ripple is not visible.
// Also handle components where the focusable element is a hidden inner input (Checkbox, Radio, Switch).
[HCM]: {
'&:focus-visible, &:focus-within:has(input:focus-visible)': {
outline: `5px auto ${hcTokens.activeBackground}`
}
}
}]
}
},
MuiTooltip: {
...c?.MuiTooltip,
styleOverrides: {
...c?.MuiTooltip?.styleOverrides,
tooltip: [c?.MuiTooltip?.styleOverrides?.tooltip, {
[HCM]: {
border: `1px solid ${hcTokens.buttonText}`
}
}]
}
},
MuiToggleButton: {
...c?.MuiToggleButton,
styleOverrides: {
...c?.MuiToggleButton?.styleOverrides,
root: [c?.MuiToggleButton?.styleOverrides?.root, {
[`&.${toggleButtonClasses.selected}`]: {
[HCM]: {
forcedColorAdjust: 'none',
color: hcTokens.activeText,
backgroundColor: hcTokens.activeBackground,
borderColor: hcTokens.activeBackground
},
'&:hover': {
[HCM]: {
backgroundColor: hcTokens.activeBackground,
borderColor: hcTokens.buttonBorder
}
}
}
}]
}
}
};
return theme;
}