Skip to content

[ButtonBase] Add theme.focusRing for keyboard focus ring#48647

Draft
siriwatknp wants to merge 2 commits into
mui:masterfrom
siriwatknp:exp/focus-ring
Draft

[ButtonBase] Add theme.focusRing for keyboard focus ring#48647
siriwatknp wants to merge 2 commits into
mui:masterfrom
siriwatknp:exp/focus-ring

Conversation

@siriwatknp

@siriwatknp siriwatknp commented Jun 10, 2026

Copy link
Copy Markdown
Member

Preview: https://deploy-preview-48647--material-ui.netlify.app/experiments/focus-ring/

Experiment

Adds a keyboard focus ring to ButtonBase (and its derived components), rendered with CSS outline on Mui-focusVisible.

Two behaviors, one renderer

  • A11y fallback (automatic): when disableRipple is set, the ripple — the only keyboard-focus indicator for ripple-pulse components (Button, IconButton, Fab, Tab…) — disappears, failing WCAG 2.4.7. The ring fills that gap automatically, no config needed. Default outlineColor: palette.primary.main, outlineWidth: 2, outlineOffset: 2, outlineStyle: solid.
  • Opt-in theme: theme.focusRing shows the ring on every keyboard focus, regardless of ripple. Typed as the outline subset of React.CSSProperties (outlineColor/outlineWidth/outlineOffset/outlineStyle) and spread directly onto the focus-visible styles — no field mapping.
  • Kill-switch: theme.focusRing: false disables the ring entirely, including the fallback.
createTheme({ focusRing: { outlineColor: '#9c27b0', outlineOffset: 3 } });
createTheme({ focusRing: false }); // opt out of the a11y fallback too

Why outline

Survives Windows High Contrast / forced-colors (where box-shadow and background tints are stripped), has zero layout shift, and avoids colliding with the box-shadow elevation Button/Fab already animate on focus. See docs/adr/0001.

Demo

docs/pages/experiments/focus-ring.tsx — fallback / opt-in / kill-switch side by side. Tab through with the keyboard (ring is Mui-focusVisible-only).

Notes / open questions

  • Behavior change: apps that already set disableRipple gain a visible focus outline — intended, but worth release-notes.
  • Scope is ButtonBase + derived only; other focusable components (TextField, Checkbox…) are out of scope here.
  • Design rationale lives in CONTEXT.md + docs/adr/ (included for review; can be dropped before merge).

Draft — opening for discussion on the API shape and default color.

Render an outline focus ring on Mui-focusVisible:
- auto fallback when disableRipple removes the ripple focus indicator
- opt-in via theme.focusRing (outline CSSProperties), ripple-independent
- theme.focusRing: false hard-disables the ring

Experiment: design in CONTEXT.md + docs/adr, demo at
docs/pages/experiments/focus-ring.tsx.
@code-infra-dashboard

code-infra-dashboard Bot commented Jun 10, 2026

Copy link
Copy Markdown

Deploy preview

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

Bundle size

Bundle Parsed size Gzip size
@mui/material 🔺+252B(+0.05%) 🔺+70B(+0.05%)
@mui/lab 0B(0.00%) 0B(0.00%)
@mui/private-theming 0B(0.00%) 0B(0.00%)
@mui/system 0B(0.00%) 0B(0.00%)
@mui/utils 0B(0.00%) 0B(0.00%)

Details of bundle changes


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

@siriwatknp siriwatknp added the RFC Request For Comments. label Jun 10, 2026
@mj12albert

mj12albert commented Jun 10, 2026

Copy link
Copy Markdown
Member

I kind of needed a focus ring in #48613 already and added it when focusableWhenDisabled is enabled

});
}

// focusRing theme key

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.

Is "focus ring" the best name for this? Alternatives could be "focus outline" or "focus indicator"

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

Labels

component: ButtonBase The React component. RFC Request For Comments.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants