Skip to content

@wordpress/ui: Icons from @wordpress/icons below 15.0.0 render black inside Button and IconButton #83471

Closed

Description

@dabowman

Description

#79320 moved icon colouring into the icons themselves. Every @wordpress/icons icon from 15.0.0 on declares currentColor, and ui's Icon stopped adding fill="currentColor". The ui changelog lists that as a breaking change in 0.16.0 and tells consumers to tint icons with CSS color. Button, IconButton and other components rely on this: they set color and expect the SVG to follow it.

Problem

@wordpress/ui lists @wordpress/icons as a regular dependency, which only controls the copy ui uses internally. Consumers pass icons from their own @wordpress/icons, and nothing checks that version. If a project resolves @wordpress/icons below 15.0.0, or passes a custom SVG with no fill, the icon renders black whatever colour the button sets. There's no warning about it.

It's most visible on a pressed neutral minimal toggle:

<IconButton icon={ seen } label="Toggle" variant="minimal" tone="neutral" aria-pressed />

The button's color switches to the pressed foreground (#f0f0f0), but the icon's fill stays the browser default rgb(0, 0, 0). It disappears against the #2d2d2d pressed background.

Suggested solutions

  1. Declare @wordpress/icons as a peer dependency (for example >=15.0.0), so package managers warn when a project's copy predates the self-declared colour. @wordpress/build already declares @wordpress/* peers, so this has precedent. One trade-off: ui imports @wordpress/icons internally, so as a peer it would use the consumer's copy. npm 7+ installs missing peers automatically, but Yarn v1 and some pnpm setups don't.
  2. Document the requirement where consumers will look. The only record of it today is the 0.16.0 changelog entry. The icon prop's JSDoc (packages/ui/src/icon/types.ts) says to pass an SVG, ideally from @wordpress/icons, but not that the SVG must declare currentColor to follow the text colour, or that @wordpress/icons 15.0.0 or later is needed. Adding that to the prop docs (and to Button/IconButton, which accept icon too) would also cover custom SVGs, which a peer dependency can't.

The two can be combined.

Related

Step-by-step reproduction instructions

  1. Create a React project: npm create vite@latest ui-icon-repro -- --template react-ts, then cd ui-icon-repro && npm install.
  2. Install React 18, @wordpress/ui, and an @wordpress/icons release older than 15.0.0 (14.x requires React 18):
    npm install react@18 react-dom@18 @types/react@18 @types/react-dom@18 @wordpress/ui @wordpress/icons@14.0.1
  3. In src/main.tsx, remove the import './index.css' line so the template's styles don't affect the buttons.
  4. Replace src/App.tsx with:
import { IconButton } from '@wordpress/ui';
import { seen } from '@wordpress/icons';

export default function App() {
    return (
        <>
            <IconButton icon={ seen } label="Unpressed" variant="minimal" tone="neutral" />
            <IconButton icon={ seen } label="Pressed" variant="minimal" tone="neutral" aria-pressed />
        </>
    );
}
  1. Run npm run dev and open the page.
  2. Look at the second (pressed) button. Its background is dark (rgb(45, 45, 45)) and the icon is black on it, so it's barely visible.
  3. In DevTools, inspect the pressed button: its computed color is rgb(240, 240, 240). Then inspect the inside it: its computed fill is rgb(0, 0, 0).
  4. To confirm the cause, run npm install @wordpress/icons@latest, then restart the dev server with npm run dev -- --force so Vite doesn't reuse the old icons. Reload the page. The pressed icon is now light: 17.0.0 draws seen as an outline, so check its stroke, which is rgb(240, 240, 240).

Expected: the icon follows the button's text colour in every state.
Actual: the icon stays black whatever colour the button sets.

Screenshots, screen recording, code snippet

Image Image

Environment info

No response

Please confirm that you have searched existing issues in the repo.

  • Yes

Please confirm that you have tested with all plugins deactivated except Gutenberg.

  • Yes

Please confirm which theme type you used for testing.

  • Block
  • Classic
  • Hybrid (e.g. classic with theme.json)
  • Not sure

Activity

  1. added
    [Type] BugAn existing feature does not function as intended
    on Sep 23, 2026
  2. simison commented on Sep 24, 2026

    @simison
    Member

    FYI @WordPress/gutenberg-components @jasmussen

    Since both UI and icons are "bundled" dependencies (instead of being externalised to the WordPress version), things should, in theory, be fine, but it looks like it's still possible to leak old versions into components.

    Feels like best option for consumers is to just update in lock-step to latest to avoid issues like these.

  3. jasmussen commented on Sep 24, 2026

    @jasmussen
    Contributor

    Thanks for the issue, thanks for the ping. I wonder: how can I best help here?

  4. ciampo commented on Sep 24, 2026

    @ciampo
    Contributor

    Hey there! I confirm this can be reproduced.

    Re. the solutions proposed>

    Declare @wordpress/icons as a peer dependency (for example >=15.0.0)

    This can help detect an incompatible app-level @wordpress/icons version, but AFAIK it cannot cover custom SVGs or icons supplied by another package’s older copy. As you also mentoined, it will make @wordpress/ui package use the consumer’s version internally, which needs careful consideration since @wordpress/ui is a bundled package.

    Document the requirement where consumers will look

    That's worth doing anyway, we could probably add guidance for the Icon, Button.Icon, and IconButton components.


    @WordPress/gutenberg-components what do you think about the peer dependency proposal? It could be the least invasive option.

    The other option that comes to mind is to restore fill="currentcolor" in the Icon component as a fallback (which we removed in #79320), ie.

    <SVG
        fill="currentColor"
        { ...icon.props }
        { ...restProps }
    />

    Although I'm not sure we want to do that.

  5. t-hamano commented on Sep 25, 2026

    @t-hamano
    Contributor

    As far as I understand, this issue is not triggered automatically by a WordPress update, but only occurs when consumers explicitly bump the version of @wordpress/ui. Therefore, for now, I think it's sufficient to simply add an appropriate explanation to the ui package's documentation files or JSDoc.

  6. jasmussen commented on Sep 25, 2026

    @jasmussen
    Contributor

    The other option that comes to mind is to restore fill="currentcolor" in the Icon component as a fallback

    Wouldn't this cause bounded icons, like "scheduled" or "draft", to just become black circles?

  7. self-assigned this
    on Sep 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

[Package] Icons/packages/icons[Package] UI/packages/ui[Type] BugAn existing feature does not function as intended

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions