Spark component catalog
Live variants and states. 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
↗ 12.8% vs. last month
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
| Company | Status | Amount |
|---|---|---|
| Northstar | Completed | $4,200 |
| Atlas | Pending | $1,800 |
| Pine | Failed | $750 |
| Field | Completed | $3,100 |
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
Pixel histogram
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 or purchases anything.
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 Spark 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 Spark 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>