web-components icon indicating copy to clipboard operation
web-components copied to clipboard

Badge component

Open Artur- opened this issue 2 years ago • 36 comments

Describe your motivation

Lumo has styles for a badge but they are unintuitive to use. It would be much more intuitive if there was a <vaadin-badge> component.

Describe the solution you'd like

The feature set needed for a <vaadin-badge> component is quite small, with the most typical features being:

  1. You can set the text
  2. You can set the style. The predefined styles available in Lumo work fine: error, success, small, contrast, primary, pill
  3. You can add an icon to the badge, before or after the text

You might also want to:

  1. Add a tooltip to the badge

Describe alternatives you've considered

No response

Additional context

No response

Artur- avatar Jan 24 '23 12:01 Artur-

As we already have theme presets for badge-like elements in Lumo, what would be our plan regarding those? Do we keep existing styles and reuse in vaadin-badge while also keeping an option to use them separately?

web-padawan avatar Jan 24 '23 12:01 web-padawan

I would deprecate the badge part of Lumo with the aim to remove them and guide people to use <vaadin-badge> instead

Artur- avatar Jan 24 '23 12:01 Artur-

We also need to figure out a Flow API and we could discuss it in the same issue (could be also GitHub discussion). Existing API for badges is a bit inconvenient, but it's still only 2 lines of code unless you also want to add an icon:

Span pending = new Span("Pending");
pending.getElement().getThemeList().add("badge");

Suggestion from the daily: wrap that code inside a factory in Flow Components, something like Badge.create()

web-padawan avatar Jan 24 '23 12:01 web-padawan

Some more considerations for vaadin-badge component to discuss and agree upon, before implementing it:

  1. Do we also use the new "badge" a base for vaadin-multi-select-combo-box-chip as they seem quite similar?
  2. What the Material theme should look like? Would it make sense to use the new Material v3 chips as a base?

@jouni WDYT about this? Personally, I would like us to cover both if we agree to introduce a badge component.

web-padawan avatar Jan 24 '23 12:01 web-padawan

The most relevant Flow API could be

new Badge("Hello");
new Badge("Hello", BadgeVariant.PILL);
badge.setText("Goodbye");
badge.setVariants(BadgeVariant.PRIMARY);

Not sure why we would need a factory method, no other components have that

Artur- avatar Jan 24 '23 12:01 Artur-

Fair enough. We would need to have BadgeVariant enum on the Flow side and implement HasThemeVariant as well. In case of the vaadin-badge component, we probably don't need badge theme, so that HTML would look like this:

<span theme="badge primary">I'm a badge.</span>
<vaadin-badge theme="primary">I'm a better badge!</vaadin-badge>

web-padawan avatar Jan 24 '23 12:01 web-padawan

Not sure why we would need a factory method, no other components have that

Badge could be a simple Flow-only component as well, right?


public class Badge extends Span {

  public Badge(String text, BadgeVariant variant) {
    setText(text);
    getElement().getThemeList().add("badge");
    ...
  }

}

tomivirkki avatar Jan 24 '23 13:01 tomivirkki

Yes it can be Java only if other web developers never need a badge component. It currently feels more like a jquery component that somehow ended up in a web component set

Artur- avatar Jan 24 '23 13:01 Artur-

My first thought was to do it like @tomivirkki proposes. But then I ended up wrapping lumo-badge styles in the web component like this https://github.com/TatuLund/badge/blob/v23/src/main/resources/META-INF/resources/frontend/badge.ts

This allowed me to add tooltip support nicely too.

Java API is here: https://github.com/TatuLund/badge/blob/v23/src/main/java/org/vaadin/addons/badge/Badge.java

TatuLund avatar Jan 24 '23 13:01 TatuLund

It currently feels more like a jquery component that somehow ended up in a web component set

Somewhat yes, in Flow app it is abstracted away, if you use it only thru Java API. The difference becomes more visible in Hilla, like here:

https://github.com/TatuLund/hilla-demo/blob/master/frontend/views/list/renderers.ts#L23

So how much improvement it is?

          <span theme="badge" class="ml-auto text-s">
            ${lang.getText(uiStore.lang,contact.status.name)}
          </span>

vs.

          <vaadin-badge class="ml-auto text-s">
            ${lang.getText(uiStore.lang,contact.status.name)}
          </vaadin-badge>

TatuLund avatar Jan 24 '23 13:01 TatuLund

One thing about vaadin-badge in the above example is that regardless of span or vaadin-badge, users would still have to use both class and theme combined for e.g. primary variant, which is probably not so nice in terms of DX.

Note, Lumo utility classes generally shouldn't be used for Vaadin components, but they are OK for span or div etc.

web-padawan avatar Jan 24 '23 13:01 web-padawan

Note, Lumo utility classes generally shouldn't be used for Vaadin components, but they are OK for span or div etc.

Do you mean inside Vaadin components? Because using them on the component instances sound perfectly fine, just like it is fine to use CSS

Artur- avatar Jan 24 '23 13:01 Artur-

One thing about vaadin-badge in the above example is that regardless of span or vaadin-badge, users would still have to use both class and theme combined for e.g. primary variant, which is probably not so nice in terms of DX.

How do you mean? <vaadin-badge theme="primary">I'm a better badge!</vaadin-badge> requires no class. If you want to do other styling, you would use class of course, or style. The theme attribute in this case is similar to a component property. It could also be type but I'm not sure what that would improve

Artur- avatar Jan 24 '23 14:01 Artur-

Do you mean inside Vaadin components? Because using them on the component instances sound perfectly fine

Yes, now when we lean towards adding class based styling in V24 for all components including overlays it's indeed fone to use utility classes too. It wasn't the case in V14 where we mostly used theme attribute.

I was thinking more of cases when people add many class names to customize look and feel of am element instance: my personal approach would be to wrap it with a separate custom element, or maybe extend.

The theme attribute in this case is similar to a component property. It could also be type but I'm not sure what that would improve

This choice is essential for custom design systems based on Vaadin components. Some people might want <my-primary-badge> instead of <vaadin-badge theme="primary">. We could consider this case when documenting the badge component.

web-padawan avatar Jan 24 '23 15:01 web-padawan

Not sure how much value this brings for front-end developer, but for our Java audience, we should provide a Badge class. Currently, showing Badge on our docs seems like a fake news to me. And that Java audience probably don't care if it is based on <vaadin-badge>or <span> on the DOM. As long as it gives developer a decent type (to improve readability of the code), don't require us to write in our documentation hints to call getElement() (this generally raises a flag that we are missing a feature) and hopefully a decent way to discover (and use) various optional styles with IDE.

mstahv avatar Jan 24 '23 16:01 mstahv

If we want to provide the Badge component already in Vaadin 24.0 then I would propose to make it a Flow only component for now, as Tomi suggested. We can then update it to use vaadin-badge internally in 24.1 or later after conducting proper DX tests, implementing Material theme etc.

web-padawan avatar Jan 24 '23 17:01 web-padawan

I’m a bit confused myself if #2008 (Text component for generic text elements) relates to this.

Somehow badge is very close to simply applying the relevant CSS utility classes on a <span>, and with that approach, a developer could produce any number of different text style variations. Of course there should still be convenient ways to produce the most common styles (like badges).

Still, a dedicated component sounds nice, to decouple it from regular text, if we indeed consider making it serve the “chips” use case as well. And limiting the use case allows us to make the implementation more opinionated as well. For example, limit the text to a single line, make sure icons inside the badge/chip align properly, make sure keyboard navigation works (in case of chips), etc.

jouni avatar Jan 24 '23 17:01 jouni

Note, Lumo utility classes generally shouldn't be used for Vaadin components,

I know, they were just left there, no particular purpose.

TatuLund avatar Jan 24 '23 19:01 TatuLund

At least some IDEs do not like you putting a theme attribute on a <span>. This would be resolved by either using class names instead or by having a component

Artur- avatar Apr 03 '23 05:04 Artur-

There is interesting behavior in Badge theme in Vaadin 24, that needs to be taken into account if implemented as web component. See https://github.com/TatuLund/badge/issues/4

I ended up creating API to my component to toggle this behavior.

TatuLund avatar Jul 20 '23 09:07 TatuLund

Tatu: it looks like you’re applying the same theme variant names on both the host and the <span> inside the shadow DOM of the component. If you apply the application theme in the shadow root of the <tatus-badge> component, you’ll get the same styles imported there, and the [theme~=badge] styles will apply for the span. The same styles will also apply on the host from the global scope, as the same styles are imported there as well from the theme.

So it’s an issue in your add-on, not in Vaadin 24, and you shouldn't need a feature to toggle this behavior. Just apply the styles on the host and remove the internal span (or at least the theme attribute on it).

jouni avatar Jul 20 '23 11:07 jouni

@jouni , yes, that is exactly what I thought as well.

TatuLund avatar Jul 20 '23 11:07 TatuLund

...for our Java audience, we should provide a Badge class...

100%!

This is one of many implementations I've done over the years: https://github.com/anezthes/vaadinplus/blob/main/src/main/java/com/example/application/components/Badge.java https://github.com/anezthes/vaadinplus/blob/main/src/main/java/com/example/application/utilities/BadgeVariant.java

Does the job for the most part.

anezthes avatar Aug 22 '23 06:08 anezthes

Would we be ready to proceed with this a some point? The value for this for Java audience would be real, with rather small effort.

I was just recently missing something like this for my app (to decorate items in Select) but in reality badge still didn't exist for me:

  • The code using it looked so bad that I didn't dare to publish it
  • The code didn't work (my app didn't have a separate theme, so apparently something didn't include the styling)

Before noticing the already built component by @anezthes & @TatuLund (the second issue would have though followed me with these solutions 🤔), I drafted a custom Badge component to Viritin and "stole" the theme from Valo. Used bit more basic CSS and loaded with JavaScript annotation, now it works without custom client side bundle or theme.

mstahv avatar Aug 25 '25 07:08 mstahv

We’ve talked about this recently with @rolfsmeds, and it is a component we definitely want to get done. We just have some research and design to do before we jump to the actual implementation.

We need to define the use cases more clearly, so we can be specific where this badge component is intended to be used. For example, we are not intending it to be a “chip”, that you might use for listing email recipients or active filtering options.

Accessibility is a big topic to clarify, mainly regarding screen reader usage, how is a badge announced when it’s associated with a nav-item/avatar/button/etc. If the visible badge is just a number, how do you provide more context about it? Can the badge can turn into a small “dot” without any visible text?

We most probably will also need new APIs in many components to support “badging”, possibly similar to tooltip support.

We can perhaps start with something that’s not “feature complete”, and simply give you an element that has the proper styles applied, and the rest is up to the application developer to figure out at first.

jouni avatar Aug 25 '25 11:08 jouni

Recording yet another facepalm of this missing component, now while drafting some code that would potentially be on the frontpage 🧸 cc: @miikande

mstahv avatar Oct 30 '25 09:10 mstahv

To ignite the disqussion, a quickly drafted Java API for Badge now in https://github.com/vaadin/flow-components/pull/8239

mstahv avatar Nov 05 '25 09:11 mstahv

Some thoughts on the API, accessibility, and use cases: (not a requirement spec, but a discussion opener and food for thought)

Use cases

1) As a generic text badge, not connected to another component.

Image

Generic text badges often consist of a value and a text: Image On small viewports, icon-only badges are often used

Another common pattern is an icon+text badge. These often collapse into icon-only badges on small viewports: Image

Nice-to-have: an icon+text badge that turns into an icon-only badge on small viewports.

2) As a notification badge attached to another component, e.g.

2.1) On a button Image

2.2) On a navigation link Image

2.3) On a tab Image

2.4) On a dropdown menu item Image

Especially on small viewports like phones, notification badges are often rendered as dots: Image

Nice-to-have: an icon or number badge that automatically turns into a pill on small viewport.

Overall, we need to investigate how notification badges would be applied to, and appropriately rendered within, these components.

Accessibility

  • The ARIA role can probably be nothing by default, but it should be possible to apply e.g. role="status" for live announcements of important statuses. No other semantics seem relevant.
  • Icon-only badges need an accessible name. This could be either through aria-label or by visually hiding the text content.
  • Number-only badges sometimes need a noun or other text that explains what the number indicates. This could be an aria-label, but in that case both the number and the text would need to be re-applied when the number changes. A nicer alternative would be to visually hide the text.
  • Notification badges applied to focusable components would ideally be part of the accessible name of the component, since they would easily go unnoticed otherwise unless found via virtual cursor traversal. Need to check which components this could be achieved with.

API design

Considering the accessibility and responsive small-viewport factors mentioned above, it would be nice if the component had separate APIs for setting icon, value and text, so that the text can be hidden separately, and so that the number can be updated without re-applying the text. E.g. something like: new Badge(Integer number, String text, Icon icon)

The following style variants would be nice to have:

  • Rectangular badge (with rounded corners)
  • Pill-shaped badge
  • Icon-only badge (that hides text if provided and is round)
  • Number-only badge (that hides text if provided and is round if the number fits)
  • Notification dot (that hides all content)
  • Ideally the dot, icon-only and number-only would have responsive versions that switch to that rendering based on viewport size.
  • and color variants

Additionally:

  • Would be nice to be able to set a character limit on rendered numbers, so that if set to e.g. 2, it would render as 99+ if there are more than 99.

DOM Structure

Could be something like

<vaadin-badge>
    [shadow root]
      <div part="icon"><slot="icon"></slot></div>
      <div part="value"><slot="prefix"></slot></div>
      <div part="text"><slot="text"></slot></div>
    [end shadow root]
</vaadin-badge>

API

Icon, value and text could be set directly in the constructor: new Badge(Number value, String text, Component icon); (with various overloads omitting unnecessary args)

var completed = new Badge("Completed", VaadinIcons.CHECKMARK.create());
completed.addThemeVariants(BadgeVariant.PILL, BadgeVariant.SUCCESS);
var notification = new Badge(42, "new emails");
notification.addThemeVariants(BadgeVariant.NOTIFICATION, BadgeVariant.VALUE_ONLY);

For attaching the notification badge to a component, the API would vary a bit. For Button, it could be as simple as btn.setSuffixComponent(notification); using the existing HasSuffix interface.

React API could be along the lines of

<Badge text="new emails" value="42" theme="pill success">
    <Icon slot="prefix" icon="vaadin:vaadin-checkmark" />
</Badge>

rolfsmeds avatar Nov 18 '25 13:11 rolfsmeds

Icon-only badge (that hides text if provided and is round) Number-only badge (that hides text if provided and is round if the number fits)

I’d say the part "and is round" should be determined by the styling. If it’s a pill shape, then it becomes round/circle when only the icon/value is shown. If it’s a round-rectangle, then it stays as a round-rectangle even when only the icon/value is shown.

jouni avatar Nov 18 '25 17:11 jouni

I wouldn't add any internal parts. Just slots.

jouni avatar Nov 18 '25 17:11 jouni