Canvas Design System
Main Site Tokens

Banner

A full-width strip at the top of a page carrying one announcement that applies to the whole page.

When to Use

Use when: The message is about the site or the session rather than about anything on the page — a maintenance window, a feature launch, a time-limited offer. One banner per page, at the very top.
Don't use when: The message concerns a specific region or action — use Alert inline. For a page's own headline use Hero, and for a transient confirmation use Toast.

Variants

Core

Five documented variants. The icon is never automatic — pass one that matches the message.

This is a default announcement banner.
New feature: Custom Exercise Builder is now live.
Your workshop has been saved successfully.
Scheduled maintenance this Saturday 2–4 AM UTC.
Limited time: 30% off annual plans.
<?= ws_banner('Default announcement.') ?>
<?= ws_banner('Brand message', ['variant' => 'brand', 'icon' => 'auto_awesome']) ?>
<?= ws_banner('Success message', ['variant' => 'success', 'icon' => 'check_circle']) ?>
<?= ws_banner('Warning message', ['variant' => 'warning', 'icon' => 'schedule']) ?>
<?= ws_banner('Dark banner', ['variant' => 'dark', 'icon' => 'local_offer']) ?>

Phase

Five additional variants exist in CSS for phase-scoped surfaces. They aren't in the helper's docblock but work, because the variant string is interpolated straight into the class name.

Learn-phase banner.
Plan-phase banner.
Facilitate-phase banner.
Reflect-phase banner.
Facilitator DNA banner.
<?= ws_banner('Plan-phase banner.', ['variant' => 'plan', 'icon' => 'edit_note']) ?>
info Because the variant is interpolated rather than validated, a typo produces .ws-banner--typo, which matches nothing — the banner renders unstyled rather than erroring. Check the class list below for valid values.

Alignment

Centred by default. Set centered to false when the banner sits above left-aligned page content and the centring looks detached.

Left-aligned banner for contextual pages. Learn more
<?= ws_banner('Left-aligned banner.', [
    'centered' => false,
    'action'   => 'Learn more',
    'href'     => '/docs/',
]) ?>

States

With an action

One action per banner, rendered as a link with a trailing arrow. Give it a verb.

New: Run Mode lets you present your workshop like a pro. Try it now
Workshop planning just got easier with drag-and-drop. Open Planner
<?= ws_banner('New feature announcement.', [
    'variant' => 'brand',
    'icon'    => 'slideshow',
    'action'  => 'Try it now',
    'href'    => '/planner/',
]) ?>

Dismissible

Adds a close button. The dismissal is visual only and lasts until the next page load — persist it yourself if it should stick.

Maintenance window: Saturday 2–4 AM UTC.
Welcome back. Your 3 saved workshops are ready. View workshops
<?= ws_banner('Dismissible banner.', [
    'variant'     => 'warning',
    'icon'        => 'construction',
    'dismissible' => true,
]) ?>
StateBehavior
DefaultFull-bleed background; contents constrained by .ws-banner__inner.
Hover (action)The action link shifts colour over --transition-fast; the arrow nudges right.
DismissedThe close button sets display: none on the banner via an inline onclick. The element stays in the DOM and no event fires — to remember the dismissal, write your own handler and a cookie or localStorage flag.

Real-World Usage

The standard site-wide pattern: rendered above the header, gated on a flag, dismissible, with the action pointing at the thing being announced.

Synthesize turns your session notes into a shareable readout. Open Synthesize
<?php if (!isset($_COOKIE['dismissed_synthesize_banner'])): ?>
    <?= ws_banner('Synthesize turns your session notes into a shareable readout.', [
        'variant'     => 'brand',
        'icon'        => 'hub',
        'action'      => 'Open Synthesize',
        'href'        => '/synthesize/',
        'dismissible' => true,
    ]) ?>
<?php endif; ?>

Options

OptionTypeDefaultPurpose
$messagestringPositional banner text. Rendered as raw HTML so it can carry inline markup — see the security note.
variantstring'default'default | brand | success | warning | dark, plus the phase set learn | plan | facilitate | reflect | dna
iconstring''Material icon name. No default — omit for no icon.
actionstring''Action link text. Omit for no action.
hrefstring'#'Action URL. Note the default is '#', not empty — set it whenever you set action.
onclickstring''Inline JS handler on the action
dismissibleboolfalseShow the close button
centeredbooltrueCentre the inner content. On by default.
idstring''Element ID
classstring''Additional CSS classes
warning Security: $message is echoed unescaped. Banners are usually authored copy, so that's normally fine — but escape with htmlspecialchars() at the call site the moment any part of the message comes from user data.

Accessibility

ConcernBehavior
ARIANo role is applied. As page-load content that's correct — the banner is read in document order. If you inject one after load and it's urgent, add role="status" yourself.
KeyboardThe action link and close button are native focusable controls, reached with Tab in that order. The banner body isn't focusable.
FocusDismissing hides the banner while focus is on its close button, leaving focus on a hidden element. Move focus to the page heading afterwards if the banner is dismissible in a keyboard flow.
LabellingThe close button is labelled aria-label="Dismiss banner". The action link is labelled by its own text, so make that text meaningful on its own — "Open Planner", not "Click here".
Reading orderA banner above the header is the first thing announced on every page. Keep it short, and remove it once the announcement is stale.
ContrastEach variant pairs tested token foreground and background, including --text-inverse on the dark and brand fills. Don't override one side alone.

Tokens

TokenUsed for
--gray-100Default variant background
--mainsite-gradient / --mainsite-primary / --mainsite-lightBrand variant fill and accents
--color-success-surface / --color-success-ink / --color-success-darkerSuccess variant
--color-warning-inkWarning variant foreground
--color-inkDark variant background
--phase-learn / --phase-plan / --phase-facilitate / --phase-reflect (and their -dark pairs)Phase variant fills and text
--text-inverse / --text-dark / --text-mutedForeground across variants
--text-h4 / --text-ui / --text-small / --text-metaMessage and action type scale
--radius-smAction link corners
--font-bodyBody face
--transition-fastAction hover

CSS Classes

ClassPurpose
.ws-bannerFull-bleed wrapper
.ws-banner--defaultLight neutral background
.ws-banner--brandBrand gradient
.ws-banner--successGreen tint
.ws-banner--warningAmber tint
.ws-banner--darkDark fill
.ws-banner--learn / --plan / --facilitate / --reflect / --dnaPhase-scoped fills
.ws-banner--centeredCentre-aligned inner content (applied by default)
.ws-banner__innerMax-width flex container
.ws-banner__iconLeading icon
.ws-banner__messageText content
.ws-banner__actionAction link
.ws-banner__arrowTrailing arrow on the action
.ws-banner__closeDismiss button

Files

FilePurpose
includes/components/helpers.phpws_banner() helper function
includes/components/banner.phpTemplate — variant class assembly and markup
includes/components/components.cssStyles (.ws-banner rules)