Tag Input
Type to add a chip; Backspace on empty input removes the last chip. Real autocomplete dropdown filtered live by input — the full filter-input pattern with aria-controls/expanded.
Tag Input the 14th of 20 designs in the 20 CSS Tags & Chips Designs collection. The design pairs CSS styling with a small amount of JavaScript for interactivity. Copy the HTML, CSS and JavaScript panels below into your project — the JS is self-contained, has zero dependencies, and is safe to drop into any framework (React, Vue, Svelte, plain HTML). The design honours prefers-reduced-motion and uses real semantic markup, so it ships accessibility-ready out of the box.
Live preview
The code
<div class="ctc-input">
<ul class="ctc-input-chips" role="list"></ul>
<input
type="text"
class="ctc-input-field"
placeholder="Type a language…"
aria-label="Add a language tag"
aria-controls="ctc-input-list"
aria-expanded="false"
autocomplete="off"
/>
<ul class="ctc-input-list" id="ctc-input-list" role="listbox" hidden></ul>
</div> .ctc-input {
position: relative;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px;
padding: 6px 8px;
width: 280px;
background: #15151d;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
}
.ctc-input:focus-within {
border-color: #7c6cff;
}
.ctc-input-chips {
display: contents;
list-style: none;
margin: 0;
padding: 0;
}
.ctc-input-chips li {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 3px 6px 3px 10px;
background: rgba(124, 108, 255, 0.18);
color: #c4b5fd;
border-radius: 4px;
font:
600 11px/1 system-ui,
sans-serif;
}
.ctc-input-chips button {
background: transparent;
border: 0;
padding: 0;
width: 16px;
height: 16px;
color: inherit;
cursor: pointer;
font-size: 14px;
line-height: 1;
}
.ctc-input-field {
flex: 1;
min-width: 60px;
background: transparent;
border: 0;
outline: none;
color: #f0eeff;
font:
500 12px/1 system-ui,
sans-serif;
padding: 4px 0;
}
.ctc-input-field::placeholder {
color: #b8b6d4;
}
.ctc-input-list {
position: absolute;
top: calc(100% + 4px);
left: 0;
right: 0;
margin: 0;
padding: 4px;
list-style: none;
background: #15151d;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
z-index: 5;
max-height: 160px;
overflow: auto;
}
.ctc-input-list[hidden] {
display: none;
}
.ctc-input-list li {
padding: 6px 10px;
border-radius: 4px;
font:
500 12px/1 system-ui,
sans-serif;
color: #c4b5fd;
cursor: pointer;
}
.ctc-input-list li:hover,
.ctc-input-list li[aria-selected="true"] {
background: rgba(124, 108, 255, 0.18);
color: #fff;
} (function () {
var SUGGESTIONS = [
"JavaScript",
"TypeScript",
"Python",
"Go",
"Rust",
"Ruby",
"Swift",
"Kotlin",
"Java",
"C++",
"Elixir",
"Haskell",
];
document.querySelectorAll(".ctc-input").forEach(function (root) {
var chipsEl = root.querySelector(".ctc-input-chips");
var input = root.querySelector(".ctc-input-field");
var listEl = root.querySelector(".ctc-input-list");
var chips = [];
function render() {
chipsEl.innerHTML = "";
chips.forEach(function (c, i) {
var li = document.createElement("li");
var txt = document.createElement("span");
txt.textContent = c;
var btn = document.createElement("button");
btn.type = "button";
btn.setAttribute("aria-label", "Remove " + c);
btn.textContent = "×";
btn.addEventListener("click", function () {
chips.splice(i, 1);
render();
});
li.appendChild(txt);
li.appendChild(btn);
chipsEl.appendChild(li);
});
}
function showList(items) {
if (!items.length) {
listEl.setAttribute("hidden", "");
input.setAttribute("aria-expanded", "false");
return;
}
listEl.innerHTML = "";
items.forEach(function (s) {
var li = document.createElement("li");
li.setAttribute("role", "option");
li.textContent = s;
li.addEventListener("mousedown", function (e) {
e.preventDefault();
if (chips.indexOf(s) === -1) chips.push(s);
input.value = "";
render();
showList([]);
});
listEl.appendChild(li);
});
listEl.removeAttribute("hidden");
input.setAttribute("aria-expanded", "true");
}
input.addEventListener("input", function () {
var q = input.value.trim().toLowerCase();
if (!q) {
showList([]);
return;
}
var matches = SUGGESTIONS.filter(function (s) {
return s.toLowerCase().includes(q) && chips.indexOf(s) === -1;
}).slice(0, 6);
showList(matches);
});
input.addEventListener("keydown", function (e) {
if (e.key === "Enter") {
e.preventDefault();
var v = input.value.trim();
if (v && chips.indexOf(v) === -1) {
chips.push(v);
input.value = "";
render();
showList([]);
}
} else if (e.key === "Backspace" && !input.value && chips.length) {
chips.pop();
render();
} else if (e.key === "Escape") {
showList([]);
}
});
input.addEventListener("blur", function () {
setTimeout(function () {
showList([]);
}, 150);
});
});
})();