src/App.tsx
Ready
localhost:5173
Headless UI 2ReadyTSXLn 1, Col 1
Online Headless UI playground with React, TypeScript, accessible unstyled components, editable project files, and instant live preview.
Online Headless UI playground with React, TypeScript, accessible unstyled components, editable project files, and instant live preview.
Runtime: v2.2.10
Press Enter to evaluate, ↑ and ↓ to browse command history, and Ctrl/⌘ + L to clear the console.
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App'
import './styles.css'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)
import { useState } from 'react'
import {
Dialog,
DialogBackdrop,
DialogPanel,
DialogTitle,
Listbox,
ListboxButton,
ListboxOption,
ListboxOptions,
Menu,
MenuButton,
MenuItem,
MenuItems,
Switch,
Tab,
TabGroup,
TabList,
TabPanel,
TabPanels,
} from '@headlessui/react'
import { ActivityRow } from './components/ActivityRow'
import { activity, teams } from './data'
export default function App() {
const [team, setTeam] = useState(teams[0])
const [updates, setUpdates] = useState(true)
const [dialogOpen, setDialogOpen] = useState(false)
const [saved, setSaved] = useState(false)
const savePreferences = () => {
setSaved(true)
setDialogOpen(false)
window.setTimeout(() => setSaved(false), 1800)
}
return (
<main className="app-shell">
<section className="workspace" aria-labelledby="page-title">
<header className="topbar">
<div className="brand-mark" aria-hidden="true"><span /></div>
<div className="heading-copy">
<p className="eyebrow">HEADLESS UI · REACT 19</p>
<h1 id="page-title">Workspace pulse</h1>
<p>Accessible interactions, styled for a focused product team.</p>
</div>
<Menu as="div" className="profile-menu">
<MenuButton className="avatar-button" aria-label="Open account menu">DM</MenuButton>
<MenuItems anchor="bottom end" className="menu-items">
<div className="menu-heading">Danielle · Admin</div>
<MenuItem><button type="button">Account settings</button></MenuItem>
<MenuItem><button type="button">Keyboard shortcuts</button></MenuItem>
<div className="menu-separator" />
<MenuItem><button type="button">Sign out</button></MenuItem>
</MenuItems>
</Menu>
</header>
<div className="control-row">
<div>
<span className="control-label">Active team</span>
<Listbox value={team} onChange={setTeam}>
<ListboxButton className="listbox-button">
<span className="team-avatar" style={{ background: team.accent }}>{team.initials}</span>
<span>{team.name}</span>
<span className="chevron" aria-hidden="true">⌄</span>
</ListboxButton>
<ListboxOptions anchor="bottom start" className="listbox-options">
{teams.map((option) => (
<ListboxOption key={option.id} value={option} className="listbox-option">
<span className="team-avatar" style={{ background: option.accent }}>{option.initials}</span>
<span className="option-copy">
<strong>{option.name}</strong>
<small>{option.members} members</small>
</span>
<span className="option-check" aria-hidden="true">✓</span>
</ListboxOption>
))}
</ListboxOptions>
</Listbox>
</div>
<span className="status-pill"><i /> Systems ready</span>
</div>
<TabGroup className="dashboard">
<TabList className="tab-list" aria-label="Workspace sections">
<Tab className="tab">Overview</Tab>
<Tab className="tab">Activity</Tab>
</TabList>
<TabPanels>
<TabPanel className="tab-panel">
<div className="metric-grid">
<article className="metric-card"><span>Accessibility</span><strong>98</strong><small>+4 this release</small></article>
<article className="metric-card"><span>Components</span><strong>24</strong><small>Across 8 flows</small></article>
<article className="metric-card"><span>Open reviews</span><strong>3</strong><small>2 due today</small></article>
</div>
<div className="settings-card">
<div>
<h2>Release updates</h2>
<p>Get notified when accessibility reviews are complete.</p>
</div>
<Switch checked={updates} onChange={setUpdates} className="switch" aria-label="Release updates">
<span className="switch-thumb" />
</Switch>
</div>
</TabPanel>
<TabPanel className="tab-panel">
<div className="activity-list">
{activity.map((item) => <ActivityRow key={item.title} {...item} />)}
</div>
</TabPanel>
</TabPanels>
</TabGroup>
<div className="footer-actions">
<p aria-live="polite">{saved ? 'Preferences saved.' : ''}</p>
<button className="primary-button" type="button" onClick={() => setDialogOpen(true)}>
Review preferences
</button>
</div>
<Dialog open={dialogOpen} onClose={setDialogOpen} className="dialog-root">
<DialogBackdrop className="dialog-backdrop" />
<div className="dialog-container">
<DialogPanel className="dialog-panel">
<div className="dialog-icon" aria-hidden="true">✦</div>
<DialogTitle>Release preferences</DialogTitle>
<p className="dialog-description">
Choose whether {team.name} should receive accessibility review updates.
</p>
<label className="dialog-setting">
<span><strong>Release updates</strong><small>{updates ? 'Currently enabled' : 'Currently disabled'}</small></span>
<Switch checked={updates} onChange={setUpdates} className="switch" aria-label="Dialog release updates">
<span className="switch-thumb" />
</Switch>
</label>
<div className="dialog-actions">
<button className="secondary-button" type="button" onClick={() => setDialogOpen(false)}>Cancel</button>
<button className="primary-button" type="button" onClick={savePreferences}>Save preferences</button>
</div>
</DialogPanel>
</div>
</Dialog>
</section>
</main>
)
}