Relay component catalog
Live variants and states on Bootstrap 6.0.0-alpha.1, scoped with b2-bs6 and Relay tokens. Buttons, forms, and feedback update local state only. Use keyboard Tab to inspect focus. Search the sample table for an empty state. Open the native dialog and press Escape.
Atoms
Icon button / Button group
Badge / Status / Tag
Avatar / Avatar group
Input / Label / Helper
Default with helper text.Type to preview the invalid state.Textarea / Select
Checkbox / Radio
Switch / Slider
OnProgress / Spinner / Skeleton
Text / Amount / Separator
$128,450.80Body text for decisions and context.
Secondary content stays readable.tabular-nums · 123456Validation and risk use semantic color.
Molecules
Alert
Card / KPI
↗ 18% vs. yesterday
Tabs
Accordion / Details
Billing details
Example monthly plan. No purchase.
Payment methods
No card is connected in this demo.
Workspace permissions
Illustrative member roles.
Dialog / Drawer
Close with Escape or the close control. No outside action.
Menu / Popover / Tooltip
Search / Table / Empty state
| Agent | Status | Calls |
|---|---|---|
| Research Agent | Online | 420 |
| Code Agent | Pending | 310 |
| Ops Agent | Online | 188 |
| Inbox Agent | Paused | 12 |
Pagination / Breadcrumb / Stepper
- Details
- Review
- Complete
Toast / Notification / List item
Charts
Line / Area / Sparkline
Trend colors identify a series, not approval.Bar / Stack / Distribution
Donut / Progress ring
Cash-flow comparison
Layouts
Sidebar / Header / Navigation
Overview
Form / Validation / Action footer
Catalog coverage
Button, icon button, button group, badge, status, tag, avatar, avatar group, label, input, helper/error, textarea, select, checkbox, radio, switch, slider, progress, spinner, skeleton, amount/text, separator, alert, card/KPI, tabs, details/accordion, dialog, drawer, menu/popover, tooltip, search, table/empty state, pagination, breadcrumb, stepper, toast, notification/list item, charts and app/form layouts.
Menus are lightweight illustrative patterns. Dialogs and drawers use native modal focus containment and Escape. A production application should add its own menu keyboard navigation and routing. No live preview sends a tool call or contacts a server.
Extended variants
Local, keyboard-focusable examples. File selection stays in this page; nothing is uploaded.
Buttons: variants and outline
Buttons: sizes and disabled
Buttons: full width and shapes
Buttons: active, icons, counters and loading
Button groups: sizes, toolbar and vertical
Button groups: checks, radio and dropdown
Badges: variants, pill, square, dot and positioned
Alerts: variants, actions, icon and dismissible
Your fictitious report is available.
Cards: header, footer, image and horizontal
Card body with a generated CSS illustration.
Cards: highlighted, list and grid groups
Use emphasis for a decision, not every surface.
Forms: sizes, textarea and file
No file selectedForms: validation, input groups and floating labels
Helper text.Select, checks, radio, switches and range
Navigation: tabs, vertical and panes
Pagination, breadcrumbs, steps and keys
- Details
- Review
- Done
Search ⌘ K · Close Esc
Modal, offcanvas, popover and tooltip
Popover details
Local context. Native disclosure.
Progress, spinners and placeholders
List groups: active, disabled and actions
Tables: basic, striped, hover, border, compact and states
| Status | Amount | |
|---|---|---|
| Northstar | Success | $4,200 |
| Atlas | Warning | $1,800 |
| Pine | Danger | $750 |
Accordion and collapse
Extended accordion
Native keyboard disclosure.
Carousel
Avatars, stacks and toast
Language select and navbar
English selected. Demo selection, not whole-site translation.Date picker and choice cards
Hover card, notifications and error/retry
Message, attachment and close control
The draft report is ready for review.
Local message sample, not sent.⌕ report.pdf · sample attachment
Navigation menu, sidebar, footer and scrollspy
Anchor navigation keeps the section visible.
Second sample section.
Extended coverage checklist
These patterns match the Felix/Lumen extended catalog, with Relay tokens. Base patterns appear in the live catalog above; extended variants follow it.
- Buttons: variants
- Buttons: outline
- Buttons: sizes
- Buttons: disabled
- Buttons: block and full width
- Buttons: shapes
- Buttons: active and toggle
- Buttons: with icons and counters
- Buttons: loading
- Button groups: basic and sizes
- Button groups: toolbar and vertical
- Button groups: checkbox and radio style
- Button groups: with dropdown
- Badges: variants
- Badges: pill, square, dot and in buttons
- Badges: positioned on a button
- Alerts: variants
- Alerts: with heading and actions
- Alerts: dismissible and with icon
- Cards: basic, header and footer
- Cards: with image, horizontal and highlighted
- Cards: list and grid groups
- Forms: text inputs
- Forms: sizes, textarea and file
- Forms: validation
- Forms: input groups
- Forms: floating labels and layout
- Select: variants
- Checks and radios
- Switches
- Range
- Nav and tabs
- Nav: vertical and tab panes
- Pagination
- Breadcrumbs
- Dropdowns
- Modals
- Tooltips
- Popovers
- Progress
- Spinners
- List groups
- Tables: basic, striped and hover
- Tables: bordered, small, dark header and states
- Accordion
- Collapse
- Toasts
- Offcanvas
- Carousel
- Avatars and stacks
- Placeholders and skeletons
- Steps and keyboard keys
- Language select
- Navbar
Complete family reference
Usage, copyable markup and keyboard notes for the same 43 core families as the other systems, using Relay tokens.
Every component, in context.
43 families · Live previews · Usage · Markup · Keyboard notes
Dropdowns
More actions
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo l-actions"><details class="l-dropdown"><summary>More actions</summary><div class="l-menu"><button @click="showToast('Copied link')">Copy link</button><button @click="showToast('Saved for later')">Save for later</button></div></details></div>Pagination
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo l-actions"><a href="/lustro">Lustro foundation ↗</a><nav class="l-pages" aria-label="Example pages"><button @click="page=Math.max(1,page-1)" :disabled="page===1">Previous</button><span>Page {{page}} / 3</span><button @click="page=Math.min(3,page+1)" :disabled="page===3">Next</button></nav></div>Alerts
Alerts occupy the page and stay until dismissed. Notifications need a clear source, severity and next action. Do not use color alone to explain the state.
Two checks are waiting for a review.
Your changes are up to date.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo"><div v-if="alertOpen" class="l-alert l-alert-warn" role="alert"><div><strong>Needs attention</strong><p>Two checks are waiting for a review.</p></div><button class="l-icon-btn" aria-label="Dismiss notification" @click="alertOpen=false">×</button></div><button v-else class="l-btn l-secondary" @click="alertOpen=true">Show notification again</button><div class="l-alert l-alert-success"><div><strong>Saved</strong><p>Your changes are up to date.</p></div></div></div>Toasts
A toast confirms a short-lived result; it must never be the only place for an error that needs fixing. Trigger several in quick succession: each one stays visible independently until dismissed or its six-second timer ends. Color, a label and a message communicate the variant together.
For production, keep independent IDs and timers so a new arrival never overwrites an earlier one. Render the stack in a portal above app content, cap its height and let it scroll on small screens. Keep errors that need user action inline too; the toast is only a short announcement. Respect reduced-motion when animating entry and exit.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo l-actions"><button class="l-btn l-primary" @click="showToast('Changes saved', 'success')">Show toast</button><button class="l-btn l-secondary" @click="showToast('Secondary action queued', 'info')">Show secondary toast</button><button class="l-btn l-secondary" @click="showToast('Review before continuing', 'warn')">Show warning</button><button class="l-btn l-secondary" @click="showToast('Could not save changes', 'error')">Show error</button></div>
<div class="l-toast-stack" aria-label="Toast notifications"><div v-for="item in toasts" :key="item.id" class="l-toast" :class="'l-toast-' + item.type" :role="item.type === 'error' ? 'alert' : 'status'"><span class="l-dot" :class="'l-dot-' + item.type" aria-hidden="true"></span><span class="l-toast-copy"><strong>{{ { success: 'Success', info: 'Info', warn: 'Warning', error: 'Error' }[item.type] }}</strong><span>{{ item.message }}</span></span><button class="l-icon-btn" :aria-label="'Dismiss ' + item.type + ' toast: ' + item.message" @click="dismissToast(item.id)">×</button></div></div>
<p>For production, keep independent IDs and timers so a new arrival never overwrites an earlier one. Render the stack in a portal above app content, cap its height and let it scroll on small screens. Keep errors that need user action inline too; the toast is only a short announcement. Respect reduced-motion when animating entry and exit.</p>
Badge
Use a short label with text, not a color alone. The grouped demo below includes four states.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo l-actions"><span class="l-badge">Draft</span><span class="l-badge l-badge-success">Ready</span><span class="l-badge l-badge-warn">Running</span><span class="l-badge l-badge-error">Blocked</span></div>
<p>Use a short label with text, not a color alone. The grouped demo below includes four states.</p>
Progress
Progress has a value and a max; use an indeterminate loader only when no useful estimate exists.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo"><label class="l-field-label" for="sample-progress">Upload progress · 68%</label><progress id="sample-progress" max="100" value="68">68%</progress></div>
<p>Progress has a value and a max; use an indeterminate loader only when no useful estimate exists.</p>
Spinners/Loaders
Name the loading region for assistive technology.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo"><span class="l-spinner" role="status" aria-label="Loading"></span></div>
<p>Name the loading region for assistive technology.</p>
Placeholders
Loading summary...
Skeletons reserve space while content loads; do not leave them in a permanent empty state.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo"><div class="l-skeleton" aria-hidden="true"></div><p>Loading summary...</p></div>
<p>Skeletons reserve space while content loads; do not leave them in a permanent empty state.</p>
Card
Use a compact summary with a clear title, metadata and the next action; keep card links and nested buttons separate.
Review changes
The latest request is waiting for your review.
Ready to ship
Checks completed. The next step is a merge.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo l-grid"><article class="l-card"><span class="l-eyebrow">PROJECT · REPO A</span><h3>Review changes</h3><p>The latest request is waiting for your review.</p><div class="l-card-foot"><span class="l-badge l-badge-warn">Needs review</span><button class="l-btn l-secondary" @click="showToast('Card action selected')">Open</button></div></article><article class="l-card"><span class="l-eyebrow">TASK · 024</span><h3>Ready to ship</h3><p>Checks completed. The next step is a merge.</p><div class="l-card-foot"><span class="l-badge l-badge-success">Ready</span><button class="l-btn l-secondary" @click="showToast('Card action selected')">Details</button></div></article></div>Accordion
Use the shared color, type and spacing tokens.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo"><div v-for="item in ['Tokens','Behavior','Accessibility']" :key="item" class="l-accordion"><button :aria-expanded="accordion===item" @click="accordion=accordion===item?'':item">{{item}} <span>{{accordion===item?'−':'+'}}</span></button><p v-if="accordion===item">{{item==='Tokens'?'Use the shared color, type and spacing tokens.':item==='Behavior'?'Keep transitions short and tied to a state change.':'Use labels, visible focus and reduced-motion fallbacks.'}}</p></div></div>List group
- 01 · Surface tokensDocs
- 02 · Component statesReady
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo"><ul class="l-list"><li><span>01 · Surface tokens</span><span class="l-badge">Docs</span></li><li><span>02 · Component states</span><span class="l-badge l-badge-success">Ready</span></li></ul></div>Tables
| Component | Status | Owner |
|---|---|---|
| Buttons | Ready | Design |
| Notifications | Review | Product |
| Graphs | Example | Motion |
Try a different search or clear your filters.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo"><div class="l-table-scroll"><table class="l-table"><thead><tr><th scope="col">Component</th><th scope="col">Status</th><th scope="col">Owner</th></tr></thead><tbody><tr><td>Buttons</td><td><span class="l-badge l-badge-success">Ready</span></td><td>Design</td></tr><tr><td>Notifications</td><td><span class="l-badge l-badge-warn">Review</span></td><td>Product</td></tr><tr><td>Graphs</td><td><span class="l-badge">Example</span></td><td>Motion</td></tr></tbody></table></div><div class="l-empty"><strong>No results</strong><p>Try a different search or clear your filters.</p><button class="l-btn l-secondary" @click="search=''">Clear filters</button></div></div>Forms
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo l-form"><label class="l-field-label" for="sample-search">Search components</label><input id="sample-search" type="search" v-model="search" placeholder="Search by name"><small>{{search?`Searching for “${search}”`:'Use descriptive labels, not only placeholders.'}}</small><label class="l-field-label" for="sample-select">Category</label><select id="sample-select"><option>All components</option><option>Actions</option><option>Feedback</option><option>Content</option></select><label class="l-field-label" for="sample-notes">Notes</label><textarea id="sample-notes" rows="2" placeholder="Optional notes"></textarea><label class="l-check"><input type="checkbox" checked> Send updates</label><fieldset><legend>Density</legend><label class="l-check"><input type="radio" name="density" checked> Comfortable</label><label class="l-check"><input type="radio" name="density"> Compact</label></fieldset><label class="l-switch"><input type="checkbox" checked><span aria-hidden="true"></span> Enable notifications</label></div>Modal
Use a blocking dialog for one short decision. Native showModal supplies focus containment and Escape; focus returns to the opener.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo"><button class="l-btn l-secondary" @click="openNative('modal',$event)">Open dialog</button></div>Tooltips
Optional help appears on hover and keyboard focus. Required instructions stay visible.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo"><span class="kit-tooltip"><button class="l-btn l-secondary" aria-describedby="kit-tip">Focus or hover for help</button><span id="kit-tip" role="tooltip">Changes apply to this workspace.</span></span></div>Popovers
The adjacent disclosure stays open for longer text.
More information
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo l-actions"><details class="l-dropdown"><summary>More information</summary><div class="l-menu l-popover">This content stays visible until you close the panel.</div></details></div>Carousel
A short series of related previews; arrows and dots remain keyboard-operable.
Overview
Start with the foundation.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo l-carousel"><button class="l-btn l-secondary" aria-label="Previous slide" @click="slide=(slide+2)%3">‹</button><article class="l-card" aria-live="polite"><span class="l-eyebrow">{{slide+1}} / 3</span><h3>{{['Overview','Details','Next steps'][slide]}}</h3><p>{{['Start with the foundation.','Try one interaction.','Check the result in context.'][slide]}}</p></article><button class="l-btn l-secondary" aria-label="Next slide" @click="slide=(slide+1)%3">›</button><div class="l-carousel-dots"><button v-for="n in 3" :key="n" :aria-label="`Go to slide ${n}`" :aria-current="slide===n?'true':undefined" @click="slide=n-1">{{n}}</button></div></div>Collapse
For one block of secondary detail, prefer native disclosure. Accordion above coordinates several blocks.
Show delivery details
Open with Enter or Space without losing context.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo"><details class="l-collapse"><summary>Show delivery details</summary><p>Open with Enter or Space without losing context.</p></details></div>Offcanvas
Drawer / sheet / offcanvas: contextual details beside the current view. Native modal behavior includes Escape and focus containment.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo"><button class="l-btn l-secondary" @click="openNative('sheet',$event)">Open side panel</button></div>Scrollspy
Select a section to bring it into view; the active state is also exposed as text.
Section inicio selected.
This is a section-selection preview, not automatic scroll tracking. Production scrollspy should use IntersectionObserver to update aria-current.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo"><nav class="l-spy-nav" aria-label="Example sections"><button v-for="item in ['Home','Details','Summary']" :key="item" :aria-current="menuSection===item?'location':undefined" @click="menuSection=item">{{item}}</button></nav><div class="l-card" aria-live="polite"><strong>{{menuSection}}</strong><p>Section {{menuSection.toLowerCase()}} selected.</p></div></div>
<p>This is a section-selection preview, not automatic scroll tracking. Production scrollspy should use IntersectionObserver to update aria-current.</p>
Notifications
A persistent inbox for events that need attention, distinct from short-lived toasts.
- Unread · Dana requested a project review4 minutes ago
- Unread · Budget approved1 hour ago
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo"><div class="l-actions"><strong>Notifications</strong><span class="l-badge">{{notificationsRead?0:2}} unread</span><button class="l-btn l-secondary" @click="notificationsRead=!notificationsRead">{{notificationsRead?'Reset unread':'Mark all read'}}</button></div><ul class="l-list"><li><span>{{notificationsRead?'Read':'Unread'}} · Dana requested a project review</span><small>4 minutes ago</small></li><li><span>{{notificationsRead?'Read':'Unread'}} · Budget approved</span><small>1 hour ago</small></li></ul></div>Attachment and file upload
Native file selection, filename and size. This preview never uploads a file.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo l-form"><label>Attach a PDF or text file<input type="file" accept=".pdf,.txt" @change="selectFile"></label><span class="l-badge">{{pickedFile}}</span><small>Local selection only. Production: validate type and size on the server.</small></div>Date picker
Use locale-aware native date input for a single date; see Calendar for richer scheduling.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo l-form"><label>Due date<input type="date" value="2026-10-15"></label><label>Date and time<input type="datetime-local"></label></div>Choice card
A larger radio target for options with descriptions. Arrow keys move between native radios.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo l-grid"><label class="l-card"><input type="radio" name="delivery" checked> <strong>Weekly summary</strong><p>A weekly overview of your workspace.</p></label><label class="l-card"><input type="radio" name="delivery"> <strong>Daily summary</strong><p>Updates at the end of each day.</p></label></div>Stepper
Show a finite workflow. Production steps must not bypass required validation.
- 1. Details
- 2. Review
- 3. Done
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo"><ol class="l-actions"><li v-for="(s,i) in ['Details','Review','Done']" :aria-current="step===i+1?'step':undefined" :class="{'l-badge':step===i+1}">{{i+1}}. {{s}}</li></ol><div class="l-actions"><button class="l-btn l-secondary" :disabled="step===1" @click="step--">Back</button><button class="l-btn l-secondary" :disabled="step===3" @click="step++">Continue</button></div></div>Hover card
Optional rich context. Native disclosure also makes the information available without a mouse.
Dana Lee · Coach details
Lead coach · 12 classes this month
AvailableKeyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo"><details><summary>Dana Lee · Coach details</summary><article class="l-card"><strong>Dana Lee</strong><p>Lead coach · 12 classes this month</p><span class="l-badge l-badge-success">Available</span></article></details></div>Message
Keep sender and time with the content. Incoming, outgoing and system states have different roles.
Ready for review.
I will check the task list.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo"><article class="kit-bubble"><strong>Dana · 10:42</strong><p>Ready for review.</p></article><article class="kit-bubble outgoing"><strong>You · 10:43</strong><p>I will check the task list.</p></article><small>System · Review requested</small></div>Range slider
For approximate numeric settings; always show the current value. Arrow keys adjust the native slider.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo l-form"><label>Volume<input type="range" min="0" max="100" v-model="volume"><output>{{volume}}%</output></label></div>Input group
Combine a field with units or a related action while retaining a visible label.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo l-form"><label>Budget (USD)<div class="l-actions"><span>$</span><input type="number" min="0" value="1200"><button class="l-btn l-secondary" @click="showToast('Demo budget updated','success')">Apply</button></div></label></div>Validation and field states
Validate after interaction, preserve entered values, and connect errors to the field.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo l-form"><form @submit.prevent="formSent=true" novalidate><label>Project name<input v-model="formName" required :aria-invalid="formSent&&!formName.trim()" aria-describedby="kit-validation"></label><small id="kit-validation" role="status">{{formSent?(formName.trim()?'Demo saved':'Enter a project name'):'Use a short unique name'}}</small><label>Read-only reference<input value="PRJ-042" readonly></label><label>Disabled field<input value="Managed by workspace" disabled></label><button class="l-btn l-secondary" type="submit">Validate demo</button></form></div>Error and retry
Explain the failure, keep context, and offer recovery. This retry is local simulation.
Your previous changes are safe. Try again.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo" :role="failed?'alert':'status'"><strong>{{failed?'Could not load projects':'Demo projects loaded'}}</strong><p>{{failed?'Your previous changes are safe. Try again.':'Connection restored (simulation).'}}</p><button class="l-btn l-secondary" @click="failed=!failed">{{failed?'Try again':'Reset error'}}</button></div>Empty state
Explain why there is no content and offer one useful next action.
Create a project to start tracking work.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo l-empty"><strong>No projects yet</strong><p>Create a project to start tracking work.</p><button class="l-btn l-secondary" @click="showToast('Demo project created','success')">Create demo project</button></div>Select and suggestions
Use select for a fixed set. Datalist is browser-native suggestions, not a full custom combobox.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo l-form"><label>Priority<select><option>Normal</option><option>High</option><option>Low</option></select></label><label>Owner suggestions<input list="kit-owners" placeholder="Type a name"></label><datalist id="kit-owners"><option value="Dana Lee"/><option value="Marcos Silva"/></datalist></div>Checkbox, radio and switch
Choose many values with checkboxes, one with radios, and immediate on/off settings with a switch.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo l-form"><label><input type="checkbox" checked> Email updates</label><label><input type="checkbox" disabled> Managed option (disabled)</label><fieldset><legend>Plan</legend><label><input type="radio" name="kit-plan" checked> Personal</label><label><input type="radio" name="kit-plan"> Team</label></fieldset><label class="l-switch"><input type="checkbox" role="switch" checked><span aria-hidden="true"></span> Automatic reminders</label></div>Avatar
Initials fallback and text status. Noninteractive avatars do not need a tab stop.
Keyboard: Tab to interactive controls. Enter activates buttons and links; Space toggles native choices. Disabled controls cannot be activated. Use Escape to close modal surfaces.
Copy implementation markup
<div class="l-demo l-actions"><span class="l-avatar" role="img" aria-label="Dana Lee">DL</span><span class="l-avatar" role="img" aria-label="Marcos Silva">MS</span><span>Available</span></div>