Skip to content
ShineUI
Install

CheckboxGroup

Multi-pick with one label.

<sui-checkbox-group>SelectionStable

About

Related checkboxes under one label, with shared help and error text and an array value. Use it where a question has several answers that fit on the page; for a long list reach for sui-multi-select. Form-associated: posts one name=value entry per checked option, and the child boxes post nothing.

Usage

<sui-checkbox-group label="Send reminders" name="reminders"></sui-checkbox-group> <script type="module"> import { defineCustomElements } from "@shineui/core/loader"; import "@shineui/core/shineui.css"; defineCustomElements(); const el = document.querySelector("sui-checkbox-group"); el.options = ["7 days before due", "On due date", "7 days overdue"]; /* SuiCheckboxGroupOptionInput[] */ el.value = ["On due date"]; /* SuiKey[] */ el.addEventListener("suiChange", (e) => console.log(e.detail)); </script>
import { SuiCheckboxGroup } from "@shineui/react"; import "@shineui/core/shineui.css"; export function Example() { const options = ["7 days before due", "On due date", "7 days overdue"]; /* SuiCheckboxGroupOptionInput[] */ const value = ["On due date"]; /* SuiKey[] */ return <SuiCheckboxGroup label="Send reminders" name="reminders" options={options} value={value} onSuiChange={(e) => console.log(e.detail)} />; }
<script setup> import { SuiCheckboxGroup } from "@shineui/vue"; import "@shineui/core/shineui.css"; const options = ["7 days before due", "On due date", "7 days overdue"]; // SuiCheckboxGroupOptionInput[] const value = ["On due date"]; // SuiKey[] const onChange = (e) => console.log(e.detail); </script> <template> <SuiCheckboxGroup label="Send reminders" name="reminders" :options="options" :value="value" @sui-change="onChange" /> </template>
import { Component } from "@angular/core"; import { SuiCheckboxGroup } from "@shineui/angular"; @Component({ standalone: true, imports: [SuiCheckboxGroup], template: `<sui-checkbox-group label="Send reminders" name="reminders" [options]="options" [value]="value" (suiChange)="onChange($event)"></sui-checkbox-group>`, }) export class Example { options = ["7 days before due", "On due date", "7 days overdue"]; // SuiCheckboxGroupOptionInput[] value = ["On due date"]; // SuiKey[] onChange(e: CustomEvent) { console.log(e.detail); } }

Basic

The group is a role="group" labeled by its own label, and it owns the form value. Each child is a native checkbox, so Tab moves between them and Space toggles.

<sui-checkbox-group id="cbg-basic" label="Send reminders" name="reminders"></sui-checkbox-group>
// setup(root): properties and events setup(root) { const el = root.querySelector('#cbg-basic'); el.options = reminders; el.value = ['On due date']; }

Descriptions and disabled options

An option can carry a description line. A disabled option cannot be checked and posts nothing.

<div style="max-width:420px"> <sui-checkbox-group id="cbg-desc" label="Delivery channels" name="channel" help="Pick every channel this customer accepts."></sui-checkbox-group> </div>
// setup(root): properties and events setup(root) { const el = root.querySelector('#cbg-desc'); el.options = channels; el.value = ['email']; }

Row layout

orientation="horizontal" lays the options out in a wrapping row with an 18px gap instead of a 9px column.

<sui-checkbox-group id="cbg-inline" label="Statuses to include" name="status" orientation="horizontal"></sui-checkbox-group>
// setup(root): properties and events setup(root) { const el = root.querySelector('#cbg-inline'); el.options = ['Open', 'Overdue', 'Paid', 'Draft']; el.value = ['Open', 'Overdue']; }

States

required means at least one option checked. An error replaces the help line, paints every box in the danger tone and sets aria-invalid on the group.

<div style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px;align-items:start"> <sui-checkbox-group id="cbg-req" label="Send reminders" name="reminders-req" required help="Choose at least one reminder."></sui-checkbox-group> <sui-checkbox-group id="cbg-err" label="Send reminders" name="reminders-err" required error="Choose at least one reminder."></sui-checkbox-group> <sui-checkbox-group id="cbg-off" label="Send reminders" name="reminders-off" disabled help="Reminders are off for this customer."></sui-checkbox-group> </div>
// setup(root): properties and events setup(root) { ['#cbg-req', '#cbg-err', '#cbg-off'].forEach((id) => { root.querySelector(id).options = reminders; }); root.querySelector('#cbg-off').value = ['On due date']; }

Read-only and invalid

readonly reaches every box and refuses a change while the group still posts. invalid paints every box in the danger tone without a message, for a form that draws its summary elsewhere.

<div style="display:grid;gap:18px;max-width:420px"> <sui-checkbox-group id="cbg-ro" label="Reminders" name="reminders" readonly help="Set by the workspace policy."></sui-checkbox-group> <sui-checkbox-group id="cbg-invalid" label="Reminders" invalid></sui-checkbox-group> </div>
// setup(root): properties and events setup(root) { const ro = root.querySelector('#cbg-ro'); ro.options = reminders; ro.value = ['On due date']; root.querySelector('#cbg-invalid').options = reminders; }

In a form

The group posts one entry per checked value under its own name. The child boxes post nothing, so a value can never post twice.

Submit to see what posts.
<form id="cbg-form" style="display:grid;gap:18px;max-width:420px"> <sui-checkbox-group id="cbg-form-reminders" label="Send reminders" name="reminder" required></sui-checkbox-group> <sui-checkbox-group id="cbg-form-channels" label="Delivery channels" name="channel"></sui-checkbox-group> <sui-button type="submit" variant="brand" label="Save settings"></sui-button> <output id="cbg-out" style="font:460 13px/1.6 var(--font-sans);color:var(--ink-muted)">Submit to see what posts.</output> </form>
// setup(root): properties and events setup(root) { const form = root.querySelector('#cbg-form'); const out = root.querySelector('#cbg-out'); const rem = root.querySelector('#cbg-form-reminders'); rem.options = reminders; rem.value = ['On due date']; const ch = root.querySelector('#cbg-form-channels'); ch.options = channels; ch.value = ['email', 'portal']; form.addEventListener('submit', (e) => { e.preventDefault(); const entries = [...new FormData(form)].map(([k, v]) => `${k}=${v}`); out.textContent = entries.length ? entries.join(' ') : 'Nothing posted.'; }); }

Events

suiChange carries the full array of checked values. The child checkbox boolean event stops inside the group, so a consumer hears one event with one shape.

Idle.
<div style="display:flex;flex-wrap:wrap;gap:18px;align-items:flex-start"> <sui-checkbox-group id="cbg-ev" label="Send reminders" name="reminders-ev"></sui-checkbox-group> <span id="cbg-ev-log" style="font:460 13px/1.6 var(--font-sans);color:var(--ink-muted)">Idle.</span> </div>
// setup(root): properties and events setup(root) { const el = root.querySelector('#cbg-ev'); const log = root.querySelector('#cbg-ev-log'); el.options = reminders; el.addEventListener('suiChange', (e) => { log.textContent = e.detail.length ? `Checked ${e.detail.join(', ')}.` : 'Nothing checked.'; }); }

Keyboard

KeyAction
TabMoves through the boxes in order; every box is a tab stop.
SpaceToggles the focused box and fires suiChange with the full array of checked values.

API

<sui-checkbox-group>

Properties

PropertyAttributeTypeDefaultDescription
disableddisabledbooleanfalseDisables every box. A disabled group posts nothing and never holds the form.
errorerrorstringstring | undefinedError message; paints every box in the danger tone and sets aria-invalid on the group.
helphelpstringstring | undefinedHelp text under the options. Replaced by error when set.
invalidinvalidbooleanfalseDanger border on every box without a message, for a form that draws its summary elsewhere.
labellabelstringstring | undefinedGroup label (12.5px / 620).
namenamestringstring | undefinedForm control name; each checked value posts under it. Reflected: the form entry is built from the content attribute.
optionsproperty onlySuiCheckboxGroupOptionInput[][]Options: strings or numbers, or { label, value, description, disabled } objects. A disabled option cannot be checked and posts nothing. Property, not attribute.
orientationorientation"horizontal" | "vertical"'vertical'vertical (the default) stacks the options in a column with a 9px gap; horizontal lays them out in a wrapping row with an 18px gap. The word every element with an axis uses. Reflected.
readOnlyreadonlybooleanfalseDisplay only: every box keeps its name and its tab stop but changes nothing, and the group still posts. Neither a read-only nor a disabled group holds the form on valueMissing.
requiredrequiredbooleanfalseAt least one option must be checked for the surrounding form to be valid.
valueproperty onlySuiKey[][]Checked option values, each as its option gives it (a number stays a number). Mutable; the group updates it before emitting suiChange. Property, not attribute.

Events

EventDetailReactVueDescription
suiChangeSuiKey[]onSuiChange@sui-changeFires when an option toggles; detail is the new value, the full array of checked values.

Methods

MethodSignatureDescription
setFocus()setFocus() => Promise<void>Moves keyboard focus to the first box that can take it, as el.focus() does. sui-form calls this to reach the first invalid control.

Types

SuiCheckboxGroupOption interfaceOne option. value is what value holds, as given: a number stays a number through value, suiChange and a framework binding. The form posts its text.
labelstring
valueSuiKey
description?string
disabled?boolean
SuiCheckboxGroupOptionInput typeAn option: a string or a number stands for itself as label and value.
No members.
SuiKey typeA record's or an item's key wherever the library identifies one: a row, a tree node, a list item, a selected value. Numbers stay numbers, so a numeric id round-trips through selection, the events and a framework binding without turning into a string.
No members.

Theming

Every design token this element can render with, read from its own stylesheets and the ones it imports. Set a token on :root or on any ancestor and every use below follows it. The values shown are the ones in the theme you are viewing. The list is read per stylesheet, so an element that imports a shared drawing module lists that module's tokens too, whether or not it paints with all of them.

See also