Components
Reusable UI building blocks. Live interactive demos with the PHP code you need to implement them.
Buttons
Three main variants: Primary (gradient CTA), Secondary (outlined), and Ghost (minimal). All use 10px radius and semibold weight.
Primary Button
Red gradient, white text. Used for main CTAs.
<?= ws_button('Default', ['variant' => 'primary']) ?>
<?= ws_button('With Icon', ['variant' => 'primary', 'icon' => 'add']) ?>Secondary Button
Cream background, navy border, navy text. For secondary actions.
<?= ws_button('Secondary', ['variant' => 'secondary']) ?>
<?= ws_button('Settings', ['variant' => 'secondary', 'icon' => 'settings']) ?>Ghost Button
Transparent background, subtle border. For less prominent actions.
<?= ws_button('Ghost', ['variant' => 'ghost']) ?>
<?= ws_button('Cancel', ['variant' => 'ghost', 'icon' => 'close']) ?>Danger Button
<?= ws_button('Delete', ['variant' => 'danger']) ?>States
Size Modifiers
| Size | Code |
|---|---|
| Small | 'size' => 'sm' |
| Medium (default) | 'size' => 'md' |
| Large | 'size' => 'lg' |
Cards
Cards are the primary content container. Use ws_card() for simple cards or ws_card_start() / ws_card_end() for complex content.
Basic Card
<?= ws_card('This is a basic card with simple text content.') ?>Card with Title
<?= ws_card('Description text.', ['title' => 'Card Title']) ?>Custom Card Content
A collaborative exercise for rapid ideation and prototyping.
<?php ws_card_start(); ?>
<!-- Custom card content -->
<?php ws_card_end(); ?>Inputs
All inputs use 12px radius, consistent padding, and a focus ring in the app's accent color.
<?= ws_input('field', ['label' => 'Label', 'placeholder' => 'Enter text...']) ?>
<?= ws_input('q', ['label' => 'Search', 'type' => 'search', 'icon' => 'search']) ?>Badges
Category, status, and duration badges for labeling content.
Variants
<?= ws_badge('Success', ['variant' => 'success']) ?>
<?= ws_badge('Warning', ['variant' => 'warning']) ?>With Icons
Duration Badge
<?= ws_duration('30 min') ?>
<?= ws_duration('1h 30m') ?>Alerts
Contextual feedback messages for user actions.
<?= ws_alert('Message text.', ['variant' => 'success']) ?>
<?= ws_alert('Warning text.', ['variant' => 'warning']) ?>Avatars
User images with initials fallback.
<?= ws_avatar('Bill Bulman', ['size' => 'sm']) ?>
<?= ws_avatar('Jane Doe', ['size' => 'md']) ?>
<?= ws_avatar('Alex Smith', ['size' => 'lg']) ?>Select
Dropdown select fields styled consistently with inputs.
<?= ws_select('category', [
'' => 'Select...',
'ideation' => 'Ideation',
'strategy' => 'Strategy',
], ['label' => 'Category']) ?>Modals
Five size presets. Below 600px, modals convert to bottom sheets.
| Size | Max Width | Use Case |
|---|---|---|
| Small | 360px | Confirmations, simple actions |
| Default | 500px | Standard forms, details |
| Large | 640px | Complex forms, settings |
| XL | 800px | Multi-step flows |
| Full | 100vw - 48px | Image viewers, rich editors |
Empty States
Placeholder content when data is not available.
No items found
Get started by creating your first item.
<?= ws_empty_state('No items found', [
'icon' => 'inbox',
'description' => 'Get started by creating your first item.',
'action' => ws_button('Create Item', ['variant' => 'primary', 'icon' => 'add']),
]) ?>Tabs
Navigation tabs for switching between views.
<?= ws_tabs([
['label' => 'Exercises', 'active' => true],
['label' => 'Icebreakers'],
['label' => 'Workshops'],
]) ?>Progress
Visual progress indicators.
<?= ws_progress(60, ['label' => 'In Progress']) ?>
Accordion
Expandable content sections.
<?= ws_accordion([
['title' => 'Question?', 'content' => 'Answer text.'],
['title' => 'Another?', 'content' => 'More details.'],
]) ?>Tooltip
Contextual information on hover.
<?= ws_tooltip('Hover over me', ['text' => 'This is a helpful tooltip']) ?>Breadcrumb
Navigation breadcrumbs for wayfinding.
<?= ws_breadcrumb([
['label' => 'Home', 'href' => '/'],
['label' => 'Library', 'href' => '/library'],
['label' => 'Current Page'],
]) ?>Skeleton
Loading placeholders that indicate content is being loaded.
<?= ws_skeleton(['type' => 'card']) ?>
Stats
Numeric statistics with labels.
<?= ws_stats([
['value' => '2,847', 'label' => 'Exercises'],
['value' => '156', 'label' => 'Workshops'],
]) ?>