Tags
Tags widgets are Input Fields that capture an
ordered list of free-form text values. Each committed value becomes a removable pill
shown to the left of the input, and the value bound at the widget’s path is a string[].
There is no prop that caps the number of tags; the widget never silently drops an entry.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.tags('keywords', { label: 'Keywords', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "tags", "path": "keywords", "label": "Keywords" } ]}Use these props to customize your Tags widget.
| prop | type | description |
|---|---|---|
allowDuplicates | boolean | When false, typing an existing tag is silently ignored. Defaults to true |
hint | string | A description displayed below the row |
icon | string | CSS class string for a leading icon (rendered to the left of the pills) |
placeholder | string | Placeholder text shown in the input when empty |
removeAriaLabel | string | ARIA label prefix used on each chip’s remove button. Defaults to Remove tag |
removeIcon | string | CSS class string for a custom remove-icon. When omitted, a built-in × glyph is rendered |
separators | ('Enter' | ',' | 'Tab' | 'blur' | string)[] | Triggers that commit the current draft as a new chip. Defaults to ['Enter', ',', 'Tab', 'blur'] |
trim | boolean | When true (default), whitespace is trimmed from each value before committing |
Placeholder
Section titled “Placeholder”Use the property placeholder to set the text shown inside the empty input.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.tags('keywords', { placeholder: 'Add a keyword and press Enter', label: 'Keywords', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "tags", "path": "keywords", "label": "Keywords", "props": { "placeholder": "Add a keyword and press Enter" } } ]}Use the property hint to add a description below the row.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.tags('keywords', { hint: 'Use Enter, Tab or comma to add a tag', placeholder: 'Add a keyword…', label: 'Keywords', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "tags", "path": "keywords", "label": "Keywords", "props": { "hint": "Use Enter, Tab or comma to add a tag", "placeholder": "Add a keyword…" } } ]}Use the property icon to render a leading icon to the left of the pills. The value is a
CSS class string — the same convention used by Textinput.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.tags('keywords', { icon: 'label', placeholder: 'Add a keyword…', label: 'Keywords', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "tags", "path": "keywords", "label": "Keywords", "props": { "icon": "label", "placeholder": "Add a keyword…" } } ]}Separators
Section titled “Separators”By default a tag is committed on Enter, ,, Tab, or blur. Override the property
separators to restrict the commit triggers — useful when you want the user to commit
explicitly (e.g. Enter only) and treat commas as part of the tag value.
Pasting text that contains any of the configured character separators (e.g. commas) also splits the paste into multiple chips in a single commit.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.tags('keywords', { hint: 'Only Enter or comma commit a tag — Tab and blur do not', placeholder: 'Type something and press Enter or ,', separators: ['Enter', ','], label: 'Keywords', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "tags", "path": "keywords", "label": "Keywords", "props": { "hint": "Only Enter or comma commit a tag — Tab and blur do not", "placeholder": "Type something and press Enter or ,", "separators": [ "Enter", "," ] } } ]}Allow duplicates
Section titled “Allow duplicates”Set allowDuplicates: false to silently ignore tags the user has already added — no error
message, the second attempt simply does nothing. This is a UX safeguard at commit time;
there is no form-level error to surface.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.tags('keywords', { hint: 'Try adding the same tag twice — the second attempt is silently ignored', placeholder: 'Add a tag…', allowDuplicates: false, label: 'Keywords', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "tags", "path": "keywords", "label": "Keywords", "props": { "hint": "Try adding the same tag twice — the second attempt is silently ignored", "placeholder": "Add a tag…", "allowDuplicates": false } } ]}trim defaults to true so leading/trailing whitespace is removed before a tag is
committed. Set it to false when surrounding whitespace is semantically meaningful (for
example, deliberately quoting a phrase with padding).
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.tags('keywords', { hint: 'trim is off — leading/trailing whitespace is preserved verbatim', placeholder: 'Try adding \' spaced \'', trim: false, label: 'Keywords', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "tags", "path": "keywords", "label": "Keywords", "props": { "hint": "trim is off — leading/trailing whitespace is preserved verbatim", "placeholder": "Try adding ' spaced '", "trim": false } } ]}Max items
Section titled “Max items”There is no prop that caps the number of chips. Use the array validator’s maxItems
instead: entry is never silently blocked, the extra tag is committed and the validator
flags it, so the user is told why. Combine it with messages to change the wording, as in
Validation.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.tags('keywords', { hint: 'A maxItems validator flags a fourth tag instead of blocking it', placeholder: 'Up to 3 tags', label: 'Keywords (max 3)', validator: { maxItems: 3, }, }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "tags", "path": "keywords", "label": "Keywords (max 3)", "props": { "hint": "A maxItems validator flags a fourth tag instead of blocking it", "placeholder": "Up to 3 tags" }, "validator": { "type": "array", "maxItems": 3 } } ]}Customize the remove button
Section titled “Customize the remove button”removeIcon swaps the built-in × glyph for any icon (CSS class string, same convention
as icon). removeAriaLabel sets the prefix announced by assistive tech alongside the
tag value — useful for translations or wording adjustments (e.g. "Discard tag: foo").
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.tags('keywords', { hint: 'Custom remove-button icon and ARIA label', placeholder: 'Add a tag…', removeIcon: 'delete', removeAriaLabel: 'Discard tag', label: 'Keywords', }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "tags", "path": "keywords", "label": "Keywords", "props": { "hint": "Custom remove-button icon and ARIA label", "placeholder": "Add a tag…", "removeIcon": "delete", "removeAriaLabel": "Discard tag" } } ]}Validation
Section titled “Validation”The widget supports the standard array validator: required, minItems and maxItems.
A tag past maxItems is committed and flagged, never dropped. Validation errors surface like any other input: below the row, with aria-invalid and
aria-errormessage wired automatically. Override the default error copy per-rule with a
messages object — each key matches the rule it customises, and values can be a string or
a Localizable for i18n.
import { gui } from '@golemui/gui-shared';
export default [ gui.inputs.tags('keywords', { hint: 'Between 1 and 5 tags', placeholder: 'Add a keyword…', label: 'Keywords', validator: { required: true, minItems: 1, maxItems: 5, messages: { required: 'Please add at least one keyword', minItems: 'You need at least 1 keyword to continue', maxItems: 'No more than 5 keywords, please', }, }, }),];{ "$schema": "https://golemui.com/schemas/form.schema.json", "form": [ { "kind": "input", "type": "tags", "path": "keywords", "label": "Keywords", "props": { "hint": "Between 1 and 5 tags", "placeholder": "Add a keyword…" }, "validator": { "type": "array", "required": true, "minItems": 1, "maxItems": 5, "messages": { "required": "Please add at least one keyword", "minItems": "You need at least 1 keyword to continue", "maxItems": "No more than 5 keywords, please" } } } ]}Styling
Section titled “Styling”Tags can be styled as explained in the Styling Guide.
CSS Variables
Section titled “CSS Variables”The chip and count-badge visuals are shared with the Range Calendar / Range Date Input pills, so they pick up the same intent / spacing / radius tokens.
| CSS Variable | Description |
|---|---|
--gui-color-border | Group border colour |
--gui-color-bg | Group background colour and scroll-fade |
--gui-color-fg | Input text colour |
--gui-color-primary | Group focus-ring colour |
--gui-color-error | Group border colour when invalid |
--gui-intent-primary | Chip and count-bubble background colour |
--gui-color-white | Chip text / remove-icon colour |
--gui-radius-md | Chip corner radius |
--gui-radius-full | Count-bubble corner radius |
--gui-space-1 | Chip vertical padding |
--gui-space-2 | Row gap between chips |
--gui-space-3 | Gap between chip text and remove button |
--gui-font-xs | Default value of --gui-pill-font-size |
--gui-pill-height | Chip height. 24px, 32px under pointer: coarse |
--gui-pill-action-size | Remove icon size. 16px, 24px under pointer: coarse |
--gui-pill-action-hit | Remove icon hit area. 24px, 32px under pointer: coarse |
--gui-pill-font-size | Chip text font size. var(--gui-font-xs), var(--gui-font-sm) under pointer: coarse |
Anatomy
Section titled “Anatomy”This is the anatomy of the Tags Widget in case you want to use your CSS styles.
<gui-tags> <span class="gui-label" id="fieldUid_label"> Label <div class="gui-widget-hint" id="fieldUid_hint">Hint</div> </span>
<div class="gui-widget"> <div class="gui-tags gui-tags--icon" role="group" aria-label="Keywords"> <span class="gui-widget-icon label" data-icon="label"></span>
<div class="gui-tags__pills-wrapper"> <div class="gui-tags__pills" role="list"> <span class="gui-sentinel gui-sentinel__start"></span> <div class="gui-tags__chip" role="listitem" tabindex="-1" aria-label="Remove tag: design" > <span class="gui-tags__chip-text">design</span> <button class="gui-tags__chip-remove" tabindex="-1" aria-hidden="true">×</button> </div> <div class="gui-tags__chip" role="listitem" tabindex="-1" aria-label="Remove tag: product" > <span class="gui-tags__chip-text">product</span> <button class="gui-tags__chip-remove" tabindex="-1" aria-hidden="true">×</button> </div> <span class="gui-sentinel gui-sentinel__end"></span> </div> </div>
<div class="gui-tags__pills-compact"> <button class="gui-tags__pill--count" aria-label="2 tags" aria-expanded="false">2</button> </div>
<input type="text" id="fieldUid" class="gui-tags__input" placeholder="Add a tag…" aria-describedby="fieldUid_hint" /> </div> </div></gui-tags>