5 Tailwind1 with JSPublished 2026-10-04Free · no sign-up
Six checkbox patterns in Tailwind: a custom box with an SVG tick, selectable cards, a to-do list that strikes out finished items, filter chips, a required terms checkbox with an error and a select-all control with an indeterminate state.
A custom checkbox using appearance-none on the real input plus an SVG tick placed over it with peer-checked:block. The input keeps its keyboard and form behaviour, and focus-visible:ring classes give keyboard users a clear outline.
Multi-select cards where the entire card is the label. has-[:checked]: on the label changes the border and background when its checkbox is ticked, and has-[:focus-visible]: adds the ring, so the card behaves like one large accessible checkbox.
A checklist where ticking an item strikes it through and greys it out, using peer-checked:line-through on the text that follows each checkbox. accent-color sets the native tick colour in one utility while keeping the browser's built-in checkbox.
Checkbox chips for filtering a product grid. The input is sr-only and the visible chip is the label, styled through has-[:checked]: so selected chips fill with colour and show a tick from a before: pseudo-element.
A required agreement checkbox with an error that appears only after a submit attempt. Tailwind's user-invalid: variant styles the checkbox and its label red, and group-has-[:user-invalid]: reveals the message tied to the input with aria-describedby.
A table-style select-all checkbox that shows a dash when only some rows are selected. A short script sets indeterminate, which only JavaScript can do, and Tailwind's indeterminate: variant gives that state its own colour.
const all = document.getElementById('tw-all'), rows = [...document.querySelectorAll('.tw-row')];
function sync() { const n = rows.filter(r => r.checked).length; all.checked = n === rows.length; all.indeterminate = n > 0 && n < rows.length; }
all.addEventListener('change', () => { rows.forEach(r => r.checked = all.checked); sync(); });
rows.forEach(r => r.addEventListener('change', sync));
sync();
About these Tailwind Checkboxes
Custom box with appearance-none
Add appearance-none to the input and style it with border, background and checked: utilities. An SVG tick placed after it shows with peer-checked:block, while the real input keeps keyboard and form behaviour.
Labels that react with has-[]
has-[:checked]: on a label styles it when the checkbox inside is ticked, which is how the cards and chips change border and background. Add has-[:focus-visible]:ring-2 so the whole card shows focus.
Native ticks with accent colour
For simple lists, accent-teal-600 recolours the browser's own checkbox in one class, which keeps platform conventions and high-contrast mode support.
Errors after interaction
The terms demo uses group-has-[:user-invalid]: so the error only appears after someone has tried to submit, not as soon as the page loads.
Frequently asked questions
Do these work in Tailwind v4?
Yes. They use variants available in v3.4 and v4.
Which demo uses JavaScript?
Only select all, because the indeterminate state must be set with JavaScript.
How do I make the cards stack on phones?
They already do: the grid is one column by default and three from the sm breakpoint.