Feature Card
Icon, title, and one line of description — the unit a benefits grid is built from.
When to Use
Variants
Default
Curated Library
Browse our collection of workshops, exercises, and icebreakers.
AI Facilitation Coach
Get real-time guidance powered by AI during your sessions.
Workshop Planner
Drag and drop to build your perfect agenda.
<?= ws_feature_card('Curated Library', 'Browse our collection…', [
'icon' => 'menu_book',
]) ?>Compact
Smaller icon and tighter padding, for grids inside app screens rather than full-width marketing sections.
Quick Setup
Get started in under 5 minutes.
Team Ready
Built for groups of any size.
<?= ws_feature_card('Quick Setup', 'Get started fast.', [
'icon' => 'bolt',
'variant' => 'compact',
]) ?>Coming soon
Dims the card to mark a feature as not yet available. Don't pass href alongside it — a dimmed card that still navigates is worse than no card.
Custom Builder
Create your own workshops from scratch.
Analytics
Track session outcomes and engagement.
Templates
Start from proven templates.
<?= ws_feature_card('Custom Builder', 'Create your own workshops.', [
'icon' => 'construction',
'variant' => 'coming-soon',
]) ?>As links
With href the card renders as an <a>, making the whole surface focusable and clickable — the accessible way to make a feature card actionable.
<?= ws_feature_card('Workshops', 'Full-day and half-day formats.', [
'icon' => 'groups',
'href' => '/library/workshops/',
]) ?>States
| State | Behavior |
|---|---|
| Default | Surface fill, bordered, with a tinted icon holder. |
| Hover | Only meaningful on the link form: border and shadow step up over --transition-base. |
| Focus | Native focus ring, present only when href makes the card an anchor. A non-link card has no focus state. |
| Coming soon | Adds .ws-feature-card--coming-soon, which dims the card. Purely visual — it doesn't disable anything. |
| Icon vs SVG | icon renders a Material Symbol; svg injects raw markup instead. Passing both is redundant — pick one. |
Real-World Usage
The homepage capability grid: three live features as links into the apps, one marked coming-soon and deliberately not linked.
Plan
Build an agenda that fits the room and the clock.
Facilitate
Run the session with timings and prompts at hand.
Reflect
Turn your notes into a readout worth sending.
Analytics
See how engagement moved across the session.
<?php foreach ($features as $f): ?>
<?= ws_feature_card($f['title'], $f['description'], [
'icon' => $f['icon'],
'href' => $f['live'] ? $f['url'] : null,
'variant' => $f['live'] ? 'default' : 'coming-soon',
]) ?>
<?php endforeach; ?>Options
| Option | Type | Default | Purpose |
|---|---|---|---|
$title | string | required | Positional. The capability, in one or two words. |
$description | string | required | Positional. One sentence. Keep lengths even across a grid so the cards align. |
icon | string | null | Material icon name |
svg | string | null | Raw SVG markup, as an alternative to icon |
variant | string | 'default' | default | compact | coming-soon |
href | string | null | Renders the card as an <a> |
id | string | null | Element ID |
class | string | '' | Additional CSS classes |
attrs | array | [] | Extra HTML attributes as key/value pairs |
Accessibility
| Concern | Behavior |
|---|---|
| Semantics | A <div> normally, an <a> with href. Use href rather than a JS click handler — it's the difference between a keyboard-reachable card and an invisible one. |
| Title element | The title is styled text, not a heading element. That keeps the page outline clean but means a screen-reader user can't jump between cards by heading — introduce the grid with a Section Header so the set is findable. |
| Link text | As a link, the accessible name is the title plus description — usually good. Avoid titles like "Learn more" that say nothing out of context. |
| Coming soon | Known gap: the state is conveyed by dimming alone. Nothing in the markup says "coming soon", so it's invisible to screen readers and easily missed at low contrast. Put the words in the description — "Coming soon — track engagement…". |
| Icons | Decorative. The title always carries the meaning, so an icon-only card would have nothing to announce. |
| Contrast | Title uses --text-dark and description --text-muted. The coming-soon variant reduces contrast further — don't stack extra opacity on top. |
Tokens
| Token | Used for |
|---|---|
--bg-surface | Card background |
--gray-200 / --gray-300 | Border, resting and hover |
--mainsite-primary / --mainsite-very-light | Icon colour and its tinted holder |
--text-dark | Title |
--text-muted | Description |
--text-inverse | Icon on a solid fill |
--radius-xl | Card corners |
--shadow-lg | Hover elevation on the link form |
--text-h2 / --text-h3 / --text-ui / --text-body / --text-small / --text-meta | Type scale across variants |
--font-heading / --font-body / --font-semibold | Fraunces title, Inter description |
--transition-base | Hover transition |
CSS Classes
| Class | Purpose |
|---|---|
.ws-feature-card | Base card, <div> or <a> |
.ws-feature-card--compact | Reduced padding and icon size |
.ws-feature-card--coming-soon | Dimmed unavailable state |
.ws-feature-card__icon | Tinted icon holder |
.ws-feature-card__title | Title text |
.ws-feature-card__desc | Description text |
Files
| File | Purpose |
|---|---|
includes/components/helpers.php | ws_feature_card() helper function |
includes/components/feature-card.php | Template — tag selection and icon/SVG branch |
includes/components/components.css | Styles (.ws-feature-card rules) |