Canvas Design System
Main Site Tokens

Navigation

The unified site header: brand, section nav, app switcher, and account menu — one component, ws_header().

warning Rewritten July 2026. This page previously documented a data-property attribute and a .property-switcher-* / .ps-* class family. None of those exist in includes/components/header.php — the real API is ws_header($section, $opts) and the real prefix is .wsh-. The old per-app colour table also listed four retired pre-unification hexes. Everything below is checked against the current source.

When to Use

Use when: Any page that needs the site header. Call ws_header('planner') with the section the page belongs to — that one argument drives the brand wordmark, the nav items, the active state, and the app switcher's current entry.
Don't use when: You're inside a full-height app shell that supplies its own bar — those use ws_app_bar() beneath the header, not instead of it. Never hand-roll a header; the account and auth wiring lives here.

Variants

Sections

The section key selects a preset: its nav items, wordmark, and accent. Seventeen are defined; passing an unknown key falls back to home rather than erroring.

KeySurface
homeMain site — the fallback for any unrecognised key
libraryExercises, workshops, icebreakers, collections, goals
plannerWorkshop Builder
facilitatorFacilitator app
synthesizePost-session synthesis
coachAI Coach
strategistWorkshop Strategist
sidekickSidekick
agenda-checkAgenda Check
interventionIntervention Cards
myworkshoprMyWorkshopr dashboard
dnaFacilitator DNA
academyAcademy / training
tipsTips
podcastsPodcasts
toolkitsToolkits, including the Hybrid Kit
roiROI calculator
<?= ws_header('planner') ?>
info This list is the header's own section table. PRODUCT.md §5 remains the canonical app inventory — if the two disagree, PRODUCT.md wins and the header needs updating.

Custom nav items

Passing nav in the options replaces the section's preset nav entirely — useful for a sub-app that shares a section but needs its own links.

<?= ws_header('library', ['nav' => [
    ['label' => 'Exercises',   'href' => '/library/exercises/'],
    ['label' => 'Icebreakers', 'href' => '/library/icebreakers/'],
]]) ?>

Resolution order

The section is resolved from the first of these that's set, so most pages never pass it explicitly — they set a global once and the header picks it up.

OrderSource
1The $section argument
2$GLOBALS['header_section']
3$GLOBALS['header_property'] — the legacy name, still honoured
4'home'

States

StateBehavior
Active nav itemMarked with aria-current="page", so the current location is conveyed structurally rather than by styling alone.
App switcher closedThe trigger is labelled "Switch app" with aria-expanded="false" and aria-haspopup="dialog".
App switcher openThe panel lists apps grouped by phase, each with a name and description.
Signed outA sign-in control replaces the account cluster.
Signed inAvatar initials and name, with a menu trigger labelled "Account menu" carrying aria-haspopup="menu".
Styles emitted onceThe component tracks a static flag and emits its CSS on first call only, so multiple invocations on one page don't duplicate it.

Real-World Usage

The standard page opening. Most pages set the global in their config block and let the shared header include do the call.

<?php
require_once 'config.php';
require_once 'includes/shared-auth.php';

$header_section = 'library';        // resolved by ws_header()
require_once 'includes/header.php';
?>

<!-- …page content… -->

<?= ws_footer() ?>

Calling it directly is equivalent, and clearer in a standalone template:

<?= ws_header('library') ?>

Options

OptionTypeDefaultPurpose
$section?stringnullPositional. One of the 17 keys above. null falls through the resolution order.
navarraythe section's presetReplaces the nav items. Each entry takes label and href.

The signature is ws_header(?string $section = null, array $opts = []). It returns markup as a string, like the other ws_* helpers.

Accessibility

Among the better-wired components in the platform — every interactive cluster carries a name and a state.

ConcernBehavior
LandmarkThe header is labelled "Site header", and the nav is labelled per section — so a page with several navs stays distinguishable.
Current pagearia-current="page" on the active nav item.
App switcherTrigger labelled "Switch app", with aria-haspopup="dialog" and aria-expanded reflecting the panel.
Account menuLabelled "Account menu" with aria-haspopup="menu". The avatar control is labelled "Account".
Icon controlsSearch and MyWorkshopr shortcuts carry explicit aria-labels rather than relying on their glyphs.
KeyboardAll triggers are real buttons and nav items real links, so tabbing and activation are native.
Focus in panelsWorth checking per surface: the switcher and account panels don't trap focus or return it to the trigger on close — the same gap as Modal. For a dropdown this is less severe than for a dialog, but Esc-to-close with focus return would be an improvement.

Tokens

The header emits its accent as an inline custom property computed from the section, with a literal fallback beside the var() so it still paints if tokens haven't loaded.

warning Every section accent is now Learn Red. The old table on this page assigned #F87171 to Tips, #BE123C to Podcasts, #0284C7 to Planner, and #7C4DFF to Coach and Interventions. All were retired by Red Unification — --phase-learn, --phase-plan, --phase-facilitate, and --phase-reflect all resolve to #E54D4D. Sections still exist as semantic grouping; they no longer differ in colour. See Colors.

Beyond the accent, the header draws on the shared surface, ink, radius, and type scales. Read the .wsh-header rules in includes/components/header.php — the styles are emitted by the component itself rather than living in components.css.

CSS Classes

The prefix is .wsh-. The .property-switcher-* and .ps-* classes this page used to list do not exist.

ClassPurpose
.wsh-headerThe <header> landmark
.wsh-header__left / __rightThe two clusters
.wsh-header__brand / __brandwrap / __mark / __word / __tagLogo, wordmark, and section tag
.wsh-header__nav / __navitemSection nav and its links
.wsh-header__switcher / __panelApp switcher trigger and panel
.wsh-panel__group / __app / __descPhase group, app entry, and its description inside the panel
.wsh-header__account / __avatar / __avatar-initialAccount cluster
.wsh-header__menu / __menu-head / __menu-name / __menu-email / __menu-itemAccount dropdown
.wsh-header__submenu / __submenu-itemNested menu level
.wsh-header__signinSigned-out control
.wsh-header__icon-btnSearch and shortcut buttons
.wsh-header__caret / __dotDisclosure caret and status dot

Files

FilePurpose
includes/components/header.phpws_header() — the section table, markup, and inline styles. The source of truth for this page.
includes/header.phpShared page include that calls ws_header()
design-system/platform-tokens.cssPhase tokens the accent resolves against
PRODUCT.md §5Canonical app inventory — the header's section list should match it