Icons: maintain absolute stroke-width regardless of icon-size#78774
Conversation
Adds a strokeWidth prop to the React Icon component and expands the PHP icon registry's wp_kses allowlist to permit stroke-related attributes (stroke, stroke-width, stroke-linecap, stroke-linejoin, vector-effect, fill). Both changes are backwards compatible: the prop is a no-op for fill-based icons, and existing icons render unchanged. Updates square.svg to the new convention as a canary: moves stroke attributes to the outer <svg> so the prop can override them, and adds vector-effect="non-scaling-stroke" so the stroke renders at a constant pixel weight at any rendered size. Adds a Stroke width RangeControl to the icons library Storybook story. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
127390a to
ea092de
Compare
|
Size Change: +32 B (0%) Total Size: 8.18 MB 📦 View Changed
ℹ️ View Unchanged
|
t-hamano
left a comment
There was a problem hiding this comment.
Thanks for the PR.
I'm not sure if it's ideal to add a special prop just for stroke-width, as the Icon component already supports any additional props.
Doing that—redrawing the remaining icons to be stroke-based, in batches—is one of the followups after this PR, the other is to add the stroke property to the PHP counterpart as well, assuming #78332 lands.
Unfortunately, there are still many attributes that Icon Registry should support. See #75550 for more details. It might be best to exclude improvements to the Icons Registry from the scope of this pull request.
Per review feedback on #78774: - The Icon component already supports any SVG attribute via prop spread, so an explicit strokeWidth prop is redundant. - The PHP wp_kses allowlist expansion is better handled in the broader Icon Registry sanitizer overhaul in #75550. The square.svg convention update and the Stroke width Storybook RangeControl remain. Stroke-scaling at any rendered size continues to work via vector-effect="non-scaling-stroke" in the source SVG; nothing in this revert affects that behavior. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Solid feedback, thank you Aki for looking. I believe I addressed it, and updated the PR description to reflect the changes. Should we keep the stroke-width slider in the Storybook story, or would you prefer I remove that too? I think it can be useful to keep even without the prop, but happy to also remove it. |
I think what we want to test on Storybook going forward is not changing the stroke width, but ensuring that stroke-based icons display correctly at all icon sizes. If that's the case, I don't see the benefit of adding a stroke width control. Instead, should we allow previews in larger sizes in addition to 16, 24 and 32 pixels? |
Without a public stroke-width API surface on the Icon component, exposing a control for it in Storybook is misleading. The control can return as a follow-up once the icon registry and PHP support stabilise (see #75550, #78332). The square.svg convention update remains. Stroke-scaling at any size continues to work via vector-effect="non-scaling-stroke" in the source SVG. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Very fair point. It's easy to add back in the future, should we decide to, so for now I've removed the stroke-width slider. Honestly I think the existing sizes we have are fine to test the stroke-width maintenance so I've kept that as is. Happy toa dd 48 if you like, just LMK. |
|
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. |
t-hamano
left a comment
There was a problem hiding this comment.
For now, this should be sufficient. We can consider extending Storybook in the future if more stroke-based icons are added.
Could you update the PR title to reflect the actual changes before merging?
|
Done, thanks so much for all the help. I will merge if the checks pass, and follow up with icon-redrawing PRs. Let me know if the title change is sufficient! |
|
Flaky tests detected in 49858e5. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/26620902672
|
|
This PR seems to have caused unintended icon changes in the actual editor.
Should we perhaps revert this PR for now and explore a more ideal approach in #78808? |
|
Thanks for the prompt. In fact I found that issue and fixed it in #78808 (comment), so we can either revert and include the changes from this PR in 78808, or merge the latter. Both should fix it, and I'm happy with either approach. What do you think? |
|
Since we don't know when #78808 will be merged, let's revert this PR for now. |

What?
This PR that I created with help from Claude does a few things:
It adds a new strokeWidth prop to the Icon component, so that not only does the stroke-width remain absolute across different sizes, you can change it from the default 1.5px.vector-effect="non-scaling-stroke", the only existing icon that is in trunk that is already fully stroke-based.For the moment, the only visual change will be in Storybook, and that is intentional, the system is fully backwards compatible: existing icons will work just as they did, they'll just ignore the strokeWidth prop.
The purpose of this effort is to enable a parallel effort of redrawing our entire icon set from fills to strokes (which sounds like a lot, but isn't actually since that's already mostly the case in the Figma source of truth, it mostly needs re-exports).
Doing that—redrawing the remaining icons to be stroke-based, in batches—is one of the followups after this PR, the other is to add the stroke property to the PHP counterpart as well, assuming #78332 lands.
Before:
After:
Why?
The icon set is currently fill-based, which means it scales poorly, e.g. to 16×16 as used in places like the Badge component, or upwards to larger icons where suddenly fill-based strokes can be very thick and out of place in its context. While not used widely for that precise reason, larger icons can play a meaningful role in empty-states.
How?
This PR is intentionally invisible to existing consumers:
Changes
ReactIconcomponent — addsstrokeWidth?: numberprop, passed throughcloneElementto the outer<svg>when set.PHPWP_Icons_Registry::sanitize_icon_content()— addsstroke,stroke-width,stroke-linecap,stroke-linejoin,vector-effect, andfillto thewp_ksesallowlist on<svg>,<path>, and<polygon>so stroke-based icons survive registry load.square.svg(the only existing stroke-based icon) — updated to the new convention as a canary:stroke,stroke-width,fillmoved to the outer<svg>so thestrokeWidthprop can override them.vector-effect="non-scaling-stroke"added to the path so the stroke renders at a constant pixel weight at any size. At 24×24 this looks identical to before; at 16×16 and 32×32 it now renders at a true 1.5px instead of scaling down/up. The two existing consumers (zoom-out-toggle, HTML block modal) render at the default 24px so are visually unchanged.Stroke widthRangeControl (0.5–5, step 0.25) to the icons library story for visualising the system and spotting which icons still need redrawing.Usage
Source SVG convention (for the redraw effort)
Stroke-based icons should carry the defaults in their source files so the raw
.svgpreviews correctly outside its rendered context:square.svgis the reference.Follow-ups
PHPwp_get_icon()stroke_widtharg — blocked on #78332 landing. Will be a one-line addition towp_get_icon()that calls$processor->set_attribute( 'stroke-width', ... )alongside the existing args. Tests to follow in that PR.Testing instructions
square.squareicon should respond; fill-based icons (e.g.plus,check) should not change.<svg>in DevTools and confirmvector-effect="non-scaling-stroke"is present on thesquarepath and that the outer<svg>'sstroke-widthreflects the slider value.Use of AI Tools
This PR was authored with Claude Code (Claude Opus 4.7). Tested and reviewed by the author; Claude Code is credited via
Co-Authored-Byin the commit.