Skip to content

UI: Scale Spinner stroke width with its size - #85378

Open
mirka wants to merge 6 commits into
trunkfrom
ui-spinner-proportional-stroke
Open

mirka wants to merge 6 commits into
trunkfrom
ui-spinner-proportional-stroke

Conversation

@mirka

@mirka mirka commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

What?

Follow-up to the discussion in #84633.

Scale the @wordpress/ui Spinner stroke width with the spinner's rendered size, using the same proportions as the WordPress icons.

Why?

In the discussion on #84633, the small spinner looked heavy next to the thin plus and check icons. Spinner currently uses a 2px non-scaling stroke at every size, while icons draw a 1.5-unit stroke in a 24×24 viewBox and scale with their size. The suggestion there was for inline spinners to follow the same rules as icons.

How?

Use a 24×24 viewBox with a 1.5-unit stroke and remove vector-effect: non-scaling-stroke, so SVG scaling sets the rendered stroke width. The arc keeps its current geometry, a half-circle whose centerline sits on the edge of the box, so the shape, centering, overflow, and rotation are unchanged. Removing non-scaling-stroke from the previous 100×100 geometry alone would have given a stroke of 0.32px at 16px.

The rendered stroke is now the spinner size divided by 16:

Size Before After
8px 2px 0.5px
12px 2px 0.75px
16px (default) 2px 1px
20px 2px 1.25px
24px 2px 1.5px
40px 2px 2.5px

The default 16px spinner is thinner. Button loading spinners, sized to the button font size, go from 2px to about 1.49px at 13px. The validating spinner in form fields goes from 2px to 0.75px at 12px. Sizes above 32px are thicker than before. The color, forced-colors stroke, animation, role and focusable attributes, and sizing API are unchanged.

Button sets a heavier 2.75-unit stroke on its spinner, so the spinner matches the emphasized label weight. At the 13px button font size, this renders at about 1.49px, close to the 1.5px stroke that 24px icons have. The override lives in the Button stylesheet, in the compositions layer, so the Spinner default is unchanged. Without it, the Button spinner would be about 0.81px at 13px.

Testing Instructions

  1. In Storybook, open Components / @wordpress-ui / Spinner / Default. The arc should have a 1px stroke.
  2. Open Custom Size. The 40px arc should have a thicker 2.5px stroke.
  3. Open Components / @wordpress-ui / Button / Loading. The spinner should look slightly heavier than a standalone spinner of the same size, close to the weight of the button label.
  4. Repeat with Global CSS set to WordPress, and with forced colors emulated (DevTools → Rendering → Emulate CSS media feature forced-colors). The arc should stay visible and use the system text color.

Screenshots or screencast

Mode Before After
Font only Before: spinners at every size have the same 2px stroke, heavier than the icons at small sizes After: spinner strokes scale with size and match the icon stroke weight
WordPress global CSS Before, with WordPress global CSS: fixed 2px spinner strokes After, with WordPress global CSS: spinner strokes scale with size
WordPress global CSS, forced colors Before, in forced colors: fixed 2px spinner strokes in the system text color After, in forced colors: scaled spinner strokes remain visible in the system text color

Button loading spinner

The Button row in the table above predates the heavier Button stroke. Before uses the default 1.5-unit stroke, and after uses the 2.75-unit Button stroke. Idle labels are shown above the loading states for weight comparison, with a standalone 13px Spinner next to body text.

Mode Before After
Font only Before: Button loading spinners with a thin 0.81px stroke, lighter than the bold labels After: Button loading spinners with a heavier 1.49px stroke closer to the label weight, while the standalone spinner stays thin
WordPress global CSS Before, with WordPress global CSS: thin Button loading spinners After, with WordPress global CSS: heavier Button loading spinners
WordPress global CSS, forced colors Before, in forced colors: thin Button loading spinners in the button text color After, in forced colors: heavier Button loading spinners in the button text color

Use a 24-unit viewBox with a 1.5-unit stroke, the same proportions as
the icon library, and let SVG scaling set the rendered stroke width
instead of keeping a fixed 2px non-scaling stroke.
@mirka mirka added the [Type] Enhancement A suggestion for improvement. label Oct 22, 2026
@mirka mirka self-assigned this Oct 22, 2026
@github-actions github-actions Bot added the [Package] UI /packages/ui label Oct 22, 2026
@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

🤖 PR meta 🤖

Preview in WordPress Playground

🎉 Props

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: mirka <0mirka00@git.wordpress.org>
Co-authored-by: jasmussen <joen@git.wordpress.org>
Co-authored-by: jameskoster <jameskoster@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Updated as activity occurs, without notifying anyone named here. Add the props-bot label to refresh.

@mirka
mirka requested a review from a team October 22, 2026 14:16
@mirka
mirka marked this pull request as ready for review October 22, 2026 14:16
@mirka
mirka requested a review from a team as a code owner October 22, 2026 14:16
@jasmussen

Copy link
Copy Markdown
Contributor

I'll ultimately defer to @jameskoster and @ciampo on this, what works for the present state for icon scaling. But I'd like to revisit how we treat stroke width scaling above a certain size, I think there are aspects to the themeability that we can explore in the future.

@jameskoster

Copy link
Copy Markdown
Contributor

This is a bit tricky because if the principle is that the stroke-weight should make the spinner feel like part of the text adjacent, then in the button context this feels a little off because button label has emphasised weight. The spinner feels light in comparison. But next to body text this feels right.

Would it be too much to have an option for emphasised spinner stroke weight for these cases? Or perhaps that's just something the button should override.

@jasmussen

Copy link
Copy Markdown
Contributor

Would it be too much to have an option for emphasised spinner stroke weight for these cases? Or perhaps that's just something the button should override.

That's one of the things I was alluding to. I'd love to explore scaling the stroke-width according to the font-weight. So if you had a 60px font size at font-weight 100 (e.g. very thin), so would the stroke-width be thin, even if it was large.

mirka added 3 commits October 23, 2026 00:19
Match the emphasized label weight with a 1.5x stroke, set from the
Button stylesheet so the Spinner default stays unchanged.
@mirka

mirka commented Oct 22, 2026

Copy link
Copy Markdown
Member Author

I've appended new screenshots to the description that show the button spinners adjusted ad hoc to a heavier weight that I thought looked ok.

For now I think the ad hoc weight is fine. If we find ourselves wanting this a lot, I'm thinking we could perhaps have inline and standalone variants on Spinner, where we can try out different weight logic.

Sound good?

@ciampo ciampo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM code-wise, altohough it will need a design blessing ✨

This branch has not been deployed

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

Labels

[Package] UI /packages/ui [Type] Enhancement A suggestion for improvement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants