エクスクルーシブ

Tag Input <mv-tag-input>

ラベル、宛先、スキル、キーワードのためのチップ入力:Enter、カンマ、Tab で追加し、「a, b; c」(またはスプレッドシートからコピーした列)を貼り付けるとそれぞれが追加され、Backspace で最後のチップを選択してから削除し、矢印キーでチップ間を移動できます。任意のパターンと上限、既存のチップを点滅させる大文字小文字とアクセントを区別しない重複排除、ネイティブの <datalist> による候補表示を備えます。

仕組み

ラベル、宛先、スキル、キーワードのためのチップ入力:Enter、カンマ、Tab で追加し、「a, b; c」(またはスプレッドシートからコピーした列)を貼り付けるとそれぞれが追加され、Backspace で最後のチップを選択してから削除し、矢印キーでチップ間を移動できます。任意のパターンと上限、既存のチップを点滅させる大文字小文字とアクセントを区別しない重複排除、ネイティブの <datalist> による候補表示を備えます。他の実装が見落とす点:本物のフォームフィールドです(ElementInternals:繰り返しの name エントリーまたは区切り文字で連結した 1 つの値、リセット、戻る・進むでの復元、required と pattern の検証)。入力したテキストが失われることはなく(blur 時や送信時にタグになるか、そのまま残り、上限超過や拒否されたテキストはフィールドに戻ります)、区切り文字はモバイルや IME のキーボードからも、アラビア語や CJK の句読点でも検出され、読み上げは短く保たれます(「research removed, 2 tags」)。

カテゴリーフォーム
タイプWeb Component(<mv-tag-input>)
ステータス安定版
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

インストール

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

Marvelous UI の MCP サーバーを使う AI エージェント:install_components({ slugs: ["tag-input"], target_dir: "<absolute path>/src/marvelous", framework: "react" })。

コピーされるファイル(依存関係を含む):tokens/tokens.css, core/base.css, core/dom.js, core/element.js, components/tag-input/tag-input.js, components/tag-input/tag-input.css。

使い方

クイックスタート、動作する最小限のマークアップ:

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

基本となるマークアップです。ここから属性、data-*、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

NameタイプDefaultDescription
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

NameタイプDescription
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.
removeTag(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.

このページは AI で翻訳されています。翻訳の問題を報告