Components: Consolidate focus style treatment#76249
Components: Consolidate focus style treatment#76249jameskoster wants to merge 2 commits intotrunkfrom
Conversation
…rings Standardize focus styles across the components package to use outline with outline-offset instead of box-shadow, matching the pattern established in the UI package. This provides a consistent outset focus ring with a 1px gap between the component and the ring. - Update shared focus mixins in base-styles to use outline properties and include outline-style/offset explicitly for robustness - Convert all component focus styles from box-shadow to outline (button, panel, modal, tab-panel, snackbar, color-palette, custom-gradient-picker, form-toggle, toolbar, dropdown-menu, input-control, checkbox-control, radio-control, range-control, border-control, textarea-control, unit-control, item-group, menu, tabs) - Move button outline transition into :focus rule so the ring animates in but disappears instantly on blur - Replace RangeControl thumb halo with standard outline focus ring - Separate hover and focus styles in BorderControl dropdown - Remove redundant outline:none/0 declarations that suppressed the new outline-based focus rings Made-with: Cursor
|
Size Change: -6.8 kB (-0.1%) Total Size: 6.87 MB
ℹ️ View Unchanged
|
|
Flaky tests detected in 5445469. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/22769951421
|
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
I wonder if there's an opportunity for consolidating focus styles in shared utilities, similarly to what done in gutenberg/packages/ui/src/utils/css/focus.module.css Lines 1 to 31 in def8695 cc @mirka as the author of those focus ring utilities |
There was a problem hiding this comment.
Pull request overview
Consolidates focus-ring styling across @wordpress/components by moving away from box-shadow/pseudo-element focus treatments toward an outline + outline-offset approach aligned with the existing @wordpress/ui pattern.
Changes:
- Updates shared Sass focus mixins to generate outline-based focus rings (and removes now-redundant WHCM fallbacks).
- Refactors multiple component styles (SCSS + Emotion) to use outline longhands for focus, often with motion-gated outline transitions.
- Documents the behavioral change in
packages/components/CHANGELOG.md.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/components/src/unit-control/styles/unit-control-styles.ts | Replaces box-shadow focus/hover treatments with outline-based transitions and focus rings for unit inputs/selects. |
| packages/components/src/toolbar/toolbar/style.scss | Suppresses inherited button outline and applies toolbar’s own outline-based ring via ::before. |
| packages/components/src/textarea-control/styles/textarea-control-styles.ts | Converts textarea focus styling from box-shadow to outline longhands + transition. |
| packages/components/src/tabs/styles.ts | Updates TabPanel focus-visible styling to use outline (with neutral base outline config). |
| packages/components/src/tab-panel/style.scss | Replaces pseudo-element/box-shadow focus indicators with outline-based focus-visible styling. |
| packages/components/src/snackbar/style.scss | Updates snackbar container focus to outline-based ring and adjusts action focus styling. |
| packages/components/src/range-control/styles/range-control-styles.ts | Replaces thumb “halo” pseudo-element focus effect with outline-based focus ring logic. |
| packages/components/src/panel/style.scss | Converts panel toggle focus styling from inset box-shadow to outline properties. |
| packages/components/src/modal/style.scss | Switches scrollable modal content focus-visible indicator from inset box-shadow to outline. |
| packages/components/src/menu/styles.ts | Converts menu item focus-visible styling to outline-based rings and removes outline suppression. |
| packages/components/src/item-group/styles.ts | Updates unstyled button focus-visible ring to outline-based approach with neutral base outline. |
| packages/components/src/input-control/styles/input-control-styles.tsx | Implements focus-within ring via Backdrop element outline instead of box-shadow (+ transition). |
| packages/components/src/form-toggle/style.scss | Adds outline ring support and includes outline in transition set. |
| packages/components/src/dropdown-menu/style.scss | Removes outline suppression on menu items to allow updated focus styling to show. |
| packages/components/src/custom-gradient-picker/style.scss | Adds outline-based focus ring on control-point button (and required theme variables import). |
| packages/components/src/color-palette/style.scss | Updates custom color button focus ring to outline-based thickness/color. |
| packages/components/src/checkbox-control/style.scss | Removes outline suppression to allow outline-based focus styles to render. |
| packages/components/src/button/style.scss | Establishes outline baseline on .components-button and uses outline-based focus ring with focus-only transition. |
| packages/components/src/border-control/styles.ts | Replaces focus box-shadow helper with outline helper; separates hover vs focus treatment. |
| packages/components/CHANGELOG.md | Notes the cross-components focus ring migration to outline-based styling. |
| packages/base-styles/_mixins.scss | Updates shared focus mixins (button, input, radio, checkbox, etc.) to output outline-based focus rings. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| outline-style: solid; | ||
| outline-color: transparent; | ||
| outline-offset: 1px; | ||
|
|
There was a problem hiding this comment.
.components-snackbar__action sets outline-style: solid and outline-color: transparent but does not reset outline-width to 0. Since the initial outline-width is medium, this can create an always-on outline in forced-colors/HC mode if colors are overridden. Set a zero-width (and optionally a transition under prefers-reduced-motion) in the base state, then only increase width on :focus/:focus-visible.
| outline-style: solid; | |
| outline-color: transparent; | |
| outline-offset: 1px; | |
| outline-width: 0; | |
| outline-style: solid; | |
| outline-color: transparent; | |
| outline-offset: 1px; | |
| @media not (prefers-reduced-motion) { | |
| transition: outline 0.1s ease-out; | |
| } |
| outline-width: var(--wp-admin-border-width-focus); | ||
| outline-style: solid; | ||
| outline-color: $components-color-accent; | ||
| outline-offset: 1px; |
There was a problem hiding this comment.
.components-modal__frame has overflow: hidden, so the outline-offset: 1px on .components-modal__content.is-scrollable:focus-visible is likely to be clipped at the frame edges (left/right and possibly top), reducing focus visibility. Consider drawing the outline inward (e.g., outline-offset: 0 or a negative offset) or otherwise ensuring there is space for the outline within the clipped container.
| outline-offset: 1px; | |
| outline-offset: 0; |
Part of #76135.
What
Replaces the variety of focus style techniques used across the components package (primarily
box-shadow) with a single, consistent approach usingoutlinewithoutline-offset, matching the pattern already established in the@wordpress/uipackage.20 files updated across shared mixins and individual components including Button, InputControl, CheckboxControl, RadioControl, RangeControl, TabPanel, Toolbar, Modal, Snackbar, BorderControl, and others.
Why
Focus styles in the components package have accumulated organically over time, resulting in a mix of techniques:
box-shadow(single and dual) to simulate focus ringsoutlinewith WHCM-onlytransparentfallbacks::before) backgrounds with opacityThis inconsistency creates visual and behavioral differences between components and makes the system harder to maintain.
Beyond consistency,
outlinewithoutline-offsetis the superior approach for focus rings because:box-shadow(which participates in visual overflow and can be clipped byoverflow: hiddenon ancestors), outlines are designed for this exact purpose.outline: 2px solid transparentfallback pattern that was scattered across many components.How
_mixins.scss): All focus mixins (block-toolbar-button-style__focus,input-style__focus,button-style__focus,button-style-outset__focus,checkbox-control,radio-control,link-reset,editor-input-reset) updated to useoutline-width,outline-style,outline-color, andoutline-offsetinstead ofbox-shadow. Focus rules explicitly setoutline-style: solidandoutline-offset: 1pxfor robustness against shorthand resets from external stylesheets.outline-width: 0; outline-style: solid; outline-color: transparent; outline-offset: 1px) set on.components-button. Thetransitionmoved from the base state into the:focusrule so the ring animates in but disappears instantly on blur, preventing flash artifacts during rapid focus navigation (e.g. arrow keys in toolbars).::beforehalo on the thumb replaced with a standard outline focus ring for consistency.outline: none), since the toolbar renders its own focus ring via::before.outline: none/outline: 0declarations that were suppressing the new outline-based focus rings (DropdownMenu, CheckboxControl), and removed now-redundant WHCMoutline: 2px solid transparentfallbacks from focus rules.Testing Instructions