Banner
A full-width strip at the top of a page carrying one announcement that applies to the whole page.
When to Use
Variants
Core
Five documented variants. The icon is never automatic — pass one that matches the message.
<?= 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.
<?= ws_banner('Plan-phase banner.', ['variant' => 'plan', 'icon' => 'edit_note']) ?>.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.
<?= 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.
<?= 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.
<?= ws_banner('Dismissible banner.', [
'variant' => 'warning',
'icon' => 'construction',
'dismissible' => true,
]) ?>| State | Behavior |
|---|---|
| Default | Full-bleed background; contents constrained by .ws-banner__inner. |
| Hover (action) | The action link shifts colour over --transition-fast; the arrow nudges right. |
| Dismissed | The 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.
<?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
| Option | Type | Default | Purpose |
|---|---|---|---|
$message | string | — | Positional banner text. Rendered as raw HTML so it can carry inline markup — see the security note. |
variant | string | 'default' | default | brand | success | warning | dark, plus the phase set learn | plan | facilitate | reflect | dna |
icon | string | '' | Material icon name. No default — omit for no icon. |
action | string | '' | Action link text. Omit for no action. |
href | string | '#' | Action URL. Note the default is '#', not empty — set it whenever you set action. |
onclick | string | '' | Inline JS handler on the action |
dismissible | bool | false | Show the close button |
centered | bool | true | Centre the inner content. On by default. |
id | string | '' | Element ID |
class | string | '' | Additional CSS classes |
$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
| Concern | Behavior |
|---|---|
| ARIA | No 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. |
| Keyboard | The action link and close button are native focusable controls, reached with Tab in that order. The banner body isn't focusable. |
| Focus | Dismissing 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. |
| Labelling | The 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 order | A banner above the header is the first thing announced on every page. Keep it short, and remove it once the announcement is stale. |
| Contrast | Each variant pairs tested token foreground and background, including --text-inverse on the dark and brand fills. Don't override one side alone. |
Tokens
| Token | Used for |
|---|---|
--gray-100 | Default variant background |
--mainsite-gradient / --mainsite-primary / --mainsite-light | Brand variant fill and accents |
--color-success-surface / --color-success-ink / --color-success-darker | Success variant |
--color-warning-ink | Warning variant foreground |
--color-ink | Dark variant background |
--phase-learn / --phase-plan / --phase-facilitate / --phase-reflect (and their -dark pairs) | Phase variant fills and text |
--text-inverse / --text-dark / --text-muted | Foreground across variants |
--text-h4 / --text-ui / --text-small / --text-meta | Message and action type scale |
--radius-sm | Action link corners |
--font-body | Body face |
--transition-fast | Action hover |
CSS Classes
| Class | Purpose |
|---|---|
.ws-banner | Full-bleed wrapper |
.ws-banner--default | Light neutral background |
.ws-banner--brand | Brand gradient |
.ws-banner--success | Green tint |
.ws-banner--warning | Amber tint |
.ws-banner--dark | Dark fill |
.ws-banner--learn / --plan / --facilitate / --reflect / --dna | Phase-scoped fills |
.ws-banner--centered | Centre-aligned inner content (applied by default) |
.ws-banner__inner | Max-width flex container |
.ws-banner__icon | Leading icon |
.ws-banner__message | Text content |
.ws-banner__action | Action link |
.ws-banner__arrow | Trailing arrow on the action |
.ws-banner__close | Dismiss button |
Files
| File | Purpose |
|---|---|
includes/components/helpers.php | ws_banner() helper function |
includes/components/banner.php | Template — variant class assembly and markup |
includes/components/components.css | Styles (.ws-banner rules) |