Skip to content

Icons: self declare color#79320

Merged
mirka merged 11 commits into
trunkfrom
update/icons-self-declare-color
Jun 24, 2026
Merged

Icons: self declare color#79320
mirka merged 11 commits into
trunkfrom
update/icons-self-declare-color

Conversation

@jasmussen

@jasmussen jasmussen commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

What?

Adds fill="currentColor" to the outer <svg> tag of all 330 icons in @wordpress/icons, so every icon self-declares its colour intent at the source.

As commit number two, to keep or delete as we decide, it adds a small normalizeSourceIcons helper to packages/icons/lib/generate-library.cjs that performs this normalisation. The helper exists as a manual maintenance tool, to be invoked on demand when new icons are added.

Why?

Follow-up to review feedback.

Delivered as a separate PR for ease of review. It's a big diff because it touches all icons, but it's largely mechanical, but large.

How?

-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">

The script skips any SVGs whose outer already declares fill= as an attribute, or declares fill inside a style attribute (e.g. stroke-based icons that use style="fill: none"). Children are untouched.

To re-run after adding new icons:

node -e "require('./packages/icons/lib/generate-library.cjs').normalizeSourceIcons()"

Testing Instructions

  • npm run storybook:dev
  • Confirm Storybook renders the icon library identically to trunk.

This PR was co-authored with help of Claude Opus 4.8.


✍️ Dev note for WP 7.1

Icon colors should be customized with color

Since @wordpress/icons version 15.0.0, each individual icon in the library now defines how they inherit currentColor internally. By default, every icon renders using the current text color.

If your code tints icons by setting fill, switch to color instead:

/* Before */
.my-icon {
	fill: #3858e9;
}

/* After */
.my-icon {
	color: #3858e9;
}

This is more reliable because icons may use fill, stroke, or a mix of both internally. Going forward, color is the recommended way to tint icons.

Adds `fill="currentColor"` to the outer `<svg>` of every icon in
@wordpress/icons. This makes each icon self-describe its colour intent at
the source, so consumers don't need to inject `fill: currentColor` via CSS
to get the expected rendering.

Per-file diff is mechanical and uniform:
    <svg ... viewBox="0 0 24 24">
becomes
    <svg ... viewBox="0 0 24 24" fill="currentColor">

330 SVGs touched. Children are unaffected; child elements that declare
their own `fill` (e.g. `fill="none"` on stroke icons) keep it.
Adds an idempotent maintenance script that normalises source SVGs in
`packages/icons/src/library` so every icon self-declares its colour via
`fill="currentColor"` on the outer `<svg>` element.

Not wired into `main()` — this is a manual one-off, intended to be invoked
on demand:

    node -e "require('./packages/icons/lib/generate-library.cjs').normalizeSourceIcons()"

Skips icons that already declare `fill=` as an attribute or have `fill`
declared inside a `style` attribute on the outer `<svg>` (e.g. stroke-based
icons using `style="fill: none"` to defeat 3rd-party CSS overrides).
@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown

Size Change: +864 B (+0.01%)

Total Size: 7.51 MB

📦 View Changed
Filename Size Change
build/modules/boot/index.min.js 52.9 kB +11 B (+0.02%)
build/modules/content-types/index.min.js 159 kB +26 B (+0.02%)
build/modules/edit-site-init/index.min.js 1.42 kB +15 B (+1.07%)
build/modules/workflow/index.min.js 19.9 kB +11 B (+0.06%)
build/scripts/block-directory/index.min.js 43.5 kB +12 B (+0.03%)
build/scripts/block-editor/index.min.js 382 kB +34 B (+0.01%)
build/scripts/block-library/index.min.js 324 kB +65 B (+0.02%)
build/scripts/commands/index.min.js 21 kB +10 B (+0.05%)
build/scripts/components/index.min.js 264 kB +19 B (+0.01%)
build/scripts/core-commands/index.min.js 4.35 kB +13 B (+0.3%)
build/scripts/customize-widgets/index.min.js 14.6 kB +14 B (+0.1%)
build/scripts/edit-post/index.min.js 52.9 kB +19 B (+0.04%)
build/scripts/edit-site/index.min.js 299 kB +511 B (+0.17%)
build/scripts/edit-widgets/index.min.js 22.2 kB +15 B (+0.07%)
build/scripts/editor/index.min.js 475 kB +52 B (+0.01%)
build/scripts/format-library/index.min.js 30.8 kB +6 B (+0.02%)
build/scripts/media-utils/index.min.js 114 kB +22 B (+0.02%)
build/scripts/patterns/index.min.js 12.2 kB +11 B (+0.09%)
build/scripts/plugins/index.min.js 2.93 kB +12 B (+0.41%)
build/scripts/preferences/index.min.js 3.31 kB +14 B (+0.42%)
build/scripts/reusable-blocks/index.min.js 3.11 kB +14 B (+0.45%)
build/scripts/widgets/index.min.js 7.81 kB +14 B (+0.18%)
build/styles/edit-site/style-rtl.css 22.1 kB -4 B (-0.02%)
build/styles/edit-site/style-rtl.min.css 18.2 kB -10 B (-0.05%)
build/styles/edit-site/style.css 22.1 kB -4 B (-0.02%)
build/styles/edit-site/style.min.css 18.2 kB -8 B (-0.04%)
build/styles/editor/style-rtl.css 30.7 kB -5 B (-0.02%)
build/styles/editor/style-rtl.min.css 26.2 kB -10 B (-0.04%)
build/styles/editor/style.css 30.8 kB -5 B (-0.02%)
build/styles/editor/style.min.css 26.1 kB -10 B (-0.04%)

compressed-size-action

Mechanical follow-up to the source SVG change — Jest snapshots that
render `@wordpress/icons` icons now reflect the new `fill="currentColor"`
attribute on the outer `<SVG>` element.

21 snapshots refreshed across 12 test suites.
@jasmussen jasmussen requested review from a team, ajitbohra, ellatrix and fabiankaegy as code owners June 18, 2026 13:57
@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown

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 props-bot label.

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: jasmussen <joen@git.wordpress.org>
Co-authored-by: mirka <0mirka00@git.wordpress.org>
Co-authored-by: ciampo <mciampini@git.wordpress.org>

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

@github-actions github-actions Bot added [Package] Components /packages/components [Package] Editor /packages/editor [Package] Block library /packages/block-library [Package] Block editor /packages/block-editor labels Jun 18, 2026
@github-actions

Copy link
Copy Markdown

Flaky tests detected in f7e9eb7.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/27764668864
📝 Reported issues:

@mirka mirka self-assigned this Jun 19, 2026
@mirka mirka marked this pull request as draft June 19, 2026 18:18
@github-actions github-actions Bot added [Package] DataViews /packages/dataviews [Package] UI /packages/ui labels Jun 19, 2026

@mirka mirka left a comment

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.

Icon component compatibility

I checked the color behavior for all icons in all three Icon components, and they all consistently render in the currentColor.

Possible breakage scenarios

The one condition that makes this possibly breaking for consumers is when they are setting a non-currentColor fill in on a wrapper element, relying on inheritance. If they're setting it directly on the Icon component, or on svg, they're safe. I've documented this in the changelog.

New guidance for customizing icon colors

The new guidance for setting custom colors on icons is to use color, not fill. This is in anticipation for the changes in #78808, where fill will no longer work consistently.

Addressing the style overrides

There are a good number of places in Gutenberg where fill is set to control the icon color. Among those places:

  1. There are likely fill styles that can be removed, now that each icon sets itself to currentColor. This should be addressed separately in a follow up after #78808.
  2. I anticipate a handful of regressions that will be triggered by the changes in #78808.
  3. I identified only one instance where the svg changes in this PR would cause a possible regression. That instance in DataForm is addressed in this PR.

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.

Color behavior is now a feature of individual icons in @wordpress/icons, so the demonstration is moved to that package's stories.

z-index: 1;
cursor: help;
fill: var(--wpds-color-foreground-content-error-weak);
color: var(--wpds-color-foreground-content-error-weak);

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.

Based on a scan of the codebase, this is the only place that breaks due to these changes. It isn't a condition that appears in Gutenberg itself, but the regression can manifest when layout is panel-dropdown, and labelPosition is none.

Before After
DataForm DataForm

(This is possibly not a condition that anyone will ever hit, but we can fix it anyway.)

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.

I'm honestly also confused by the previous state of the code: the svg had fill: currentColor, but the outer element also specified a hardcoded fill?

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.

Yes, it does look redundant.

@mirka mirka marked this pull request as ready for review June 19, 2026 20:02
@mirka

mirka commented Jun 19, 2026

Copy link
Copy Markdown
Member

I would appreciate a technical review from @WordPress/gutenberg-components.

@mirka mirka added [Type] Breaking Change For PRs that introduce a change that will break existing functionality Needs Dev Note Requires a developer note for a major WordPress release cycle and removed [Type] Enhancement A suggestion for improvement. labels Jun 19, 2026

@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.

I scanned the codebase further and found another potential candidate for regression:

.block-editor-block-icon {
margin-right: 10px;
fill: $gray-900;

There are also a few other ancestor `fill` rules that should be carefully assessed as potential regressions:

I also investigated some potential cleanup opportunities (that can be done as follow-ups to keep the PR focused):

`fill: currentColor` cleanup
`fill` -> `color` cleanup/migration
  • .block-editor-block-icon {
    margin-right: 10px;
    fill: $gray-900;
  • .dataviews-filters__summary-chip-remove {
    width: var(--wpds-dimension-size-sm);
    height: var(--wpds-dimension-size-sm);
    border-radius: var(--wpds-border-radius-md);
    border: 0;
    padding: 0;
    position: absolute;
    right: var(--wpds-dimension-padding-xs);
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    cursor: var(--wpds-cursor-control);
    svg {
    fill: var(--wpds-color-foreground-interactive-neutral);
    }
    &:hover,
    &:focus {
    background: var(--wpds-color-background-interactive-neutral-weak-active);
    svg {
    fill: var(--wpds-color-foreground-interactive-neutral-active);
    }
    }
    &.has-values {
    svg {
    fill: var(--wpds-color-foreground-interactive-brand);
  • background: var(--wpds-color-background-interactive-brand-strong);
    border-color: var(--wpds-color-stroke-interactive-brand);
    svg {
    --checkmark-size: var(--checkbox-size);
    fill: var(--wpds-color-foreground-interactive-neutral-strong);
    position: absolute;
  • .edit-site-sidebar-navigation-item__drilldown-indicator {
    fill: var(--wpds-color-foreground-interactive-neutral-active);
    }
    }
    &[aria-current="true"] {
    background: var(--wpds-color-background-interactive-neutral-weak-active);
    color: var(--wpds-color-foreground-interactive-neutral-active);
    font-weight: $font-weight-medium;
    }
    // Make sure the focus style is drawn on top of the current item background.
    &:focus-visible {
    transform: translateZ(0);
    }
    .edit-site-sidebar-navigation-item__drilldown-indicator {
    fill: var(--wpds-color-foreground-interactive-neutral-weak);
  • .edit-site-site-hub_toggle-command-center {
    color: var(--wpds-color-foreground-interactive-neutral);
    &:hover,
    &:active {
    svg {
    fill: var(--wpds-color-foreground-interactive-neutral-active);
  • svg {
    fill: $gray-600 !important;
    }
    .components-button {
    padding: 4px;
    }
    .components-button:hover {
    background: none !important; // !important to simplify the selector.
    }
    .components-button:hover svg {
    fill: var(--wp-admin-theme-color) !important;
  • .edit-site-add-new-template__template-button,
    .edit-site-add-new-template__custom-template-button {
    svg {
    fill: var(--wp-admin-theme-color);
    }
  • .template-list-add-new-template__template-button,
    .template-list-add-new-template__custom-template-button {
    svg {
    fill: var(--wp-admin-theme-color);
    }

Comment thread packages/ui/src/icon/icon.tsx
z-index: 1;
cursor: help;
fill: var(--wpds-color-foreground-content-error-weak);
color: var(--wpds-color-foreground-content-error-weak);

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.

I'm honestly also confused by the previous state of the code: the svg had fill: currentColor, but the outer element also specified a hardcoded fill?

@mirka mirka force-pushed the update/icons-self-declare-color branch from 84dd3ad to db7b33b Compare June 24, 2026 10:12
@mirka mirka force-pushed the update/icons-self-declare-color branch from db7b33b to af778a4 Compare June 24, 2026 10:19
@mirka

mirka commented Jun 24, 2026

Copy link
Copy Markdown
Member

gutenberg/packages/block-editor/src/components/block-manager/style.scss

I looked at this one too and I think it is safe to leave as-is. The rule targets the .block-editor-block-icon wrapper, so fill="currentColor" on the nested source SVG means the icon now follows inherited color instead.

In this block manager context, that inherited color appears to be the same normal UI text color as $gray-900, so I don’t expect a visual regression here. I’d rather not change the wrapper rule in this PR, since BlockIcon can also render custom/third-party block icons where inherited fill may still be useful.

There are also a few other ancestor fill rules that should be carefully assessed as potential regressions

Considered 👍

@mirka mirka added has dev note when dev note is done (for upcoming WordPress release) and removed Needs Dev Note Requires a developer note for a major WordPress release cycle labels Jun 24, 2026

@mirka mirka left a comment

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.

Thanks for the review @ciampo! I think we're good now? I also added a dev note.

@mirka mirka enabled auto-merge (squash) June 24, 2026 11:29
@mirka mirka merged commit c5cdf2a into trunk Jun 24, 2026
50 of 51 checks passed
@mirka mirka deleted the update/icons-self-declare-color branch June 24, 2026 11:42
@github-actions github-actions Bot added this to the Gutenberg 23.5 milestone Jun 24, 2026
* External dependencies
*/
import type { ReactElement } from 'react';
import type { StoryFn } from '@storybook/react-vite';

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.

Can we please add "@storybook/react-vite": "^10.4.3" to devDependencies in packages/icons/package.json?

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.

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.

@manzoorwanijk Is this not caught by one of your recent linting improvements for dependencies?

Digging into this a bit, I see we disable import/no-extraneous-dependencies for a bunch of files, including Storybook files like this one. I'm not sure the full context behind that, but my instinct is that feels wrong, and we could be catching these issues that way.

@manzoorwanijk manzoorwanijk Jun 25, 2026

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 this not caught by one of your recent linting improvements for dependencies?

This is a dev only import, not part of the bundle. That tool catches the missing dependencies in published bundles.

These types of issues will be caught automatically once we adopt #76195.

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.

I wish Node 24 upgrade would not block such things and we adopt #72143 to unblock the new npm features, but that will probably remain a wish forever 😄

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.

I still think those ESLint overrides are misguided and should be improved, and that would also help identify these issues.

I did a bit of digging and found that it stems back to #16969, with a comment "disabled [...] files for now [...] but we should revisit it really soon" but never happened, and likely expanded from there to exclude more files, maybe compounded by previous issues from #72136, and general apathy toward strict testing for "development" files.

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

Labels

has dev note when dev note is done (for upcoming WordPress release) [Package] Block editor /packages/block-editor [Package] Block library /packages/block-library [Package] Components /packages/components [Package] DataViews /packages/dataviews [Package] Editor /packages/editor [Package] Icons /packages/icons [Package] UI /packages/ui [Type] Breaking Change For PRs that introduce a change that will break existing functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants