UI Components Easy
Checkbox Group
Styled checkbox inputs with label, indeterminate state, checkbox card variant, and a grouped fieldset with select-all functionality.
Open in Lab
MCP
css vanilla-js
Targets: JS HTML
Code
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: Inter, system-ui, sans-serif;
background: #050910;
color: #f2f6ff;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
}
.demo {
width: 100%;
max-width: 520px;
}
.demo-title {
font-size: 1.5rem;
font-weight: 800;
margin-bottom: 0.375rem;
}
.demo-sub {
color: #475569;
font-size: 0.875rem;
margin-bottom: 2rem;
}
.section {
margin-bottom: 2rem;
}
.section-label {
font-size: 0.72rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: #475569;
margin-bottom: 0.75rem;
}
/* ── Base checkbox ── */
.check-input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.check-box {
display: inline-flex;
align-items: center;
justify-content: center;
width: 18px;
height: 18px;
border-radius: 4px;
border: 1.5px solid rgba(255, 255, 255, 0.2);
background: transparent;
flex-shrink: 0;
transition: border-color 0.15s, background 0.15s;
position: relative;
}
/* Checkmark */
.check-box::after {
content: "";
display: none;
width: 5px;
height: 9px;
border: 2px solid #0f172a;
border-top: none;
border-left: none;
transform: rotate(45deg) translate(-1px, -1px);
}
/* Indeterminate dash */
.check-box::before {
content: "";
display: none;
width: 10px;
height: 2px;
background: #0f172a;
border-radius: 1px;
}
.check-input:checked + .check-box {
background: #38bdf8;
border-color: #38bdf8;
}
.check-input:checked + .check-box::after {
display: block;
}
.check-input.is-indeterminate + .check-box {
background: #38bdf8;
border-color: #38bdf8;
}
.check-input.is-indeterminate + .check-box::before {
display: block;
}
.check-input.is-indeterminate + .check-box::after {
display: none;
}
.check-input:focus-visible + .check-box {
outline: 2px solid #38bdf8;
outline-offset: 2px;
}
.check-input:disabled + .check-box {
opacity: 0.35;
cursor: not-allowed;
}
/* ── Check item row ── */
.check-item {
display: flex;
align-items: center;
gap: 0.6rem;
cursor: pointer;
font-size: 0.875rem;
color: #cbd5e1;
user-select: none;
}
.check-item:hover .check-box {
border-color: #38bdf8;
}
.check-item--master {
margin-bottom: 0.625rem;
}
.check-list {
display: flex;
flex-direction: column;
gap: 0.625rem;
}
/* ── Card variant ── */
.check-cards {
display: flex;
flex-direction: column;
gap: 0.625rem;
}
.check-card {
display: flex;
align-items: flex-start;
gap: 0.75rem;
padding: 1rem 1.125rem;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 0.75rem;
cursor: pointer;
transition: border-color 0.15s, background 0.15s;
background: #0d1117;
}
.check-card:hover {
border-color: rgba(56, 189, 248, 0.3);
}
.check-card:has(.check-input:checked) {
border-color: #38bdf8;
background: rgba(56, 189, 248, 0.05);
}
.check-card:has(.check-input:disabled) {
opacity: 0.45;
cursor: not-allowed;
}
.check-card .check-box {
margin-top: 0.125rem;
}
.check-card-content {
display: flex;
flex-direction: column;
gap: 0.2rem;
font-size: 0.875rem;
}
.check-card-content strong {
color: #f2f6ff;
}
.check-card-content span {
color: #64748b;
font-size: 0.8rem;
}
/* ── Select group ── */
.select-group {
border: none;
}
.select-group-children {
padding-left: 1.5rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
border-left: 2px solid rgba(255, 255, 255, 0.06);
}(function () {
// Set indeterminate state on demo checkbox
var indeterminate = document.getElementById("indeterminate-demo");
if (indeterminate) indeterminate.indeterminate = true;
if (indeterminate) indeterminate.classList.add("is-indeterminate");
// Select-all logic
var master = document.getElementById("master-check");
var children = Array.from(document.querySelectorAll(".child-check"));
function updateMaster() {
if (!master) return;
var checked = children.filter(function (c) {
return c.checked;
}).length;
if (checked === 0) {
master.checked = false;
master.indeterminate = false;
master.classList.remove("is-indeterminate");
} else if (checked === children.length) {
master.checked = true;
master.indeterminate = false;
master.classList.remove("is-indeterminate");
} else {
master.checked = false;
master.indeterminate = true;
master.classList.add("is-indeterminate");
}
}
if (master) {
master.addEventListener("change", function () {
master.classList.remove("is-indeterminate");
master.indeterminate = false;
children.forEach(function (c) {
c.checked = master.checked;
});
});
}
children.forEach(function (c) {
c.addEventListener("change", updateMaster);
});
updateMaster();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Checkbox Group</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="demo">
<h1 class="demo-title">Checkbox Group</h1>
<p class="demo-sub">Custom styled checkboxes with card and select-all variants.</p>
<!-- Basic states -->
<section class="section">
<p class="section-label">States</p>
<div class="check-list">
<label class="check-item"><input type="checkbox" class="check-input"> <span class="check-box"></span> Unchecked</label>
<label class="check-item"><input type="checkbox" class="check-input" checked> <span class="check-box"></span> Checked</label>
<label class="check-item"><input type="checkbox" class="check-input" id="indeterminate-demo"> <span class="check-box"></span> Indeterminate</label>
<label class="check-item"><input type="checkbox" class="check-input" disabled> <span class="check-box"></span> Disabled</label>
<label class="check-item"><input type="checkbox" class="check-input" checked disabled> <span class="check-box"></span> Disabled checked</label>
</div>
</section>
<!-- Card variant -->
<section class="section">
<p class="section-label">Checkbox cards</p>
<div class="check-cards">
<label class="check-card">
<input type="checkbox" class="check-input check-card-input">
<span class="check-box"></span>
<span class="check-card-content">
<strong>Pro plan</strong>
<span>$29/mo · 10 seats · Priority support</span>
</span>
</label>
<label class="check-card">
<input type="checkbox" class="check-input check-card-input" checked>
<span class="check-box"></span>
<span class="check-card-content">
<strong>Team plan</strong>
<span>$79/mo · Unlimited seats · SSO</span>
</span>
</label>
<label class="check-card">
<input type="checkbox" class="check-input check-card-input" disabled>
<span class="check-box"></span>
<span class="check-card-content">
<strong>Enterprise</strong>
<span>Custom pricing · Contact sales</span>
</span>
</label>
</div>
</section>
<!-- Select all group -->
<section class="section">
<p class="section-label">Select all group</p>
<fieldset class="select-group" id="select-group">
<label class="check-item check-item--master">
<input type="checkbox" class="check-input" id="master-check">
<span class="check-box"></span>
<strong>Select all notifications</strong>
</label>
<div class="select-group-children">
<label class="check-item"><input type="checkbox" class="check-input child-check" checked> <span class="check-box"></span> Email notifications</label>
<label class="check-item"><input type="checkbox" class="check-input child-check" checked> <span class="check-box"></span> Push notifications</label>
<label class="check-item"><input type="checkbox" class="check-input child-check"> <span class="check-box"></span> SMS alerts</label>
<label class="check-item"><input type="checkbox" class="check-input child-check"> <span class="check-box"></span> Slack messages</label>
</div>
</fieldset>
</section>
</div>
<script src="script.js"></script>
</body>
</html>Checkbox Group
Fully styled checkboxes with a custom appearance built on real <input type="checkbox"> elements for full accessibility.
States
unchecked · checked · indeterminate · disabled
Variants
- Basic — label to the right
- Card — full clickable card with title + description, border highlights on check
- Group with select-all — master checkbox manages child state; goes indeterminate when partially selected
Implementation note
The indeterminate state must be set via JavaScript (el.indeterminate = true) — it has no HTML attribute.