Skip to the page
TACTILE UI

Kit part · Badges & tags · Inputs

Tag input

A text input that turns what you type into printed tags. Enter or a comma sets one down in the well; each tag is a key that takes itself away, and every change is spoken.

Element
<tui-tag-input>
Needs
kit.css + kit.mjs
Rules
tag-input.rules.md · 6
Finishes
Aluminium · Graphite · Porcelain · Black
Volumes
Tactile · Quiet
On this page
  1. Variants
  2. Every state, at once
  3. API
  4. Markup
  5. Accessibility
  6. How it’s built
  7. Rules
  8. Related parts
01

Variants

Empty · the placeholder
With tags
Three is the most.
At the limit · max="3"
Add at least two people.
Error
Disabled
02

Every state, at once

Both finishes, side by side. Hover, pressed and focus are forced with .is-hover, .is-down and .is-focus; checked and disabled are real.

Aluminium

PartRestTag hoverTag pressedTag focusTypingDisabled
Tag input

Graphite

PartRestTag hoverTag pressedTag focusTypingDisabled
Tag input
03

API

NameKindValuesWhat it does
<tui-tag-input>elementname · value · maxThe well. name: what each tag posts under. value: the starting tags, comma separated. max: how many at most.
disabledattribute—Stops typing and removing; 45%.
input.tui-tag-input__entryelementinsideWhere you type. Give it an id and a label, a placeholder, a maxlength for one tag and an aria-describedby for the hint.
.tui-tag-input__tagclassmade by scriptA tag: a small raised key with a cross, named “Remove …”. Not a tab stop; reached by arrow.
.tui-tag-input__statusclassmade by scriptThe polite live region: “Added print. 3 tags.”
valuespropertyarray of stringsRead or set the list.
changeeventdetail.valuesAfter tags are added or removed. Bubbles. The inner input’s own change is not passed on.
tagSplit(text) · tagAdd(tags, incoming, { max }) · tagSays(…)functionkit.mjsThe rules, pure: split a pasted list, refuse duplicates (case aside) and overflow, and say what happened.
.tui-field[data-error]attributeon the fieldRings the well in red, as it does any input.
Enter · ,keyboardin the inputAdd the tag. Enter with nothing typed submits the form as usual.
Backspace · ←keyboardin the inputBackspace on an empty input removes the last tag; ← at the start moves to the last tag.
← → · Home · End · Backspace · Deletekeyboardon a tagMove between tags and back to the input; remove the one you are on.
04

Markup

<div class="tui-field">
  <label class="tui-label" for="topics">Topics</label>
  <tui-tag-input name="topics" value="print, aluminium" max="8">
    <input class="tui-tag-input__entry" id="topics" placeholder="Add a topic"
           autocomplete="off" aria-describedby="topics-hint">
  </tui-tag-input>
  <span class="tui-hint" id="topics-hint">Enter or a comma adds one. Backspace takes the last away.</span>
</div>
import '/kit/kit.mjs';   // defines <tui-tag-input>

const tags = document.querySelector('tui-tag-input[name="topics"]');
tags.values;                       // ['print', 'aluminium']
tags.values = ['print', 'metal'];  // set the list
tags.addEventListener('change', (e) => save(e.detail.values));

// In a form, each tag posts as its own value:
new FormData(form).getAll('topics');   // ['print', 'metal']

Needs kit.css and kit.mjs. How to install the kit.

05

Accessibility

  • The input has a visible <label>, and the element is a role="group" named by it, so the tags and the input read as one field.
  • Each tag is a real button whose name says what it does (“Remove print”) and clicking, Enter or Space on it removes it.
  • Every change is spoken in a polite live region: what was added, what was removed, how many tags there are now, and what was refused (“print is already there”, “the limit is 3”).
  • Only the input is in the tab order, so a long list is not a long Tab; ← from the start of the input, or Home and End on a tag, reach the tags.
  • A refused duplicate nudges the tag that is already there. With reduced motion it presses down instead, and the live region says it either way.
  • Tags keep the case they were typed in.
06

How it’s built

A well like the combo’s, whose tags are the kit’s keys scaled to fit it: 30px caps at 7px corners inside 4px of well, so the well’s 11px corner is concentric with theirs. The element builds its tags from a list, and a list is all it holds: tagAdd refuses a repeat (case aside) and anything past max, tagSays writes what to announce, and both are pure and tested. A hidden input per tag, all under the element’s name, is how a form receives them. A pasted list is split on commas, semicolons and line breaks. Leaving the field commits what is typed, so nothing is lost silently.

07

Rules

tag-input.rules.mdIn the brain

For values people make up: topics, recipients, labels. A fixed list is a select.

6 rules in this file, with the foundations they rest on and the anti-patterns that break them. Full rules ship in the brain with the Pass.

08

Field

Text entry in a recess, the label printed above it, and a hint that says how to fix an error.

HTTPMost teams

Tag

A small printed plate: a type, a count, a badge. Solid for the one tag that must stand out.

Combo

One field with its one action in a shared well: subscribe, search, add.

Docs
Finish
Volume