Tags input
The base is a plain .re-input with data-re-tags-input. Without JavaScript it’s
a normal text field that submits one comma-separated value. Opt into the editor:
import { enhanceTagsInput } from "@relements/core/behaviors/tags-input";enhanceTagsInput(document);Enhancing wraps the input in a role="group" shell: typing + Enter or comma
commits a chip (reusing the tag visuals), Backspace
on an empty field removes the last, and each tag is backed by a hidden input so
the form submits an array (getAll(name)). The editor itself is unnamed, so
it never contributes a stray value. Commits/removals fire a bubbling
re-tags-change event with detail.values.
Options via data-*: data-re-tags-max, data-re-tags-name (the hidden-input
name; defaults to the input’s), data-re-tags-allow-duplicates,
data-re-tags-tone. Duplicates are rejected case-insensitively by default.
In a framework? Use the <re-tags-input> element or container mode
below — both inject the chips without moving the input, so a vdom re-render can’t
throw.
<div class="re-field">
<label class="re-field__label" for="tags-basic">Tags</label>
<input
class="re-input"
type="text"
id="tags-basic"
name="tags"
value="design, engineering"
placeholder="Add tags, separated by commas"
autocomplete="off"
data-re-tags-input
/>
<span class="re-field__hint">Press Enter or comma to add. Backspace removes the last.</span>
</div> Max tags
Section titled “Max tags”At the limit, further entries are rejected and the field is marked invalid.
<div class="re-field">
<label class="re-field__label" for="tags-max">Skills</label>
<input
class="re-input"
type="text"
id="tags-max"
name="skills"
value="html, css"
placeholder="Up to 3"
autocomplete="off"
data-re-tags-input
data-re-tags-max="3"
/>
</div> In a form
Section titled “In a form”Submitting sends one name entry per tag.
<form>
<div class="re-field">
<label class="re-field__label" for="tags-form">Labels</label>
<input
class="re-input"
type="text"
id="tags-form"
name="labels"
value="bug, docs"
autocomplete="off"
data-re-tags-input
/>
</div>
<button class="re-button" type="submit" style="margin-block-start: var(--re-space-3)">
Submit
</button>
</form> Framework use
Section titled “Framework use”enhanceTagsInput on a bare <input> (above) reparents it into the chip
container. That’s ideal for plain HTML, but in a vdom framework that owns the
input, a re-render touching its slot can throw insertBefore … not a child. Two
forms never move the input:
-
<re-tags-input>(recommended) — the editor as a custom element. It owns its light DOM, so React/Vue/Svelte/Angular treat it as opaque. Give it anaria-label(it has no<label>);.valuesreads the committed tags and.clear()empties them. The inner<input>is the no-JS fallback.<re-tags-input name="tags" value="design, eng" aria-label="Tags"><input class="re-input" type="text" placeholder="Add tags" /></re-tags-input><script type="module">import "@relements/core/elements/re-tags-input";</script> -
Container mode — put
data-re-tags-inputon a container that wraps the input (instead of on the input itself); the behavior adopts the container as the group and injects the chips as siblings, leaving the input where the framework rendered it.
See the framework guide for the underlying reason.
Accessibility
Section titled “Accessibility”- Keyboard — the editor is a native text field, so it focuses with Tab and
types normally. Enter or a typed/pasted comma commits the current text
as a chip; Backspace on an empty field removes the last chip. Each chip’s
×is a real<button>(Tab to reach it, Enter/Space to activate); removing a chip by its button moves focus to the previous chip’s remove button, or back to the editor when none remain. - Focus — the whole group shows the visible focus ring on
:focus-within(the editor sheds its own ring so the shell owns it); chip remove buttons get the same ring on:focus-visible. At thedata-re-tags-maxlimit the group is marked[data-invalid]and the ring turns danger-colored. - Semantics — enhancing wraps the input in a
role="group"labelled from the field’s<label for>viaaria-labelledby, so AT announces the group by its label, and the editor isaria-describedbythe field’s.re-field__hintso the “Press Enter to add” instructions reach AT (not just sighted users). The chips sit in arole="list"(each chip alistitem) so they can be reviewed as a list. Each×button carries anaria-label="Remove <tag>", and a visually hidden (.re-sr-only)aria-live="polite"region announces “Added …”, “Removed …”, “Maximum N tags reached”, and ”… is already added” as you edit (cleared then re-set so an identical repeat still speaks). Committed tags are backed by hidden inputs that carry the submitted array values. - Notes — pre-seeded tags are not announced on enhance, only edits you make.
Without JavaScript the base degrades to a plain labelled
.re-input. See the accessibility guide.