SolutionsCase StudiesAI ToolsResourcesAboutContact
Get Your Free AI Automation Audit Book a Free 30-Minute Strategy Call
Playground~/frontend Open editor

~/css/tailwind-custom-selects

6 Tailwind Custom Selects

4 Tailwind2 with JSPublished 2026-10-04Free · no sign-up

Six select patterns in Tailwind: a native select with a custom chevron, a dark select with option groups, a fully custom listbox, a compact sort pill, a checkbox multi-select in a disclosure and a type-to-filter datalist.

01. Tailwind Styled Select

TailwindNative

A native select styled with Tailwind utilities: appearance-none, padding for the arrow and an SVG chevron as an arbitrary background value. It keeps the platform option list and mobile picker, which is usually the best choice.

View code
<div class="w-72">
  <label for="tws1" class="mb-1.5 block text-sm font-semibold text-slate-700">Size</label>
  <select id="tws1" class="w-full appearance-none rounded-lg border border-slate-300 bg-white bg-[url('data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2020%2020%22%3E%3Cpath%20d=%22M5%207.5l5%205%205-5%22%20fill=%22none%22%20stroke=%22%23475467%22%20stroke-width=%222%22/%3E%3C/svg%3E')] bg-[length:18px] bg-[right_12px_center] bg-no-repeat py-2.5 pl-3 pr-10 text-[15px] text-slate-900 focus:border-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-100">
    <option>Small</option><option selected>Medium</option><option>Large</option>
  </select>
</div>

02. Tailwind Dark Select with Groups

TailwindOptgroup

A dark native select with optgroups for organising options, using the scheme-dark arbitrary property so the browser draws the opened menu in dark colours too. Focus uses a coloured ring that is easy to see on the dark field.

View code
<div class="w-72 [color-scheme:dark]">
  <label for="tws2" class="mb-1.5 block text-sm font-semibold text-slate-300">Timezone</label>
  <select id="tws2" class="w-full rounded-lg border border-slate-700 bg-slate-900 px-3 py-2.5 text-[15px] text-white focus:outline-none focus:ring-2 focus:ring-amber-400">
    <optgroup label="Europe"><option>London (GMT)</option><option>Berlin (CET)</option></optgroup>
    <optgroup label="Asia"><option selected>Karachi (PKT)</option><option>Dubai (GST)</option></optgroup>
    <optgroup label="Americas"><option>New York (EST)</option></optgroup>
  </select>
</div>

03. Tailwind Custom Listbox

TailwindARIA

A fully custom select-only combobox styled with Tailwind, for cases where the options need rich content. It supports arrow keys, Enter, Escape and clicking, marks the selected option with aria-selected, and shows a tick with aria-selected: utilities.

View code
<div class="relative w-64" id="twlb">
  <span id="twlb-l" class="mb-1.5 block text-sm font-semibold text-slate-700">Assignee</span>
  <button id="twlb-b" role="combobox" aria-labelledby="twlb-l twlb-b" aria-haspopup="listbox" aria-expanded="false" aria-controls="twlb-o" class="flex w-full items-center justify-between rounded-lg border border-slate-300 bg-white px-3 py-2.5 text-left text-[15px] text-slate-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500">Ana Silva</button>
  <ul id="twlb-o" role="listbox" aria-labelledby="twlb-l" hidden class="absolute inset-x-0 z-10 mt-1.5 rounded-xl border border-slate-200 bg-white p-1.5 shadow-lg">
    <li role="option" aria-selected="true" class="cursor-pointer rounded-lg px-2.5 py-2 text-sm text-slate-800 aria-selected:font-semibold aria-selected:after:content-['_\2713'] [&.act]:bg-blue-50">Ana Silva</li>
    <li role="option" aria-selected="false" class="cursor-pointer rounded-lg px-2.5 py-2 text-sm text-slate-800 aria-selected:font-semibold aria-selected:after:content-['_\2713'] [&.act]:bg-blue-50">Ben Okoro</li>
    <li role="option" aria-selected="false" class="cursor-pointer rounded-lg px-2.5 py-2 text-sm text-slate-800 aria-selected:font-semibold aria-selected:after:content-['_\2713'] [&.act]:bg-blue-50">Chen Wei</li>
  </ul>
</div>
const b = document.getElementById('twlb-b'), l = document.getElementById('twlb-o'), os = [...l.children];
let a = 0;
os.forEach((o, i) => o.id = 'twlbo' + i);
function open(v) { l.hidden = !v; b.setAttribute('aria-expanded', String(v)); if (v) mark(os.findIndex(o => o.getAttribute('aria-selected') === 'true')); else b.removeAttribute('aria-activedescendant'); }
function mark(i) { a = (i + os.length) % os.length; os.forEach((o, k) => o.classList.toggle('act', k === a)); b.setAttribute('aria-activedescendant', os[a].id); }
function pick(i) { os.forEach((o, k) => o.setAttribute('aria-selected', String(k === i))); b.textContent = os[i].textContent; open(false); }
b.addEventListener('click', () => open(l.hidden));
b.addEventListener('keydown', e => {
  if (l.hidden) { if (['ArrowDown', 'Enter', ' '].includes(e.key)) { e.preventDefault(); open(true); } return; }
  if (e.key === 'ArrowDown') { e.preventDefault(); mark(a + 1); } else if (e.key === 'ArrowUp') { e.preventDefault(); mark(a - 1); }
  else if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); pick(a); } else if (e.key === 'Escape') open(false);
});
os.forEach((o, i) => o.addEventListener('click', () => { pick(i); b.focus(); }));
document.addEventListener('click', e => { if (!e.target.closest('#twlb')) open(false); });

04. Tailwind Inline Sort Select

TailwindCompact

A compact sort control for a product toolbar: visible text reads Sort: Newest, and a transparent native select covers the whole pill so it opens the system menu. focus-within: puts the focus ring on the visible pill.

View code
<label class="relative inline-flex cursor-pointer items-center gap-2 rounded-full border border-slate-700 bg-slate-900 px-4 py-2 text-sm text-slate-300 focus-within:ring-2 focus-within:ring-sky-400">
  Sort: <b id="twsv" class="text-white">Newest</b><span aria-hidden="true" class="text-slate-400">&#9662;</span>
  <select aria-label="Sort products" class="absolute inset-0 cursor-pointer opacity-0" onchange="document.getElementById('twsv').textContent=this.value"><option>Newest</option><option>Price, low to high</option><option>Price, high to low</option><option>Best rated</option></select>
</label>

05. Tailwind Checkbox Multi-Select

TailwindMulti-select

A multi-select built from a details disclosure holding a list of checkboxes, with the summary showing how many are chosen. It needs only a few lines of JavaScript for the count, and every checkbox keeps its native keyboard behaviour.

View code
<details class="group relative w-64">
  <summary class="flex cursor-pointer list-none items-center justify-between rounded-lg border border-slate-300 bg-white px-3 py-2.5 text-[15px] text-slate-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500 [&::-webkit-details-marker]:hidden">Tags: <span id="twmc" class="font-semibold">2 selected</span></summary>
  <fieldset class="absolute inset-x-0 z-10 mt-1.5 grid gap-1 rounded-xl border border-slate-200 bg-white p-2 shadow-lg">
    <legend class="sr-only">Choose tags</legend>
    <label class="flex items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-slate-50"><input type="checkbox" class="twm h-4 w-4 accent-violet-600" checked> Bestseller</label>
    <label class="flex items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-slate-50"><input type="checkbox" class="twm h-4 w-4 accent-violet-600" checked> Handmade</label>
    <label class="flex items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-slate-50"><input type="checkbox" class="twm h-4 w-4 accent-violet-600"> Limited</label>
    <label class="flex items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-slate-50"><input type="checkbox" class="twm h-4 w-4 accent-violet-600"> On sale</label>
  </fieldset>
</details>
const ms = [...document.querySelectorAll('.twm')];
ms.forEach(m => m.addEventListener('change', () => { const n = ms.filter(x => x.checked).length; document.getElementById('twmc').textContent = n ? n + ' selected' : 'None'; }));

06. Tailwind Searchable Input

TailwindDatalist

A type-to-filter field powered by an HTML datalist, styled with Tailwind. The browser draws the suggestion menu, people can still enter their own value, and no JavaScript or library is needed for a long list of options.

View code
<div class="w-72">
  <label for="twdl" class="mb-1.5 block text-sm font-semibold text-slate-700">Country</label>
  <input id="twdl" list="twdl-l" placeholder="Start typing a country" class="w-full rounded-lg border border-slate-300 px-3 py-2.5 text-[15px] text-slate-900 placeholder:text-slate-500 focus:border-teal-600 focus:outline-none focus:ring-2 focus:ring-teal-100">
  <datalist id="twdl-l"><option value="Australia"><option value="Canada"><option value="Germany"><option value="Nigeria"><option value="Pakistan"><option value="United Kingdom"><option value="United States"></datalist>
</div>

About these Tailwind Custom Selects

Native first

appearance-none plus an arbitrary background-image chevron lets a native select match your design while keeping the system picker on phones, which is the most accessible option.

Dark selects

Setting [color-scheme:dark] on a wrapper tells the browser to draw the opened option list in dark colours, so it matches a dark field.

Custom listbox

The listbox demo follows the ARIA select-only combobox pattern and styles the selected option with aria-selected: utilities, so the look always follows the accessible state.

Multi-select without a library

A details disclosure holding checkboxes gives a dependable multi-select; the summary shows the selected count, updated by a few lines of script.

Frequently asked questions

Do I need the Tailwind forms plugin?

No. These style the elements directly with utilities.

Which demos use JavaScript?

The listbox and the multi-select count; the sort pill uses one inline handler.

Is there a CSS version?

Yes, see the CSS custom selects collection.