diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md index 89085ef5e9daa5..4f85a9bc19fb10 100644 --- a/packages/ui/CHANGELOG.md +++ b/packages/ui/CHANGELOG.md @@ -13,6 +13,7 @@ ### Enhancements +- `Button`, `IconButton`: Use the shared `Spinner` for loading indicators ([#84321](https://github.com/WordPress/gutenberg/pull/84321)). - `useEnableWpCompatOverlaySlot`: Mark as recommended for use in a WordPress environment ([#84305](https://github.com/WordPress/gutenberg/pull/84305)). ### Bug Fixes diff --git a/packages/ui/src/button/button.tsx b/packages/ui/src/button/button.tsx index 1584dd4aac6e24..7ee6fd47759fcc 100644 --- a/packages/ui/src/button/button.tsx +++ b/packages/ui/src/button/button.tsx @@ -4,6 +4,7 @@ import { speak } from '@wordpress/a11y'; import { forwardRef, useEffect } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { type ButtonProps } from './types'; +import { Spinner } from '../spinner'; import styles from './style.module.css'; import resetStyles from '../utils/css/resets.module.css'; import focusStyles from '../utils/css/focus.module.scss'; @@ -59,6 +60,9 @@ export const Button = forwardRef< HTMLButtonElement, ButtonProps >( { ...props } > { children } + { loading && variant !== 'unstyled' && ( + + ) } ); } diff --git a/packages/ui/src/button/style.module.css b/packages/ui/src/button/style.module.css index 758462870a1fd6..4bf721f6c76f35 100644 --- a/packages/ui/src/button/style.module.css +++ b/packages/ui/src/button/style.module.css @@ -109,41 +109,6 @@ color: GrayText; } } - - /* Loading spinner — not animated and hidden unless in loading state */ - &::before { - content: ""; - position: absolute; - top: 50%; - /* stylelint-disable-next-line logical-css/require-logical-properties -- Physical centering technique used with transform: translate(-50%, -50%) */ - left: 50%; - transform: translate(-50%, -50%); - - display: block; - box-sizing: border-box; - height: var(--wp-ui-button-font-size); - aspect-ratio: 1; - - border: var(--wpds-border-width-focus) solid; - border-block-start-color: var(--wp-ui-button-foreground-color); - border-inline-end-color: var(--wp-ui-button-foreground-color); - border-block-end-color: transparent; - border-inline-start-color: transparent; - border-radius: 50%; - - pointer-events: none; - opacity: 0; - - @media not ( prefers-reduced-motion ) { - transition: opacity 0.1s ease-out; - } - - @media ( forced-colors: active ) { - border-color: ButtonText; - border-block-end-style: none; - border-inline-start-style: none; - } - } } .is-small { @@ -245,29 +210,33 @@ color: ButtonFace; } - * { + > :not(.spinner) { opacity: 0; } + } + } - &::before { - transition-delay: 0.05s; - opacity: 1; + @layer compositions { + .spinner { + position: absolute; + inset: 0; + margin: auto; + width: var(--wp-ui-button-font-size); + height: var(--wp-ui-button-font-size); + color: var(--wp-ui-button-foreground-color); + pointer-events: none; - @media not ( prefers-reduced-motion ) { - animation: loading-animation 1s linear infinite; + @media ( prefers-reduced-motion ) { + > * { + animation: none; } } - } - - } - @keyframes loading-animation { - 0% { - transform: translate(-50%, -50%) rotate(0deg); - } - - 100% { - transform: translate(-50%, -50%) rotate(360deg); + @media ( forced-colors: active ) { + > * { + stroke: ButtonText; + } + } } } }