Steps to reproduce
Steps:
- Open this link to the live example;
- See that there is a
Link rendered with a valid CSS color keyword and underline="always";
- See that
theme.default.ts contains a MuiTypography variant which defines the CSS color for color="white";
- The
Link is wrapped in an ErrorBoundary so the render failure is visible in the example;
- Observe in the console that rendering the
Link throws MUI: Unsupported "white" color. This is an unhandled exception that breaks the entire app;
Current behavior
-
MUI does not handle the runtime exception caused by its internal underline-color calculation. The exception escapes from Link during render and can therefore propagate to the application's ErrorBoundary. If the boundary is at the application level, a color used for a link can effectively take down the entire application UI.
-
A valid color value that is already handled by the component's themed styling should not cause Link to fail because a separate internal implementation of the underline does not know how to process that same color.
-
In this case, the color is already explicitly resolved by the MuiTypography theme configuration. Link should either use the resulting/resolved color for its underline styling, or otherwise avoid independently interpreting the original semantic color prop using a more restrictive color parser.
-
There also appears to be no supported way to opt out of this additional underline-color processing while continuing to use MUI's built-in underline variants.
For example, I would expect to be able to define the three standard states in the normal MUI way:
underlineAlways: ({ theme }) => ({
...{some style, expecting textDecoration: 'underline' is already applied out of the box}
}),
underlineHover: ({ theme }) => ({
...{some style, expecting textDecoration: 'underline' is already applied in the hover selector out of the box}
}),
underlineNone: ({ theme }) => ({
...{some style, expecting textDecoration: 'none' is already applied out of the box}
}),
However, underline="always" performs additional internal color processing, which is not performed in the same way by the other underline states and which cannot be controlled through these style overrides.
The only way to override the color used by that implementation is to know about and explicitly set the internal CSS variable:
'--Link-underlineColor': theme.palette.main.white,
This means that customizing what appears to be a straightforward text-decoration state unexpectedly requires knowledge of an internal underline-color mechanism and its CSS variable.
More importantly, there does not appear to be an option equivalent to something such as disableRipple that disables MUI's additional underline customization while preserving the underline API and its state semantics.
As a result, to avoid this behavior entirely, I have to stop using MUI's built-in underline states:
defaultProps: {
underline: 'none',
},
and define three separate variants:
declare module '@mui/material/Link' {
interface LinkPropsVariantOverrides {
underline_always: true;
underline_hover: true;
underline_never: true;
}
I then have to reimplement textDecoration: 'underline', its removal, and the hover behavior for those variants myself.
This seems unnecessarily difficult to opt out of for what is ultimately underline presentation. The built-in underline API couples basic text-decoration behavior to an additional color-transformation mechanism which (a) can throw during render for otherwise valid/themed colors, (b) is not controlled by the corresponding style overrides, and (c) cannot be disabled independently without abandoning the built-in underline states and recreating them.
Expected behavior
- MUI should not allow an internal styling calculation to cause the entire application subtree to crash. Calls such as
alpha(color, 0.4) should only be made after their inputs have been validated, or the failure should otherwise be handled gracefully.
- A valid
color value which is accepted and handled by the component's themed styling should not cause Link to throw while calculating its underline style. Either the accepted color type should reflect the actual restrictions of the internal color processing, or that processing should support valid CSS color values such as named colors.
- Theme-level component customization should be respected consistently. In this case,
MuiTypography explicitly defines how color="white" is styled, but Link independently processes the original unresolved prop value when calculating the underline color. The underline implementation should not bypass or conflict with the color styling already defined through the theme.
- There should be a simple supported way to disable MUI's additional underline-color processing while retaining the built-in
underline states and their basic text-decoration behavior. Customizing Link underline styles should not require relying on an internal CSS variable or completely reimplementing the always, hover, and none behaviors as custom variants.
Context
We encountered this in an application after changing our Link theme override so that links use underline="always".
Existing usages with color="white" had previously rendered normally. Once the underline became always, the application started to crash, seemingly at random.
And our theme already defines the intended styling for Typography with color="white", so we didn't expect any issues with it at all.
Your environment
- On StackBlitz:
System:
OS: Linux 5.0 undefined
Binaries:
Node: 22.22.3 - /usr/local/bin/node
npm: 10.8.2 - /usr/local/bin/npm
pnpm: 8.15.6 - /usr/local/bin/pnpm
Browsers:
Chrome: Not Found
Firefox: Not Found
npmPackages:
@emotion/react: latest => 11.14.0
@emotion/styled: latest => 11.14.1
@mui/core-downloads-tracker: 9.4.0
@mui/icons-material: latest => 9.4.0
@mui/material: latest => 9.4.0
@mui/private-theming: 9.4.0
@mui/styled-engine: 9.4.0
@mui/system: 9.4.0
@mui/types: 9.4.0
@mui/utils: 9.4.0
@types/react: latest => 19.2.18
@webassembly/typescript: 7.0.2
react: latest => 19.2.8
react-dom: latest => 19.2.8
- On our project (I know you don't support v6, but you have exactly the same code in all higher versions):
System:
OS: macOS 26.6.2
Binaries:
Node: 24.14.1 - /Users/romannakoval/.volta/tools/image/node/24.14.1/bin/node
npm: 11.11.0 - /Users/romannakoval/.volta/tools/image/node/24.14.1/bin/npm
pnpm: Not Found
Browsers:
Chrome: 152.0.7977.65
Edge: Not Found
Firefox: 154.0
Safari: 26.6.2
npmPackages:
@emotion/react: 11.14.0 => 11.14.0
@emotion/styled: 11.14.0 => 11.14.0
@mui/core-downloads-tracker: 6.5.0
@mui/material: 6.5.0 => 6.5.0
@mui/private-theming: 6.4.9
@mui/styled-engine: 6.5.0
@mui/system: 6.5.0 => 6.5.0
@mui/types: 7.2.24
@mui/utils: 6.4.9
@types/react: 18.3.12 => 18.3.12
react: 18.3.1 => 18.3.1
react-dom: 18.3.1 => 18.3.1
typescript: 5.9.3 => 5.9.3
Browsers: both FF and Chrome
Search keywords: Link, Typography, color, theme variant, white, CSS color, underline, alpha, getTextDecoration, unsupported color, ErrorBoundary, app crush
Steps to reproduce
Steps:
Linkrendered with a valid CSScolorkeyword andunderline="always";theme.default.tscontains aMuiTypographyvariant which defines the CSS color forcolor="white";Linkis wrapped in anErrorBoundaryso the render failure is visible in the example;LinkthrowsMUI: Unsupported "white" color. This is an unhandled exception that breaks the entire app;Current behavior
MUI does not handle the runtime exception caused by its internal underline-color calculation. The exception escapes from
Linkduring render and can therefore propagate to the application'sErrorBoundary. If the boundary is at the application level, a color used for a link can effectively take down the entire application UI.A valid
colorvalue that is already handled by the component's themed styling should not causeLinkto fail because a separate internal implementation of the underline does not know how to process that same color.In this case, the color is already explicitly resolved by the
MuiTypographytheme configuration.Linkshould either use the resulting/resolved color for its underline styling, or otherwise avoid independently interpreting the original semanticcolorprop using a more restrictive color parser.There also appears to be no supported way to opt out of this additional underline-color processing while continuing to use MUI's built-in
underlinevariants.For example, I would expect to be able to define the three standard states in the normal MUI way:
However,
underline="always"performs additional internal color processing, which is not performed in the same way by the other underline states and which cannot be controlled through these style overrides.The only way to override the color used by that implementation is to know about and explicitly set the internal CSS variable:
This means that customizing what appears to be a straightforward
text-decorationstate unexpectedly requires knowledge of an internal underline-color mechanism and its CSS variable.More importantly, there does not appear to be an option equivalent to something such as
disableRipplethat disables MUI's additional underline customization while preserving theunderlineAPI and its state semantics.As a result, to avoid this behavior entirely, I have to stop using MUI's built-in underline states:
and define three separate variants:
I then have to reimplement
textDecoration: 'underline', its removal, and the hover behavior for those variants myself.This seems unnecessarily difficult to opt out of for what is ultimately underline presentation. The built-in
underlineAPI couples basic text-decoration behavior to an additional color-transformation mechanism which (a) can throw during render for otherwise valid/themed colors, (b) is not controlled by the corresponding style overrides, and (c) cannot be disabled independently without abandoning the built-in underline states and recreating them.Expected behavior
alpha(color, 0.4)should only be made after their inputs have been validated, or the failure should otherwise be handled gracefully.colorvalue which is accepted and handled by the component's themed styling should not causeLinkto throw while calculating its underline style. Either the acceptedcolortype should reflect the actual restrictions of the internal color processing, or that processing should support valid CSS color values such as named colors.MuiTypographyexplicitly defines howcolor="white"is styled, butLinkindependently processes the original unresolved prop value when calculating the underline color. The underline implementation should not bypass or conflict with the color styling already defined through the theme.underlinestates and their basictext-decorationbehavior. CustomizingLinkunderline styles should not require relying on an internal CSS variable or completely reimplementing thealways,hover, andnonebehaviors as custom variants.Context
We encountered this in an application after changing our
Linktheme override so that links useunderline="always".Existing usages with
color="white"had previously rendered normally. Once the underline becamealways, the application started to crash, seemingly at random.And our theme already defines the intended styling for
Typographywithcolor="white", so we didn't expect any issues with it at all.Your environment
Browsers: both FF and Chrome
Search keywords: Link, Typography, color, theme variant, white, CSS color, underline, alpha, getTextDecoration, unsupported color, ErrorBoundary, app crush