Skip to content

Conversation

@jameskoster
Copy link
Contributor

@jameskoster jameskoster commented Dec 10, 2025

Screenshot 2025-12-12 at 16 55 33

Why

We need a standardized Badge component for displaying labels with semantic intent across the WordPress admin interface. Badges help communicate status, priority, and meaning through color-coded visual indicators, improving information hierarchy and user comprehension.

Why

This implementation builds on the Box primitive to ensure consistency with the design token system and maintain alignment with the broader UI architecture. By leveraging Box, the Badge automatically inherits design token support for colors, spacing, and borders, ensuring it stays in sync with design system updates.

The implementation uses directional padding (inline: 'xs') to apply horizontal spacing only, and maps semantic intent values (high, medium, low, stable, informational, draft, none) to appropriate design token colors, ensuring accessibility and visual consistency.

How

  • Component structure: Created Badge component in packages/ui/src/badge/ that wraps the Box primitive
  • Props: Supports intent prop with seven semantic values, each mapping to appropriate backgroundColor, color, borderColor, and borderWidth values from the design token system
  • Styling: Uses design tokens for all visual properties:
    • Padding: xs token applied inline-only via directional padding
    • Border radius: lg token
    • Typography: Uses design tokens for font family, size, weight, and line height
    • Colors: Intent-based mapping to design token colors (e.g., higherror, stablesuccess)
  • Element type: Defaults to span element (inline) but supports custom rendering via render prop
  • Documentation: Includes Storybook stories demonstrating all intent variants and usage patterns
  • Type safety: Fully typed with TypeScript, extending ComponentProps<'span'> for HTML attribute support

The component is exported from the main UI package index and follows the same patterns as other UI package components like Stack.

Testing

@jameskoster jameskoster requested a review from a team as a code owner December 10, 2025 11:54
@jameskoster jameskoster added [Type] Enhancement A suggestion for improvement. [Feature] UI Components Impacts or related to the UI component system labels Dec 10, 2025
@github-actions
Copy link

github-actions bot commented Dec 10, 2025

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: jameskoster <[email protected]>
Co-authored-by: aduth <[email protected]>
Co-authored-by: juanfra <[email protected]>
Co-authored-by: mirka <[email protected]>

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

Comment on lines 79 to 82
export const Badge = forwardRef< HTMLSpanElement, BadgeProps >( function Badge(
{ children, intent = 'none', render = DEFAULT_RENDER, ...props },
ref
) {
Copy link
Member

Choose a reason for hiding this comment

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

Nice work on this! Quick question: would it be worth considering an icon prop? I know you can achieve this with children or some other different ways, but having a dedicated prop might make it a bit more convenient for common use cases (for extenders mainly, but maybe also handy to have it for the project)

If not, maybe we could add a gap property to the styles? That way, if folks do add an icon alongside text, there'd be some opinionated spacing between them without needing custom styles. Since Badge is using inline-flex, gap should work nicely here.

What do you think? Happy to keep it simple if this feels like over-engineering!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we should, but I'd prefer to handle it separately as there's some nuance to consider. Specifically; I'm unsure if there should be a system that ties specific icons (or shapes) to the different tones (like IBM Carbon), or if consumers should be allowed to supply their own icons, or both :)

My concern with custom icons is that we risk the innocent introduction of inconsistent patterns at the macro level. Also because we need to keep color blindness top of mind. Consistent icons will be an helpful status indicator for folks who cannot interpret the different colors.

Screenshot 2025-12-11 at 13 48 57

Copy link
Member

Choose a reason for hiding this comment

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

Thanks for the thoughtful reply. That makes a lot of sense. I brought it up because many libraries do offer an icon option (or at least some opinionated spacing), and it can help maintain cohesion when people extend components.

From my perspective, giving extenders a bit of “opinionated freedom” could support a more unified WordPress experience, especially as we shape the future admin design language. But completely agree this is something we can iterate on separately, and I appreciate the nuance around consistency and accessibility. Nice work!

Copy link
Member

@aduth aduth left a comment

Choose a reason for hiding this comment

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

LGTM 👍 Love this as a validation of Box and, inversely, as a checklist of things we should aim to target with typography tokens.

I'm also eager to see this as a replacement for the private Badge component in @wordpress/components (i.e. replace existing usage and remove that component from @wordpress/components). It'd be good to track that in an issue.

Copy link
Member

@aduth aduth left a comment

Choose a reason for hiding this comment

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

We need to add an export to packages/ui/src/index.ts to make the component available for usage from the package.

@aduth aduth added the [Package] UI /packages/ui label Dec 11, 2025
@aduth
Copy link
Member

aduth commented Dec 11, 2025

We need to add an export to packages/ui/src/index.ts to make the component available for usage from the package.

Related: #73928, since I only just noticed this after realizing we're already not exporting the Stack component 😬

padding={ { inline: '2xs' } }
borderRadius="md"
render={ render }
style={ {
Copy link
Member

Choose a reason for hiding this comment

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

I feel like we should use a stylesheet instead of inlining all these?

Copy link
Member

Choose a reason for hiding this comment

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

I expected we'd be able to get rid of most of them through e.g. the Text component in #73931.

There's a few that wouldn't be covered:

  • display and alignItems: I'm not sure we need this anymore after the explicit min-height was removed in 46aa19e
  • boxSizing: This feels like it could be common enough that maybe we'll create a shared utility for it

Copy link
Contributor Author

@jameskoster jameskoster Dec 12, 2025

Choose a reason for hiding this comment

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

display, alignItems, and boxSizing are gone. Any preference on moving the remaining styles to a stylesheet, leaving them as-is, or waiting for Text (#73931)?

Copy link
Member

Choose a reason for hiding this comment

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

I agree with @mirka in having a light preference to a stylesheet since it's a simple shift and general preference, but honestly I think it's fine to just leave these since we'll refactor them away after Text in the next few days.

backgroundColor: 'neutral-strong',
color: 'neutral',
borderColor: 'neutral',
borderWidth: 'xs',
Copy link
Member

Choose a reason for hiding this comment

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

This selective border is making the badge's height change depending on whether it has an intent or not (20 vs 22px). Usually we add a transparent border to all the variants in these cases.

Copy link
Contributor Author

@jameskoster jameskoster Dec 12, 2025

Choose a reason for hiding this comment

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

Oh boy this is a fun one. Transparent borders will make all badges 22px tall which we don't want as it's not aligned with the 4px baseline. We might have to set a fixed height, which could actually act as a helpful forcing function given they shouldn't ever really contain enough content to wrap anyway.

Edit: Although if we set a fixed height then density will be ignored, unless we add height to the dimension tokens.

@aduth aduth self-requested a review December 12, 2025 16:48
@jameskoster
Copy link
Contributor Author

jameskoster commented Dec 12, 2025

I pushed some style changes, most notably inline and block padding are now applied to ensure density is respected.

The trade-off (compared to defining an explicit height) is that the no-tone badge is 2px taller than the others due to its border. I'm not sure if there's a great solution for this, outside of changing the design to not include a border. Here's how that could look:

Screenshot 2025-12-12 at 16 56 48

@aduth
Copy link
Member

aduth commented Dec 12, 2025

The trade-off (compared to defining an explicit height) is that the no-tone badge is 2px taller than the others due to its border. I'm not sure if there's a great solution for this, outside of changing the design to not include a border...

Yeah, it's an interesting problem. We could probably solve this with outline or box-shadow, but then we're fighting against Box. And most of the other solutions involve circumventing or tinkering with the available padding or typography tokens.

I think a fundamental issue is the combination of border tokens (1px) and spacing tokens (4px-based) resulting in heights that aren't going to align to the 4px base unit.

Couple further thoughts:

  • Is this maybe acceptable? (i.e. to have a 26px height badge)
  • Could Box handle this for us, when combining vertical padding and borders? (might be unexpected)
  • To your point, we could update the designs to avoid the border

@github-actions
Copy link

Flaky tests detected in 5ed8020.
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/20173987367
📝 Reported issues:

@jameskoster
Copy link
Contributor Author

I'd welcome @poligilad-auto's thoughts on this detail; the no-tone badge is 2px larger than the others due to its border. Do you think this is problematic? If so how do you feel about removing the border like the mockup in this comment? I can go either way to be honest.

@aduth You're right about the fundamental issue, and I suspect we'll bump into this again later :) It might be something we want Box to handle but it's tricky to say at this stage. If there's something simple we could try it might be worth it, but otherwise I wouldn't say it's high priority.

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

Labels

[Feature] UI Components Impacts or related to the UI component system [Package] UI /packages/ui [Type] Enhancement A suggestion for improvement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants