Section Header
Introduces a section with a heading, an optional italic accent line, and a supporting paragraph.
When to Use
<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.
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
| State | Behavior |
|---|---|
| Default | The only state. Static text with no hover, focus, or interactive behaviour. |
| Omitted parts | Label, subtitle, and description each render only when supplied — no empty elements or reserved space are left behind. |
| Long titles | Wraps 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.
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
| Option | Type | Default | Purpose |
|---|---|---|---|
$title | string | required | Positional heading text. With a subtitle, this is the first half of the sentence. |
subtitle | string | null | Second line, italic and brand-coloured. Reads as a continuation of the title. |
description | string | null | Supporting paragraph below the heading |
label | string | null | Small uppercase label above the heading |
tag | string | 'h2' | h2 | h3. Choose by outline position. |
align | string | 'center' | center | left. Only left emits a modifier class. |
id | string | null | Element ID — useful as an in-page anchor target |
class | string | '' | Additional CSS classes |
Accessibility
| Concern | Behavior |
|---|---|
| Semantics | Renders a real <h2> or <h3>, so the section joins the document outline and screen-reader users can jump to it by heading. |
| Heading order | Yours 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. |
| Subtitle | Rendered inside the heading element, so the announced heading is title plus subtitle as one phrase. Write them to make sense read together. |
| Label | Sits outside the heading, so it isn't part of the accessible heading text. Don't put essential context there. |
| Keyboard | Nothing focusable. With an id set, it works as a skip-link or anchor target. |
| Contrast | Title uses --text-dark, description --text-muted, and subtitle --mainsite-primary — all tested against the page surface. |
Tokens
| Token | Used for |
|---|---|
--text-dark | Title colour |
--mainsite-primary | Subtitle and label accent |
--text-muted | Description colour |
--text-h2 | Heading display size |
--text-small / --text-caption | Description and label sizes |
--font-heading | Fraunces on title and subtitle |
--font-body / --font-semibold / --font-bold | Inter on the description, and heading weights |
--tracking-tight | Letter-spacing on the display heading |
--leading-snug / --leading-relaxed | Heading and description line height |
--space-2 / --space-3 / --space-10 | Label-to-title gap, title-to-description gap, and the block's bottom margin |
CSS Classes
| Class | Purpose |
|---|---|
.ws-section-header | Container, centred by default |
.ws-section-header--left | Left alignment |
.ws-section-header__label | Small uppercase label |
.ws-section-header__title | The heading element |
.ws-section-header__subtitle | Italic brand-coloured accent inside the heading |
.ws-section-header__desc | Description paragraph |
Files
| File | Purpose |
|---|---|
includes/components/helpers.php | ws_section_header() helper function |
includes/components/section-header.php | Template — tag selection and conditional parts |
includes/components/components.css | Styles (.ws-section-header rules) |