Exclusivobeta

Tag Input <mv-tag-input>

Campo de chips para rótulos, destinatários, habilidades e palavras-chave: Enter, vírgula ou Tab adiciona, colar “a, b; c” (ou uma coluna copiada de uma planilha) adiciona cada um, Backspace seleciona e depois remove o último chip, as setas percorrem os chips, pattern e limite opcionais, eliminação de duplicados sem diferenciar maiúsculas nem acentos que faz o chip existente piscar, e sugestões nativas por <datalist>. O que os outros não fazem: é um campo de formulário de verdade (ElementInternals: entradas repetidas de name ou um único valor unido por um separador, reset, restauração ao voltar/avançar, validade required e pattern), o texto digitado nunca se perde (no blur ou no envio ele vira uma tag, ou continua no campo; o texto excedente e o vetado voltam para o campo), os separadores são reconhecidos a partir de teclados de celular e IME e na pontuação árabe e CJK, e os anúncios continuam curtos (“research removed, 2 tags”).

CategoriaFormulários
TipoWeb Component (<mv-tag-input>)
Statusbeta
Keywordsexclusive, light, tags, chips, tokens, multi-value, recipients, keywords, form-associated, paste

When to use

  • A form collects several free-form values such as tags, keywords, skills or email recipients and must submit them
  • Users paste lists from a spreadsheet, an email client or a document and expect each entry to become a tag
  • Entries must match a format such as email addresses, with invalid ones kept and flagged rather than dropped
  • A filter bar or settings page needs removable keyword chips that work fully from the keyboard

Avoid when

  • Values must be picked from a long or remote list with filtering, groups and option rendering → use Combobox instead
  • The field is a storefront search with recent searches and product results → use Search Autocomplete instead
  • Only a fixed handful of options can be chosen, with nothing typed → use Checkbox instead

Instalação

node scripts/add.mjs tag-input --out ./src/marvelous

Agente de IA com o servidor MCP do Marvelous UI: install_components({ slugs: ["tag-input"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).

Arquivos copiados (dependências incluídas): tokens/tokens.css, core/base.css, core/dom.js, core/element.js, components/tag-input/tag-input.js, components/tag-input/tag-input.css.

Uso

Início rápido, a menor marcação que funciona:

<mv-tag-input name="tags" value="design, research"></mv-tag-input>

Marcação de referência, para usar como ponto de partida e personalizar com atributos, data-* e variáveis CSS:

<form id="mv-tag-input-demo" style="display:grid;gap:1.1rem;width:min(100%,34rem);padding:1.25rem;border:1px solid var(--mv-border);border-radius:var(--mv-radius-xl);background:var(--mv-surface);box-shadow:var(--mv-shadow-sm)">
  <div style="display:grid;gap:.4rem">
    <label for="mv-tag-demo-topics" style="font-size:.875rem;font-weight:600">Topics</label>
    <mv-tag-input id="mv-tag-demo-topics" name="topics" value="design, research" unique max="6" required placeholder="Add a topic…">
      <datalist>
        <option value="Accessibility"></option>
        <option value="Branding"></option>
        <option value="Café culture"></option>
        <option value="Localization"></option>
        <option value="Pricing"></option>
        <option value="Typography"></option>
      </datalist>
    </mv-tag-input>
    <small style="color:var(--mv-fg-muted);font-size:.8125rem">Up to 6. Paste “ux, pricing; Café culture” to add several; typing “Design” flashes the existing tag.</small>
  </div>

  <div style="display:grid;gap:.4rem">
    <label for="mv-tag-demo-invite" style="font-size:.875rem;font-weight:600">Invite reviewers</label>
    <mv-tag-input id="mv-tag-demo-invite" name="reviewers" separator=";" unique pattern="[^\s@]+@[^\s@]+\.[^\s@]{2,}" placeholder="[email protected]"
      value="[email protected]; [email protected]; lucia.fernandez@lumen"></mv-tag-input>
    <small style="color:var(--mv-fg-muted);font-size:.8125rem">Emails only, sent as one “;”-separated value. Invalid addresses stay, flagged, so nothing is lost.</small>
  </div>

  <div style="display:flex;flex-wrap:wrap;align-items:center;gap:.5rem">
    <button class="mv-button" type="submit">Save project</button>
    <button class="mv-button" data-variant="ghost" type="reset">Reset</button>
    <output id="mv-tag-demo-out" style="flex-basis:100%;min-height:1.2em;font:.75rem/1.5 var(--mv-font-mono);color:var(--mv-fg-muted);overflow-wrap:anywhere"></output>
  </div>
</form>

<script type="module">
  const form = document.getElementById("mv-tag-input-demo");
  const out = document.getElementById("mv-tag-demo-out");
  form.addEventListener("submit", (e) => {
    e.preventDefault();
    const fd = new FormData(form);
    out.textContent = [...fd].map(([k, v]) => `${k}=${v}`).join("  ·  ");
  });
  form.addEventListener("invalid", () => { out.textContent = ""; }, true);
</script>

API

Attributes

NameTipoDefaultDescription
namestringForm field name. Without separator, each tag is submitted as its own name entry (like a multiple select).
valuestringInitial tags, split on commas, semicolons, line breaks, tabs (and separator); also the value restored by form reset.
separatorstringSubmit one value with the tags joined by this string (e.g. “,” or “;”) instead of repeated entries; it also splits typed and pasted text.
maxnumberMaximum number of tags; extra text stays in the field and “Limit reached” is announced.
patternregexEach tag must fully match (like input pattern). Mismatches are still added, flagged visually and as “(invalid)”, and make the field invalid (patternMismatch).
uniquebooleanNo duplicates, compared without case or accents (“cafe” = “Café”); the existing chip flashes instead.
pendingcommit | keepcommitWhat happens to typed text when focus leaves or the form submits: it becomes a tag (commit) or stays in the field (keep).
placeholderstringAdd a tag…Shown while there are no tags (defaults to strings.placeholder).
required / disabledbooleanStandard form behavior: valueMissing with no tags; disabled (also from a disabled fieldset) makes the field and chips inert.
data-count / data-full / data-invalidset by the componentTag count, limit reached, at least one invalid tag (for styling).

Properties

NameTipoDescription
tagsstring[]Current tags (a copy). Setting it replaces them silently, like a native field value. Can be set before the element is defined.
valuestringTags joined with separator (default “, ”); setting accepts a string or an array.
suggestionsstring[]Suggestions shown in the native datalist popup (already added ones are hidden); picking one adds it. A <datalist> child works too.
stringsobjectOverrides for every visible and announced text: placeholder, hint, remove (“Remove {tag}”), invalidTag, added, addedInvalid, addedMany, removed, duplicate, full, required, mismatch, overflow. Plural entries are objects keyed by Intl.PluralRules category ({ zero?, one, other… }); numbers are formatted with Intl in the nearest lang.
form / labels / validity / validationMessage / willValidateread-onlyStandard form-control properties.

Methods

NameDescription
add(text | string[])Adds tags as the user would (split, dedupe, limit, mv-add veto, announcement); returns the tags added.
remove(tag | index)Removes a tag by value (case- and accent-insensitive) or index.
clear()Removes every tag (silent).
checkValidity() / reportValidity()Standard; the browser bubble points at the text field.
focus()Focuses the text field.

Events

NameDescription
mv-addCancelable, before a user-added tag is created; detail = { value }. Cancel to veto (the text returns to the field).
mv-removeCancelable, before a tag is removed; detail = { value, index }.
mv-changeAfter tags were added or removed by the user; detail = { tags, value }. A native change event fires on the element too.

Content structure

NameDescription
datalistOptional <datalist> child with <option value> suggestions.

CSS classes

NameDescription
mv-tag-input-listThe <ul role=list> of chips (display: contents, so chips and field share one wrapping row).
mv-tag-input-chip / -text / -removeA chip (li; data-invalid, data-flash), its label and its remove button.
mv-tag-input-fieldThe text input.

CSS variables

NameDefaultDescription
--mv-tag-input-chip-bgvar(--mv-bg-muted)Chip background.
--mv-tag-input-chip-fgvar(--mv-fg)Chip text.
--mv-tag-input-flashvar(--mv-accent)Highlight of the existing chip when a duplicate is typed.
--mv-tag-input-invalidvar(--mv-danger)Invalid chip and field color.
--mv-input-radius / --mv-input-bgvar(--mv-radius-md) / field backgroundShared with mv-input, so both fields match.

Accessibility

The host is a form-associated custom element: a <label for> names it (the inner text field is aria-labelledby the label, or takes the host’s aria-label), clicking the label or the empty part of the box focuses the field, and validity messages anchor to the field. Chips are a real list (ul role=list), so screen readers hear “list, 2 items”; each chip has a real button named “Remove design” (the × icon is aria-hidden). Only the text field is in the Tab order: Left arrow at the start of the field, or Backspace in an empty field, moves focus to the last chip’s remove button; Left/Right (mirrored in RTL), Home and End walk the chips, Right past the last one returns to the field; Backspace removes and moves left, Delete and Enter remove and stay in place, Escape or typing a character goes back to the field. A short screen-reader hint on the field says Enter or comma adds and Left arrow reaches the tags. One polite status region announces only results, briefly: “design added”, “3 tags added”, “research removed, 2 tags”, “Design is already added”, “Limit reached: 6 tags”. Invalid chips are flagged by a “!” mark, a wavy underline and the text “(invalid)”, not by color alone, and the field gets aria-invalid. Focus is always visible (the whole chip is outlined when its button has focus); forced colors use system outlines, a dashed border for invalid chips and a CanvasText mark. Chip entry and the duplicate flash scale only with --mv-motion and stop under prefers-reduced-motion or [data-motion=reduce] (the highlight still shows, statically). Enter never submits while text is pending; with an empty field it submits the form as usual. IME composition is respected.

Esta página foi traduzida com IA. Informar um problema de tradução