Checkbox
<input type="checkbox">
Custom-styled checkboxes built on native <input type="checkbox"> — accessible, keyboard-friendly, and fully styled with Tailwind.
Overview
Preview
Notifications
Basic checkbox
Preview
html
<!-- Unchecked -->
<label class="flex cursor-pointer items-center gap-3 group">
<span class="relative flex h-4 w-4 shrink-0 items-center justify-center rounded border border-zinc-600 bg-zinc-900 transition-all group-hover:border-zinc-500">
<input type="checkbox" class="sr-only" />
</span>
<span class="text-sm text-zinc-300">Accept terms and conditions</span>
</label>
<!-- Checked -->
<label class="flex cursor-pointer items-center gap-3 group">
<span class="relative flex h-4 w-4 shrink-0 items-center justify-center rounded border border-[#D40C37] bg-[#D40C37] transition-all">
<input type="checkbox" checked class="sr-only" />
<svg class="h-2.5 w-2.5 text-white" viewBox="0 0 10 8" fill="none">
<path d="M1 4L3.5 6.5L9 1" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</span>
<span class="text-sm text-zinc-300">Accept terms and conditions</span>
</label>With Vue reactivity
vue
<script setup>
import { ref } from 'vue'
const checked = ref(false)
</script>
<template>
<label class="flex cursor-pointer items-center gap-3 group">
<span
class="relative flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-all"
:class="checked ? 'border-[#D40C37] bg-[#D40C37]' : 'border-zinc-600 bg-zinc-900 group-hover:border-zinc-500'"
>
<input type="checkbox" v-model="checked" class="sr-only" />
<svg v-if="checked" class="h-2.5 w-2.5 text-white" viewBox="0 0 10 8" fill="none">
<path d="M1 4L3.5 6.5L9 1" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</span>
<span class="text-sm text-zinc-300">{{ checked ? 'Enabled' : 'Disabled' }}</span>
</label>
</template>Indeterminate state
Preview
html
<!-- Indeterminate — dash instead of checkmark -->
<span class="relative flex h-4 w-4 shrink-0 items-center justify-center rounded border border-[#D40C37] bg-[#D40C37]">
<span class="h-0.5 w-2 rounded-full bg-white"></span>
</span>vue
<!-- Set indeterminate programmatically -->
<script setup>
import { ref, onMounted, watchEffect } from 'vue'
const checkboxRef = ref(null)
const indeterminate = ref(true)
watchEffect(() => {
if (checkboxRef.value) checkboxRef.value.indeterminate = indeterminate.value
})
</script>
<template>
<input ref="checkboxRef" type="checkbox" ... />
</template>Card checkbox group
Preview
html
<!-- Selected card -->
<label class="flex cursor-pointer items-center justify-between rounded-lg border border-[#D40C37]/40 bg-[#D40C37]/5 px-4 py-3">
<span class="text-sm text-zinc-100">Email</span>
<span class="relative flex h-4 w-4 items-center justify-center rounded border border-[#D40C37] bg-[#D40C37]">
<input type="checkbox" checked class="sr-only" />
<svg class="h-2.5 w-2.5 text-white" viewBox="0 0 10 8" fill="none">
<path d="M1 4L3.5 6.5L9 1" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</span>
</label>Disabled
html
<label class="flex items-center gap-3 cursor-not-allowed opacity-40">
<span class="flex h-4 w-4 shrink-0 items-center justify-center rounded border border-zinc-700 bg-zinc-900">
<input type="checkbox" disabled class="sr-only" />
</span>
<span class="text-sm text-zinc-500">Disabled option</span>
</label>Accessibility notes
- Use
class="sr-only"on the hidden<input>— neverdisplay:none, which removes it from keyboard tab order - The visible
<span>is purely decorative; the real<input>handles all keyboard/screen reader interaction - Always wrap in a
<label>so the entire row is clickable and the label text is announced - For groups of checkboxes, wrap in
<fieldset>with a<legend>