Button group
Category: Buttons · Tags: button-group, segmented, radio, single-choice, filter
How to use it
Use for a small set (two to five) of mutually exclusive views or filters that change content on the same page, such as a chart's Day/Week/Month range. For actions that do different things, use separate buttons; for more options than fit on one row, use a select field. After copying, give every copy its own radio name and ids, and change the legend and labels to describe what the choice controls.
Accessibility
A <fieldset> with a <legend> wraps native radio inputs, so the group is announced with its name ("Chart range"), only the chosen option is a Tab stop, and the arrow keys move and select within the group as with any radio set. Each radio is visually hidden but stays focusable, and its <label> is the visible button, which shows a 2px outline while its radio has keyboard focus. The selected option is filled and also gets a check mark and bold text, so the choice never depends on colour alone. Text is #0f172a on #ffffff (17.85:1), #0f172a on the #e2e8f0 hover (14.48:1) and #ffffff on the #1d4ed8 selected fill (6.70:1).
Source (copy-ready)
<fieldset class="dcl-btngroup">
<legend class="dcl-btngroup__legend">Chart range</legend>
<div class="dcl-btngroup__options">
<input type="radio" class="dcl-btngroup__input" name="dcl-btngroup-range" id="dcl-btngroup-day" value="day">
<label for="dcl-btngroup-day" class="dcl-btngroup__option">Day</label>
<input type="radio" class="dcl-btngroup__input" name="dcl-btngroup-range" id="dcl-btngroup-week" value="week" checked>
<label for="dcl-btngroup-week" class="dcl-btngroup__option dcl-btngroup__option--selected">Week</label>
<input type="radio" class="dcl-btngroup__input" name="dcl-btngroup-range" id="dcl-btngroup-month" value="month">
<label for="dcl-btngroup-month" class="dcl-btngroup__option">Month</label>
</div>
</fieldset>
<style>
.dcl-btngroup { margin: 0; padding: 0; border: none; min-width: 0; max-width: 100%; font: inherit; color: #0f172a; background-color: #ffffff; }
.dcl-btngroup__legend { padding: 0; margin-bottom: 0.5rem; font-weight: 600; color: #0f172a; background-color: #ffffff; }
.dcl-btngroup__options { display: inline-flex; flex-wrap: wrap; max-width: 100%; border: 1px solid #64748b; border-radius: 0.5rem; overflow: hidden; }
.dcl-btngroup__input { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.dcl-btngroup__option { display: inline-flex; align-items: center; gap: 0.35rem; min-height: 2.75rem; padding: 0 1.1rem; border-left: 1px solid #64748b; cursor: pointer; font-weight: 500; color: #0f172a; background-color: #ffffff; }
.dcl-btngroup__option:first-of-type { border-left: none; }
.dcl-btngroup__option:hover { color: #0f172a; background-color: #e2e8f0; }
.dcl-btngroup__option.dcl-btngroup__option--selected { font-weight: 700; color: #ffffff; background-color: #1d4ed8; }
.dcl-btngroup__input:checked + .dcl-btngroup__option { font-weight: 700; }
.dcl-btngroup__input:checked + .dcl-btngroup__option::before { content: "\2713"; }
.dcl-btngroup__input:focus-visible + .dcl-btngroup__option { outline: 2px solid #0f172a; outline-offset: -4px; }
.dcl-btngroup__input:checked:focus-visible + .dcl-btngroup__option { outline-color: #ffffff; }
</style>
<script>
(function () {
document.querySelectorAll(".dcl-btngroup").forEach(function (root) {
if (root.dataset.dclBound) { return; }
root.dataset.dclBound = "true";
var inputs = root.querySelectorAll(".dcl-btngroup__input");
function sync() {
inputs.forEach(function (input) {
var label = input.id ? root.querySelector("label[for=\"" + input.id + "\"]") : null;
if (label) { label.classList.toggle("dcl-btngroup__option--selected", input.checked); }
});
}
inputs.forEach(function (input) { input.addEventListener("change", sync); });
sync();
});
})();
</script>
Was this useful? Send feedback.