Canvas Design System
Main Site Tokens

Task Checklist

Tracks activation tasks against real data, with each undone item linking to the place it gets done.

When to Use

Use when: A user has setup steps left and each one lives somewhere specific — the MyWorkshopr Getting Started list is the canonical case. Done state must be computed from real data, never hardcoded.
Don't use when: The list is explanatory rather than tracked — that's Checklist or How Steps. For a single proportion use Progress.

Variants

Full — progress, phases, descriptions

The complete form. Descriptions explain the value of each step and disappear once it's done, so the list gets quieter as the user progresses.

Getting started

Four steps to your first workshop that works.

2 of 4 done
  1. Plan Build your first agenda (done) Drag a few activities onto the timeline — it takes about five minutes.
  2. Plan Save it so it's yours anywhere (done) A saved agenda opens on any device and can be shared with your team.
  3. Reflect Take the 3-minute DNA quiz Find out which of the five facilitator archetypes matches how you run a room.
  4. Learn Find an exercise you'd actually run Browse the library and favourite one for your next session.
<?= ws_task_checklist([
    ['label'       => 'Build your first agenda',
     'description' => 'Drag a few activities onto the timeline.',
     'href'        => '/planner/',
     'done'        => $user->hasPlan(),
     'phase'       => 'Plan',
     'key'         => 'build'],
], [
    'title'       => 'Getting started',
    'subtitle'    => 'Four steps to your first workshop that works.',
    'dismissible' => true,
]) ?>

Complete

Every item done. Decide deliberately what happens next — a finished checklist that never goes away becomes clutter.

Getting started

3 of 3 done
  1. Plan Build your first agenda (done)
  2. Plan Save it so it's yours anywhere (done)
  3. Reflect Take the 3-minute DNA quiz (done)

Minimal

No progress bar, no phases, no descriptions — for a short list where the count would be noise.

<?= ws_task_checklist($items, ['title' => 'Set up your team', 'progress' => false]) ?>

States

StateBehavior
UndoneRenders as a link to href with a hollow icon, its description, and a trailing arrow. This is the actionable state.
DoneFilled check in --color-success, description hidden, and no longer a link — there's nothing left to do there.
Undone with no hrefRenders as plain text. Valid, but the user is then told what to do without being shown where — supply an href.
ProgressOn by default: an "x of N done" count and a bar, both computed from the items' done flags. No separate total to keep in sync.
Dismisseddismissible renders a button carrying data-checklist-dismiss. The component doesn't handle the click — wire it and persist the choice, or the checklist returns on the next load.
HoverUndone rows lift their background; done rows are inert.
FocusUndone rows are links with a --focus-ring outline.

Real-World Usage

The MyWorkshopr activation panel. Every done flag is a query against real state — that's the contract, and it's what stops the list lying to the user.

<?php
$hasPlan  = (bool) getPlanCount($userId);
$hasSaved = (bool) getSavedPlanCount($userId);
$hasDna   = (bool) getDnaResult($userId);
$hasFav   = (bool) getFavouriteCount($userId);
$tasks = [
    ['label' => 'Build your first agenda', 'href' => '/planner/',  'done' => $hasPlan,
     'phase' => 'Plan',    'key' => 'build',
     'description' => 'Drag a few activities onto the timeline.'],
    ['label' => 'Save it so it\'s yours anywhere', 'href' => '/planner/', 'done' => $hasSaved,
     'phase' => 'Plan',    'key' => 'save'],
    ['label' => 'Take the 3-minute DNA quiz', 'href' => '/dna/',   'done' => $hasDna,
     'phase' => 'Reflect', 'key' => 'dna'],
    ['label' => 'Find an exercise you\'d actually run', 'href' => '/library/', 'done' => $hasFav,
     'phase' => 'Learn',   'key' => 'library'],
];

// Hide it entirely once everything is done, rather than showing a finished list.
if (in_array(false, array_column($tasks, 'done'), true)):
?>
    <?= ws_task_checklist($tasks, ['title' => 'Getting started', 'dismissible' => true]) ?>
<?php endif; ?>

<script>
document.querySelector('[data-checklist-dismiss]')?.addEventListener('click', async () => {
    await fetch('/api/prefs.php?action=dismiss_checklist', { method: 'POST' });
    document.querySelector('.ws-task-checklist').remove();
});
</script>

Options

Checklist options

OptionTypeDefaultPurpose
$itemsarray[]Positional. The task definitions, in the order they should be done.
titlestring'Getting started'Heading, set in Fraunces
subtitlestringnullSupporting line under the title
progressbooltrueShow the "x of N done" count and bar
dismissibleboolfalseRender the dismiss button. Your JS handles the click.
idstringnullElement ID
classstring''Additional CSS classes

Item options

OptionTypeDefaultPurpose
labelstringrequiredThe task. Phrase it as the outcome the user gets.
doneboolfalseCompletion state. Compute it from real data.
hrefstringnullWhere the task happens. Undone items with an href render as links.
descriptionstringnullWhy it's worth doing. Hidden once done.
phasestringnullUppercase eyebrow — Learn, Plan, Facilitate, Reflect
keystringnullEmitted as data-task for JS and analytics

Accessibility

ConcernBehavior
State iconsThe done and todo icons carry aria-hidden="true", so the tick isn't read as stray text.
Dismiss buttonLabelled aria-label="Hide this checklist" — it says what it does rather than just "close".
KeyboardUndone items are real links in the tab order; done items aren't focusable, which correctly signals there's nothing to do.
FocusLinks use --focus-ring with --focus-ring-offset, matching the rest of the platform.
Completion stateWorth knowing: done versus undone is conveyed by icon and colour, with no text equivalent on the row. The progress count ("2 of 4 done") carries it for the list as a whole, but an individual row's state isn't announced. If a checklist ships without the progress bar, consider adding visually-hidden text to each row.
Link textThe label is the link's accessible name, so it must stand alone in a screen reader's link list — "Build your first agenda" does; "Get started" wouldn't.
ContrastDone uses --color-success, phase eyebrows --phase-learn-dark, descriptions --text-muted — all tested.

Tokens

TokenUsed for
--bg-surface / --bg-subtlePanel background and row hover
--color-successCompleted check
--mainsite-primaryProgress bar fill
--phase-learn-darkPhase eyebrow
--gray-100 / --gray-200 / --gray-300Dividers, progress track, and the todo icon
--text-dark / --text-muted / --text-faintTitle, description, and completed-label text
--focus-ring / --focus-ring-offsetLink focus treatment
--radius-lg / --radius-2xl / --radius-fullPanel, row, and progress-bar corners
--text-h3 / --text-body / --text-small / --text-meta / --text-captionType scale across title, label, and eyebrow
--font-heading / --font-body / --font-medium / --font-semiboldFraunces title, Inter body
--tracking-widePhase eyebrow letter-spacing
--space-1 through --space-6Internal rhythm
--transition-fastRow hover

CSS Classes

ClassPurpose
.ws-task-checklistPanel container
.ws-task-checklist__headerTitle, subtitle, and dismiss row
.ws-task-checklist__title / __subtitleHeading text
.ws-task-checklist__dismissDismiss button, carrying data-checklist-dismiss
.ws-task-checklist__progress / __progress-bar / __progress-countProgress display
.ws-task-checklist__body / __itemsList wrappers
.ws-task-checklist__item / __rowAn individual task and its layout row
.ws-task-checklist__checkState icon holder
.ws-task-checklist__icon-done / __icon-todoThe two state icons
.ws-task-checklist__phaseUppercase phase eyebrow
.ws-task-checklist__label / __descTask text
.ws-task-checklist__arrowTrailing arrow on actionable rows

Files

FilePurpose
includes/components/helpers.phpws_task_checklist() helper function
includes/components/task-checklist.phpTemplate — progress calculation and row markup
includes/components/components.cssStyles (.ws-task-checklist rules)

No JS file — dismissal is left to the consumer, as noted in States.