Open demo

CSS code

HTML
<div><span class="lbl">Square</span><div class="stack">
<label class="chk"><input type="checkbox" checked><span class="bx"></span>Remember this device</label>
<label class="chk"><input type="checkbox"><span class="bx"></span>Send me product updates</label></div></div>
<div><span class="lbl">Rounded square</span><div class="stack">
<label class="chk"><input type="checkbox" checked><span class="bx bx-rounded"></span>Enable two-factor auth</label>
<label class="chk"><input type="checkbox"><span class="bx bx-rounded"></span>Share usage data</label></div></div>
<div><span class="lbl">Circular</span><div class="stack">
<label class="chk"><input type="checkbox" checked><span class="bx bx-circle"></span>Accepted</label>
<label class="chk"><input type="checkbox"><span class="bx bx-circle"></span>Pending review</label></div></div>
<div><span class="lbl">Heavy mark</span><div class="stack">
<label class="chk"><input type="checkbox" checked><span class="bx bx-heavy"></span>Mark as complete</label></div></div>
CSS
.stack{display:flex;flex-direction:column;gap:13px;align-items:flex-start}
.chk{position:relative;display:inline-flex;align-items:center;gap:11px;cursor:pointer;font-size:.92rem}
.chk input{position:absolute;opacity:0;width:0;height:0}

.bx{width:22px;height:22px;border:2px solid #64748b;border-radius:6px;position:relative;flex:none;
  transition:background .16s,border-color .16s}
.bx::after{content:"";position:absolute;top:2px;left:7px;width:5px;height:11px;border:solid #fff;
  border-width:0 2px 2px 0;transform:rotate(45deg) scale(0);transition:transform .16s}
.chk input:checked+.bx{background:#2563eb;border-color:#2563eb}
.chk input:checked+.bx::after{transform:rotate(45deg) scale(1)}
.chk input:focus-visible+.bx{outline:2px solid #2563eb;outline-offset:2px}

.bx-rounded{border-radius:8px}
.bx-circle{border-radius:50%}
.bx-heavy::after{width:6px;height:12px;border-width:0 3px 3px 0}
.chk input:checked+.bx-heavy{background:#0f172a;border-color:#0f172a}

@media(prefers-reduced-motion:reduce){.bx,.bx::after{transition:none}}

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div><span class="lbl">Square</span><div class="vstack gap-3 align-items-start">
<div class="form-check choice"><input class="form-check-input" type="checkbox" id="sq1" checked><label class="form-check-label" for="sq1">Remember this device</label></div>
<div class="form-check choice"><input class="form-check-input" type="checkbox" id="sq2"><label class="form-check-label" for="sq2">Send me product updates</label></div></div></div>
<div><span class="lbl">Rounded square</span><div class="vstack gap-3 align-items-start">
<div class="form-check choice"><input class="form-check-input choice-rounded" type="checkbox" id="rs1" checked><label class="form-check-label" for="rs1">Enable two-factor auth</label></div>
<div class="form-check choice"><input class="form-check-input choice-rounded" type="checkbox" id="rs2"><label class="form-check-label" for="rs2">Share usage data</label></div></div></div>
<div><span class="lbl">Circular</span><div class="vstack gap-3 align-items-start">
<div class="form-check choice"><input class="form-check-input choice-circle" type="checkbox" id="ci1" checked><label class="form-check-label" for="ci1">Accepted</label></div>
<div class="form-check choice"><input class="form-check-input choice-circle" type="checkbox" id="ci2"><label class="form-check-label" for="ci2">Pending review</label></div></div></div>
<div><span class="lbl">Heavy mark</span><div class="vstack gap-3 align-items-start">
<div class="form-check choice choice-heavy"><input class="form-check-input" type="checkbox" id="hv1" checked><label class="form-check-label" for="hv1">Mark as complete</label></div></div></div>
CSS
/* .choice restyles Bootstrap's .form-check; change --choice-accent for the checked colour */
.choice{--choice-accent:#2563eb;--choice-border:#64748b;
  display:flex;align-items:center;gap:11px;padding-left:0;margin:0;min-height:0;font-size:.92rem}
.choice .form-check-input{--bs-form-check-bg:transparent;float:none;margin:0;width:22px;height:22px;
  border:2px solid var(--choice-border);border-radius:6px;cursor:pointer;
  transition:background-color .16s,border-color .16s}
.choice .form-check-label{cursor:pointer}
.choice .form-check-input:checked{background-color:var(--choice-accent);border-color:var(--choice-accent)}
.choice .form-check-input:focus{box-shadow:none;border-color:var(--choice-border)}
.choice .form-check-input:checked:focus{border-color:var(--choice-accent)}
.choice .form-check-input:focus-visible{outline:2px solid #2563eb;outline-offset:2px}

.choice .form-check-input.choice-rounded{border-radius:8px}
.choice .form-check-input.choice-circle{border-radius:50%}
.choice-heavy{--choice-accent:#0f172a}
.choice-heavy .form-check-input:checked{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='4' d='m5.5 10 3.2 3.2 6-6.4'/%3e%3c/svg%3e")}

@media(prefers-reduced-motion:reduce){.choice .form-check-input{transition:none}}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-slate-500 uppercase">Square</span><div class="flex flex-col items-start gap-[13px]">
<label class="relative inline-flex cursor-pointer items-center gap-[11px] text-[.92rem]"><input type="checkbox" class="peer sr-only" checked><span class="relative size-[22px] flex-none rounded-md border-2 border-slate-500 transition-[background-color,border-color,color] duration-150 peer-checked:border-blue-600 peer-checked:bg-blue-600 peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-blue-600 after:absolute after:top-[2px] after:left-[7px] after:h-[11px] after:w-[5px] after:scale-0 after:rotate-45 after:border-r-2 after:border-b-2 after:border-white after:transition-[scale] after:duration-150 after:content-[''] peer-checked:after:scale-100 motion-reduce:transition-none motion-reduce:after:transition-none"></span>Remember this device</label>
<label class="relative inline-flex cursor-pointer items-center gap-[11px] text-[.92rem]"><input type="checkbox" class="peer sr-only"><span class="relative size-[22px] flex-none rounded-md border-2 border-slate-500 transition-[background-color,border-color,color] duration-150 peer-checked:border-blue-600 peer-checked:bg-blue-600 peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-blue-600 after:absolute after:top-[2px] after:left-[7px] after:h-[11px] after:w-[5px] after:scale-0 after:rotate-45 after:border-r-2 after:border-b-2 after:border-white after:transition-[scale] after:duration-150 after:content-[''] peer-checked:after:scale-100 motion-reduce:transition-none motion-reduce:after:transition-none"></span>Send me product updates</label></div></div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-slate-500 uppercase">Rounded square</span><div class="flex flex-col items-start gap-[13px]">
<label class="relative inline-flex cursor-pointer items-center gap-[11px] text-[.92rem]"><input type="checkbox" class="peer sr-only" checked><span class="relative size-[22px] flex-none rounded-lg border-2 border-slate-500 transition-[background-color,border-color,color] duration-150 peer-checked:border-blue-600 peer-checked:bg-blue-600 peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-blue-600 after:absolute after:top-[2px] after:left-[7px] after:h-[11px] after:w-[5px] after:scale-0 after:rotate-45 after:border-r-2 after:border-b-2 after:border-white after:transition-[scale] after:duration-150 after:content-[''] peer-checked:after:scale-100 motion-reduce:transition-none motion-reduce:after:transition-none"></span>Enable two-factor auth</label>
<label class="relative inline-flex cursor-pointer items-center gap-[11px] text-[.92rem]"><input type="checkbox" class="peer sr-only"><span class="relative size-[22px] flex-none rounded-lg border-2 border-slate-500 transition-[background-color,border-color,color] duration-150 peer-checked:border-blue-600 peer-checked:bg-blue-600 peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-blue-600 after:absolute after:top-[2px] after:left-[7px] after:h-[11px] after:w-[5px] after:scale-0 after:rotate-45 after:border-r-2 after:border-b-2 after:border-white after:transition-[scale] after:duration-150 after:content-[''] peer-checked:after:scale-100 motion-reduce:transition-none motion-reduce:after:transition-none"></span>Share usage data</label></div></div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-slate-500 uppercase">Circular</span><div class="flex flex-col items-start gap-[13px]">
<label class="relative inline-flex cursor-pointer items-center gap-[11px] text-[.92rem]"><input type="checkbox" class="peer sr-only" checked><span class="relative size-[22px] flex-none rounded-full border-2 border-slate-500 transition-[background-color,border-color,color] duration-150 peer-checked:border-blue-600 peer-checked:bg-blue-600 peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-blue-600 after:absolute after:top-[2px] after:left-[7px] after:h-[11px] after:w-[5px] after:scale-0 after:rotate-45 after:border-r-2 after:border-b-2 after:border-white after:transition-[scale] after:duration-150 after:content-[''] peer-checked:after:scale-100 motion-reduce:transition-none motion-reduce:after:transition-none"></span>Accepted</label>
<label class="relative inline-flex cursor-pointer items-center gap-[11px] text-[.92rem]"><input type="checkbox" class="peer sr-only"><span class="relative size-[22px] flex-none rounded-full border-2 border-slate-500 transition-[background-color,border-color,color] duration-150 peer-checked:border-blue-600 peer-checked:bg-blue-600 peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-blue-600 after:absolute after:top-[2px] after:left-[7px] after:h-[11px] after:w-[5px] after:scale-0 after:rotate-45 after:border-r-2 after:border-b-2 after:border-white after:transition-[scale] after:duration-150 after:content-[''] peer-checked:after:scale-100 motion-reduce:transition-none motion-reduce:after:transition-none"></span>Pending review</label></div></div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-slate-500 uppercase">Heavy mark</span><div class="flex flex-col items-start gap-[13px]">
<label class="relative inline-flex cursor-pointer items-center gap-[11px] text-[.92rem]"><input type="checkbox" class="peer sr-only" checked><span class="relative size-[22px] flex-none rounded-md border-2 border-slate-500 transition-[background-color,border-color,color] duration-150 peer-checked:border-slate-900 peer-checked:bg-slate-900 peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-blue-600 after:absolute after:top-[2px] after:left-[7px] after:h-[12px] after:w-[6px] after:scale-0 after:rotate-45 after:border-r-3 after:border-b-3 after:border-white after:transition-[scale] after:duration-150 after:content-[''] peer-checked:after:scale-100 motion-reduce:transition-none motion-reduce:after:transition-none"></span>Mark as complete</label></div></div>

About this set

Four custom checkbox shapes that cover most forms: a square box that draws its tick, a rounded square, a circular checkbox and a heavy-mark variant that fills with near-black and draws a thicker tick. Use the square or rounded square for ordinary form options such as remember-me and newsletter consent, the circular version where a row of choices should read as softer status marks, and the heavy mark for task-style completion. The plain CSS version keeps a real input inside each label, hidden with position absolute and zero opacity rather than display none, so it stays in the tab order and in the accessibility tree. A sibling span draws the box, and its ::after pseudo-element is a rotated border that scales in when the input is checked. The Bootstrap version uses the native .form-check and .form-check-input, restyled with a few CSS custom properties and Bootstrap’s own SVG tick. The Tailwind version uses peer and peer-checked utilities on the same hidden-input pattern. Because the label wraps or targets the input, clicking the text toggles the box, and the browser handles Space and form submission as it would for any checkbox.

What’s included

  • Four shapes: square, rounded square, circular and heavy mark
  • Real checkbox inputs, hidden without display:none
  • Tick drawn in CSS (Bootstrap uses its own SVG tick)
  • Visible keyboard focus ring on the box
  • No JavaScript

Accessibility

Each checkbox is a real input with its visible text as the label, so screen readers announce the name and the checked state. Tab moves between boxes and Space toggles them. Keyboard focus draws a 2px blue outline around the box, and the unchecked border reaches 3:1 contrast against the page. The tick and colour transitions are switched off under prefers-reduced-motion.

Customise it

Plain CSS: change #2563eb in the .chk input:checked+.bx rule and the width/height of .bx. Bootstrap: set --choice-accent and --choice-border on .choice. Tailwind: swap blue-600 in the peer-checked: classes and size-[22px] on the box.