Canvas Design System
Main Site Tokens

Section Header

Introduces a section with a heading, an optional italic accent line, and a supporting paragraph.

When to Use

Use when: You're opening a section within a page — a feature block on a landing page, a listing's introduction, a settings group. It replaces the inline heading markup that had drifted apart across the site.
Don't use when: It's the page's own top-level headline — that's Hero, which owns the <h1>. This component only renders h2 or h3.

Variants

Centred with a subtitle

The signature form. Title and subtitle are one sentence split across two lines — the subtitle renders italic in the brand colour, so write them to read continuously.

Everything you need to run
world-class workshops.

Ditch the clunky tools. Workshopr gives you a curated library of exercises, agendas, and an AI coach.

<?= ws_section_header('Everything you need to run', [
    'subtitle'    => 'world-class workshops.',
    'description' => 'Ditch the clunky tools…',
]) ?>

With a label

A small uppercase label above the heading, naming the section's role. Keep it to one or two words.

Our Mission

Democratizing the art of
facilitation.

We believe every team deserves a great facilitator.

<?= ws_section_header('Democratizing the art of', [
    'subtitle' => 'facilitation.',
    'label'    => 'Our Mission',
]) ?>

Left aligned

Use left alignment inside app screens and listings, where centred text fights the surrounding left-aligned content. Centred belongs on marketing pages.

Browse Workshops

Find the perfect workshop for your next session.

<?= ws_section_header('Browse Workshops', [
    'description' => 'Find the perfect workshop for your next session.',
    'align'       => 'left',
]) ?>

Heading level

Pick the tag by document structure, not by the size you want. A section nested inside another section takes h3.

H2 Heading (default)

H3 Heading

<?= ws_section_header('Nested section', ['tag' => 'h3', 'align' => 'left']) ?>

Title only

Everything but the title is optional. When the section is self-explanatory, don't invent a description to fill the slot.

Simple Section Title

<?= ws_section_header('Simple Section Title') ?>

States

StateBehavior
DefaultThe only state. Static text with no hover, focus, or interactive behaviour.
Omitted partsLabel, subtitle, and description each render only when supplied — no empty elements or reserved space are left behind.
Long titlesWraps naturally with --leading-snug and --tracking-tight holding the display type readable. No truncation.

Real-World Usage

Two calls on one page showing the alignment rule in practice: centred to open a marketing block, left-aligned to introduce a listing further down.

Why Workshopr

Built for facilitators who
actually run the room.

Every feature came from a session that went sideways.

Recent workshops

Pick up where you left off.

<?= ws_section_header('Built for facilitators who', [
    'subtitle'    => 'actually run the room.',
    'label'       => 'Why Workshopr',
    'description' => 'Every feature came from a session that went sideways.',
]) ?>

<?= ws_section_header('Recent workshops', [
    'description' => 'Pick up where you left off.',
    'align'       => 'left',
    'tag'         => 'h3',
]) ?>

Options

OptionTypeDefaultPurpose
$titlestringrequiredPositional heading text. With a subtitle, this is the first half of the sentence.
subtitlestringnullSecond line, italic and brand-coloured. Reads as a continuation of the title.
descriptionstringnullSupporting paragraph below the heading
labelstringnullSmall uppercase label above the heading
tagstring'h2'h2 | h3. Choose by outline position.
alignstring'center'center | left. Only left emits a modifier class.
idstringnullElement ID — useful as an in-page anchor target
classstring''Additional CSS classes

Accessibility

ConcernBehavior
SemanticsRenders a real <h2> or <h3>, so the section joins the document outline and screen-reader users can jump to it by heading.
Heading orderYours to get right. The component honours whatever tag you pass, including one that skips a level. Never pick the tag for its size — use class if you need to adjust appearance.
SubtitleRendered inside the heading element, so the announced heading is title plus subtitle as one phrase. Write them to make sense read together.
LabelSits outside the heading, so it isn't part of the accessible heading text. Don't put essential context there.
KeyboardNothing focusable. With an id set, it works as a skip-link or anchor target.
ContrastTitle uses --text-dark, description --text-muted, and subtitle --mainsite-primary — all tested against the page surface.

Tokens

TokenUsed for
--text-darkTitle colour
--mainsite-primarySubtitle and label accent
--text-mutedDescription colour
--text-h2Heading display size
--text-small / --text-captionDescription and label sizes
--font-headingFraunces on title and subtitle
--font-body / --font-semibold / --font-boldInter on the description, and heading weights
--tracking-tightLetter-spacing on the display heading
--leading-snug / --leading-relaxedHeading and description line height
--space-2 / --space-3 / --space-10Label-to-title gap, title-to-description gap, and the block's bottom margin

CSS Classes

ClassPurpose
.ws-section-headerContainer, centred by default
.ws-section-header--leftLeft alignment
.ws-section-header__labelSmall uppercase label
.ws-section-header__titleThe heading element
.ws-section-header__subtitleItalic brand-coloured accent inside the heading
.ws-section-header__descDescription paragraph

Files

FilePurpose
includes/components/helpers.phpws_section_header() helper function
includes/components/section-header.phpTemplate — tag selection and conditional parts
includes/components/components.cssStyles (.ws-section-header rules)