Canvas Design System
Main Site Tokens

Checkbox Card Group

A stacked list of independent on/off options, each with an icon, a title, and an explanation.

When to Use

Use when: The user selects any number of independent options that apply together — cleanup operations, feature opt-ins, batch actions. Each row explains what it will do before the user commits.
Don't use when: Only one option can apply — use Radio Card Group. For a single setting that takes effect immediately use Toggle; for a list with no input, Checklist.

Variants

Default

One row per option. Pre-check the ones that are safe by default and leave the consequential ones off — here, reordering the agenda is opt-in.

Cleanup options

<?= ws_checkbox_card_group([
    ['id'          => 'roundDurations',
     'title'       => 'Round durations',
     'description' => 'Round all times to the nearest 5 minutes',
     'icon'        => 'schedule',
     'iconColor'   => 'blue',
     'checked'     => true],
    ['id'          => 'applyStructure',
     'title'       => 'Apply recommended structure',
     'description' => 'Reorder for optimal flow',
     'icon'        => 'auto_fix_high',
     'iconColor'   => 'red',
     'highlight'   => true],
], ['label' => 'Cleanup options']) ?>

Icon colours

Five accent keys: blue, orange, purple, red, green. These are functional coding — use one colour per category of operation and hold it, rather than cycling for variety.

Accent keys

States

StateBehavior
UncheckedDefault row treatment, driven by the input's native state.
CheckedSet by checked on the item and maintained by the native :checked pseudo-class — no JS keeps it in sync.
Highlighthighlight: true gives a row a featured treatment. Use it for the option with the largest consequence, not for the recommended one.
HoverRow background shifts; the whole row is the click target because the label wraps it.
FocusThe real checkbox takes focus and shows its ring on the row.
DisabledNot supported. Unlike Radio Card Group, items have no disabled option. Omit an unavailable option, or explain in its description why it can't be used.

Real-World Usage

The Planner's agenda cleanup modal: the safe operations start checked, the one that reorders the user's work does not, and it carries highlight so it reads as the significant choice.

What should we clean up?

<?= ws_checkbox_card_group($operations, ['label' => 'What should we clean up?']) ?>

<script>
// Item `id` becomes the checkbox id — read them back by that.
const selected = $operations
    .map(op => op.id)
    .filter(id => document.getElementById(id).checked);
</script>

Options

Group options

OptionTypeDefaultPurpose
$itemsarray[]Positional. The option definitions.
labelstringnullGroup label. Not programmatically associated — see Accessibility.
idstringnullContainer ID
classstring''Additional CSS classes

Item options

OptionTypeDefaultPurpose
idstring''Required in practice — it becomes the checkbox's id and the handle you read the value back by. Each must be unique on the page.
titlestring''Row title. Phrase it as the action: "Round durations".
descriptionstring''What checking it will do
iconstringnullMaterial icon name
iconColorstring'blue'blue | orange | purple | red | green
checkedboolfalseInitially checked
highlightboolfalseFeatured row treatment
info There's no group name, so the checkboxes aren't submitted as an array. Read them back individually by their id, as in the example above.

Accessibility

ConcernBehavior
SemanticsReal <input type="checkbox"> elements, so checked state, focusability, and keyboard operation are all native.
KeyboardTab between rows, Space to toggle. Each checkbox is its own tab stop, which is correct for independent options.
Labelling per optionThe row is a <label> wrapping its input, so title and description are both part of the accessible name and the whole row is clickable.
Group labelNot associated. Like Radio Card Group, the label renders as a plain element with no <fieldset>/<legend> and no role="group" with aria-labelledby. Wrap the call yourself when the question matters.
Icon colourDecorative. The five accents carry no meaning to assistive tech, so never let colour be the only thing distinguishing two rows — the title and description must stand alone.
HighlightVisual only, with no text equivalent. If a row is highlighted because it's consequential, say so in the description.
<fieldset>
    <legend>What should we clean up?</legend>
    <?= ws_checkbox_card_group($operations) ?>
</fieldset>

Tokens

TokenUsed for
--color-ink-secondaryGroup label colour
--text-smallGroup label size

The rows lean on the shared card and toggle rules for surface, border, and the five icon accents rather than defining tokens of their own.

CSS Classes

ClassPurpose
.ws-checkbox-card-groupStacked container
.ws-checkbox-card-group__labelGroup label (unassociated — see Accessibility)

Row-level styling comes from the shared checkbox-card rules rather than a per-row modifier — which is why the class list here is short.

Files

FilePurpose
includes/components/helpers.phpws_checkbox_card_group() helper function
includes/components/checkbox-card-group.phpTemplate — row markup and icon accent mapping
includes/components/components.cssStyles (.ws-checkbox-card-group rules)