Skip to content

[system] Apply theme variants to all slots by default#48503

Open
starboyvarun wants to merge 1 commit into
mui:masterfrom
starboyvarun:44272-styled-slot-variants
Open

[system] Apply theme variants to all slots by default#48503
starboyvarun wants to merge 1 commit into
mui:masterfrom
starboyvarun:44272-styled-slot-variants

Conversation

@starboyvarun
Copy link
Copy Markdown
Contributor

Fixes #44272

Problem

theme.components[X].variants silently had no effect for any slot other than Root/root. The root cause: skipVariantsResolver defaulted to true for any non-root slot name (e.g. icon, label, track), which caused the variants resolution block to be skipped entirely.

This was inconsistent with styleOverrides, which already applies to every slot.

Fix

Remove the slot-name condition so skipVariantsResolver is false for all slots unless explicitly opted out with skipVariantsResolver: true.

Tests

  • Updated "variants should be skipped for non root slots" to reflect the new default (variants now apply to non-root slots)
  • Added "variants should be skipped for non-root slots when skipVariantsResolver is true" for the explicit opt-out case

@code-infra-dashboard
Copy link
Copy Markdown

code-infra-dashboard Bot commented May 8, 2026

Deploy preview

https://deploy-preview-48503--material-ui.netlify.app/

Bundle size

Bundle Parsed size Gzip size
@mui/material ▼-27B(-0.01%) ▼-16B(-0.01%)
@mui/lab 0B(0.00%) 0B(0.00%)
@mui/private-theming 0B(0.00%) 0B(0.00%)
@mui/system ▼-27B(-0.04%) ▼-15B(-0.06%)
@mui/utils 0B(0.00%) 0B(0.00%)

Details of bundle changes


Check out the code infra dashboard for more information about this PR.

Comment thread AGENTS.md Outdated
Comment on lines +204 to +219

## Merged / In-Review Contributions

| PR | Branch | Description | Author focus |
| :- | :----- | :---------- | :----------- |
| [#48500](https://github.com/mui/material-ui/pull/48500) | `36479-focus-trap-tabindex-order` | Fix FocusTrap `sentinelStart` `tabIndex` so it wins over positive-`tabIndex` children | oliviertassinari |
| [#48501](https://github.com/mui/material-ui/pull/48501) | `35939-typography-custom-variant-fontfamily` | Inject `fontFamily`/`allVariants` into custom typography variants in `createTypography` | siriwatknp |
| [#48502](https://github.com/mui/material-ui/pull/48502) | `39296-tabs-scroll-button-rtl-slots` | Fix `TabScrollButton` picking wrong slot icon in RTL mode | mj12albert |
| [#48503](https://github.com/mui/material-ui/pull/48503) | `44272-styled-slot-variants` | Remove `skipVariantsResolver=true` default for non-root slots so theme variants apply everywhere | siriwatknp |

### Key internals learned

- **`createStyled.js` `skipVariantsResolver`** — was `true` for any non-root slot; changing to `false` makes `theme.components[X].variants` consistent with `styleOverrides` (which already applies to all slots).
- **`FocusTrap` sentinel ordering** — `sentinelStart` must have `tabIndex={1}` (not `0`) so it beats any positive-`tabIndex` child in forward tab order and keeps focus trapped.
- **`createTypography` `other` spread** — custom variants passed via the `other` bag bypass `buildVariant`; must pre-process them to inject `fontFamily`/`allVariants` before `deepmerge`.
- **`TabScrollButton` RTL icon slots** — correct XOR: `(direction === 'left') !== isRtl` maps semantic Start/End slots to visual direction in both LTR and RTL.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are open to AI tools but please sanity check your output (manually) before opening PRs 🙏

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry about that — I accidentally staged a local scratch file I keep for notes while exploring the codebase. It shouldn't have been part of this PR at all. Removed it from the branch now.

: // TODO v6: remove `Root` in the next major release
// For more details: https://github.com/mui/material-ui/pull/37908
(componentSlot && componentSlot !== 'Root' && componentSlot !== 'root') || false;
const skipVariantsResolver = inputSkipVariantsResolver !== undefined ? inputSkipVariantsResolver : false;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this fix the repro in the linked issue?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes — the fix directly addresses the repro. The issue is that skipVariantsResolver defaulted to rue for any slot whose name was not 'Root'/'root', so heme.components[X].variants was silently skipped for slots like icon, label, etc. The styleOverrides block has no such restriction and already applies to all slots.

The one-line change removes that slot-name condition so variants follow the same rules as overrides. processStyleVariants already checks both props[key] and props.ownerState?.[key] when matching, so variant conditions that depend on ownerState props work correctly for non-root slots without any further changes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a look at the repro, can you explain a bit how you arrived at your conclusions?

@starboyvarun starboyvarun force-pushed the 44272-styled-slot-variants branch from 2a001c7 to 7cb8025 Compare May 10, 2026 18:35
Previously `skipVariantsResolver` defaulted to `true` for any non-root
slot, which silently prevented `theme.components[X].variants` from
applying to slots like `icon`, `label`, etc. This was inconsistent with
`styleOverrides`, which already works for all slots.

Remove the slot-name-based default so `skipVariantsResolver` is `false`
for all slots unless explicitly opted out.

Fixes mui#44272
@starboyvarun starboyvarun force-pushed the 44272-styled-slot-variants branch from 7cb8025 to 04b2cf7 Compare May 10, 2026 18:55
@zannager zannager added the scope: system The system, the design tokens / styling foundations used across components. eg. @mui/system with MUI label May 11, 2026
@starboyvarun
Copy link
Copy Markdown
Contributor Author

starboyvarun commented May 11, 2026

@mj12albert
The root cause is in the skipVariantsResolver default logic in createStyled.js. Before this fix, the default was computed as:

const skipVariantsResolver =
  inputSkipVariantsResolver !== undefined
    ? inputSkipVariantsResolver
    : (componentSlot && componentSlot !== 'Root' && componentSlot !== 'root') || false;

Any slot other than 'Root' / 'root' (e.g. slot: 'Icon') resulted in skipVariantsResolver = true. The styleThemeVariants function — which reads theme.components[ComponentName].variants and applies matching variants — is guarded by !skipVariantsResolver, so it never ran for non-root slots. Root slots worked fine because skipVariantsResolver stayed false for them.

The fix removes the slot-based default and always defaults to false, so theme variants apply to every slot consistently. Components that genuinely don't want variants on a slot can still pass skipVariantsResolver: true explicitly — there's a test covering that opt-out path.

@mj12albert
Copy link
Copy Markdown
Member

The root cause is in the skipVariantsResolver default logic in createStyled.js

It looks to me the repro doesn't use this API and involves a different code path

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: system The system, the design tokens / styling foundations used across components. eg. @mui/system with MUI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Variant for slot doesn't work

3 participants