Tags
An interactive tag: a small control representing a discrete value, such as an email recipient or an active filter. A tag is visually related to a badge but is a distinct control: a badge is a static styled span, while a tag is a container whose segments (label, remove) are separately interactive. The container carries no padding of its own - each segment supplies its share, so every part of the tag is a hit target.
A tag takes the same subtle corner radius as a badge by default; pass rounded: true (or add l-ui-tag--rounded) for a pill shape, matching l-ui-badge--rounded.
Tags with remove
The l_ui_tag helper builds the tag from segments declared in order. t.text renders a static label; t.remove renders a trailing ✕ as a link (when given a URL) or a button. Use <% (not <%=) when calling builder methods - the builder captures their content. Give the remove segment an aria-label so it has an accessible name.
Rounded tags
Pass rounded: true for a pill shape. The segments' focus rings follow the tag's shape automatically.
Tag rows
Wrap a row of tags - plus any trailing actions, such as a clear-all button - in l-ui-tag-row. It lays the row out with a gap and wraps onto multiple lines on narrow screens, making it the canonical container for a filter bar.
Truncation
A tag never grows past its container (max-width: 100%), and the helper wraps each label in l-ui-tag__label so a long label truncates with an ellipsis instead of overflowing - useful for tag rows on narrow screens. The example below constrains the row to force truncation.
Tags with a popover
A tag may carry a popover - e.g. a filter tag whose label opens its controls. Declare the label with t.button and the popover with t.popover (options match l_ui_popover). The whole tag becomes the placement target, so the popover aligns with the tag rather than the segment inside it, and button segments open it via popovertarget.
CSS-classes example
Use the classes directly when you need full control over markup. Segments must be direct children of l-ui-tag, label before remove; when a remove segment follows, the label segment automatically gives up its right padding so the remove owns the gap. Wrap a label segment's content in l-ui-tag__label so it truncates with an ellipsis when the tag is squeezed.
Helper options
| Option | Description |
|---|---|
rounded: |
Pill shape (matching l-ui-badge--rounded); defaults to false for the subtle badge shape |
container: |
Extra HTML attributes for the wrapping <div> (e.g. class:) |
t.text(content, **opts) |
Static label segment (<span>); pass content as the first argument or a block |
t.button(**opts) |
Button segment; opens the tag's popover when one is declared |
t.link(url, **opts) |
Link segment, styled like a button segment |
t.remove(url = nil, **opts) |
Trailing remove segment: a link when url is given, otherwise a button. Renders a ✕ icon unless a block supplies custom content. Pass aria: { label: ... } for an accessible name |
t.popover(**opts) |
Attaches a popover to the tag; the block is the popover body. Options (id:, placement:, align:, open:) match l_ui_popover |
CSS classes
| Class | Description |
|---|---|
l-ui-tag-row |
Container for a row of tags plus any trailing actions (e.g. a filter bar); wraps onto multiple lines on narrow screens |
l-ui-tag |
Tag container; carries the tag's colour and shape but no padding |
l-ui-tag--rounded |
Modifier for a pill shape (matching l-ui-badge--rounded) |
l-ui-tag__text |
Static label segment (apply to a <span>) |
l-ui-tag__button |
Interactive label segment (apply to a <button> or <a>) |
l-ui-tag__label |
Inner wrapper around a label segment's content (apply to a <span> inside l-ui-tag__text or l-ui-tag__button); truncates long labels with an ellipsis. The helper renders it for you |
l-ui-tag__remove |
Trailing remove segment (apply to a <button> or <a>); pair with an icon such as l-ui-icon--sm |