← Accessible Components results

Accessible Components

GPT 5.6 Luna Pro · typescript

Initial view of Accessible Components

Reviews & generation details

Reviews

No human review yet. Unreviewed does not mean unsuccessful.

Generation context

Imported historical artifact. Its exact original instructions and lineage are not known.

Generation prompt · exact text unavailable

See the current prompt for reference. It may differ from the instructions used to generate this artifact.

Inspect original source 21,262 bytes · SHA-256 ee4e8bdf9f19
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Aster UI · Accessible component showcase</title>
<style>
/* ── css: design ── */
:root{--ink:#18252b;--muted:#6b7a7f;--line:#dbe4e3;--paper:#f7faf8;--card:#fff;--teal:#087f78;--coral:#e87861;--pale:#e8f4f1;--shadow:0 16px 45px #1737350d;--radius:16px}*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:var(--paper);color:var(--ink);font:15px/1.6 Inter,ui-sans-serif,system-ui,-apple-system,sans-serif}.skip-link{position:fixed;top:10px;left:16px;transform:translateY(-160%);background:var(--ink);color:white;padding:8px 14px;border-radius:6px;z-index:20}.skip-link:focus{transform:none}.shell{display:flex;max-width:1440px;margin:auto}.sidebar{width:235px;position:sticky;top:0;height:100vh;padding:38px 26px;border-right:1px solid var(--line);display:flex;flex-direction:column}.brand{font-weight:800;font-size:19px;letter-spacing:-.04em;display:flex;gap:9px;align-items:center}.mark,.modal-icon{color:var(--coral)}.eyebrow,.tag,.number{font-size:11px;letter-spacing:.14em;text-transform:uppercase;font-weight:750}.eyebrow{color:var(--teal);margin:50px 0 15px}.sidebar nav{display:grid;gap:12px}.sidebar a{color:var(--muted);text-decoration:none;font-size:13px}.sidebar a:hover{color:var(--teal)}.side-note{margin-top:auto;color:var(--muted);font-size:11px;display:flex;gap:8px;align-items:center}.dot{width:7px;height:7px;background:var(--coral);border-radius:50%}main{flex:1;min-width:0;padding:0 7%;}.hero{padding:76px 0 64px;max-width:720px}.hero .eyebrow{margin:0 0 16px}.hero h1{font-size:clamp(40px,5vw,67px);line-height:1.04;letter-spacing:-.065em;margin:0 0 22px}.hero em{color:var(--teal);font-style:normal}.lede{font-size:18px;color:var(--muted);max-width:545px}.hero-meta{display:flex;gap:24px;color:var(--muted);font-size:12px;margin-top:30px}.grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:22px}.card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:28px;box-shadow:var(--shadow);min-width:0}.wide{grid-column:span 2}.card-head{display:flex;justify-content:space-between;align-items:flex-start}.number{color:var(--coral);display:block;margin-bottom:8px}.card h2{font-size:23px;letter-spacing:-.04em;margin:0}.desc{color:var(--muted);margin:8px 0 25px}.demo{min-height:115px;background:#f5f8f7;border:1px solid #e9efed;border-radius:11px;padding:22px;display:flex;align-items:center;gap:16px}.button{border:0;border-radius:7px;padding:11px 16px;font-weight:700;font-size:13px;cursor:pointer}.primary{background:var(--teal);color:white}.secondary{background:white;color:var(--ink);border:1px solid var(--line)}.ghost{background:transparent;color:var(--muted)}button,input{font:inherit}.hint{font-size:11px;color:var(--muted)}.menu-wrap{position:relative}.chevron{margin-left:18px}.menu{position:absolute;top:calc(100% + 7px);left:0;background:white;border:1px solid var(--line);border-radius:8px;box-shadow:var(--shadow);padding:5px;z-index:4;width:170px}.menu button{display:block;width:100%;border:0;text-align:left;background:white;padding:8px 10px;border-radius:5px;cursor:pointer;font-size:13px}.menu button:hover,.menu button:focus{background:var(--pale);color:var(--teal)}.tablist{display:flex;border-bottom:1px solid var(--line);background:#fbfcfc}.tabs-demo{border:1px solid var(--line);border-radius:11px;overflow:hidden}.tablist button{border:0;border-bottom:2px solid transparent;background:transparent;padding:13px 17px;color:var(--muted);cursor:pointer;font-weight:650;font-size:13px}.tablist button[aria-selected=true]{color:var(--teal);border-bottom-color:var(--teal);background:white}.tabpanel{padding:22px;min-height:100px}.tabpanel strong{font-size:16px}.tabpanel p{color:var(--muted);margin:5px 0 0}.tabs-demo+.hint{display:block;margin-top:12px}.combo-demo{display:block}.combo-demo label{display:block;font-size:12px;font-weight:700;margin-bottom:7px}.combo-wrap{position:relative}.combo-wrap input{width:100%;padding:11px 13px;border:1px solid #bdccca;border-radius:7px;background:white}.listbox{position:absolute;top:calc(100% + 5px);left:0;right:0;margin:0;padding:5px;list-style:none;border:1px solid var(--line);background:white;border-radius:8px;box-shadow:var(--shadow);z-index:5;max-height:180px;overflow:auto}.listbox li{padding:8px 10px;border-radius:5px;cursor:pointer}.listbox li[aria-selected=true]{background:var(--pale);color:var(--teal);font-weight:650}.status{font-size:11px;color:var(--muted);margin-top:9px}.accordion h3{margin:0;border-top:1px solid var(--line)}.accordion h3:last-of-type{border-bottom:1px solid var(--line)}.accordion-trigger{width:100%;text-align:left;padding:15px 4px;border:0;background:transparent;font-weight:700;color:var(--ink);cursor:pointer;display:flex;justify-content:space-between}.accordion-trigger span{color:var(--teal);font-size:20px;font-weight:400}.accordion-panel{overflow:hidden;max-height:120px;transition:max-height .25s ease}.accordion-panel p{margin:0 4px 15px;color:var(--muted);font-size:13px}footer{display:flex;justify-content:space-between;color:var(--muted);font-size:10px;letter-spacing:.12em;padding:45px 0}.modal-backdrop{position:fixed;inset:0;background:#122f2d99;display:grid;place-items:center;padding:20px;z-index:10}.modal{position:relative;background:white;border-radius:18px;padding:38px;max-width:430px;box-shadow:0 25px 70px #102f2c33}.modal-icon{font-size:25px}.modal h2{font-size:28px;letter-spacing:-.05em;margin:12px 0 8px}.modal p{color:var(--muted)}.modal-actions{display:flex;gap:8px;margin-top:25px}.icon-button{border:0;background:transparent;cursor:pointer}.close{position:absolute;top:14px;right:16px;font-size:25px;color:var(--muted)}:focus-visible{outline:3px solid var(--coral);outline-offset:3px}button:focus-visible,input:focus-visible,a:focus-visible{outline:3px solid var(--coral);outline-offset:3px}@media(max-width:800px){.sidebar{display:none}main{padding:0 20px}.grid{grid-template-columns:1fr}.wide{grid-column:auto}.hero{padding-top:45px}}@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition-duration:0!important}}
</style>
</head>
<body>
<a class="skip-link" href="#main">Skip to main content</a><div class="shell"><aside class="sidebar" aria-label="Component navigation"><div class="brand"><span class="mark">✦</span><span>Aster<span class="muted"> UI</span></span></div><p class="eyebrow">Accessible primitives</p><nav><a href="#dialog">Modal dialog</a><a href="#menu">Dropdown menu</a><a href="#tabs">Tabs</a><a href="#combobox">Combobox</a><a href="#accordion">Accordion</a></nav><div class="side-note"><span class="dot"></span>Keyboard-first by design</div></aside><main id="main"><header class="hero"><p class="eyebrow">Component showcase · v1.4</p><h1>Thoughtful interfaces,<br><em>made accessible.</em></h1><p class="lede">A small collection of resilient, keyboard-friendly patterns for products that welcome everyone.</p><div class="hero-meta"><span>⌘ No dependencies</span><span>◌ WAI-ARIA aligned</span></div></header><div class="grid"><section class="card" id="dialog" aria-labelledby="dialog-title"><div class="card-head"><div><span class="number">01</span><h2 id="dialog-title">Modal dialog</h2></div><span class="tag">FOCUS TRAP</span></div><p class="desc">A focused moment for confirmation, detail, or a considered decision.</p><div class="demo dialog-demo"><button class="button primary" id="open-dialog">Open dialog <span>↗</span></button><span class="hint">Tab to cycle · Esc to close</span></div></section><section class="card" id="menu" aria-labelledby="menu-title"><div class="card-head"><div><span class="number">02</span><h2 id="menu-title">Dropdown menu</h2></div><span class="tag">ROVING FOCUS</span></div><p class="desc">Compact actions with predictable arrow-key navigation.</p><div class="demo menu-demo"><div class="menu-wrap"><button class="button secondary" id="menu-button" aria-haspopup="menu" aria-expanded="false" aria-controls="action-menu">Actions <span class="chevron">⌄</span></button><div id="action-menu" class="menu" role="menu" hidden=""><button role="menuitem" tabindex="-1" data-action="Copied link">Copy link</button><button role="menuitem" tabindex="-1" data-action="Export started">Export</button><button role="menuitem" tabindex="-1" data-action="Settings opened">Settings</button></div></div><span class="hint" id="menu-status" aria-live="polite">Choose an action</span></div></section><section class="card wide" id="tabs" aria-labelledby="tabs-title"><div class="card-head"><div><span class="number">03</span><h2 id="tabs-title">Tabs</h2></div><span class="tag">AUTOMATIC</span></div><p class="desc">Switch between related views without losing your place.</p><div class="tabs-demo"><div class="tablist" role="tablist" aria-label="Project views"><button role="tab" id="tab-overview" aria-controls="panel-overview" aria-selected="true" tabindex="0">Overview</button><button role="tab" id="tab-activity" aria-controls="panel-activity" aria-selected="false" tabindex="-1">Activity</button><button role="tab" id="tab-accessibility" aria-controls="panel-accessibility" aria-selected="false" tabindex="-1">Accessibility</button></div><div id="panel-overview" class="tabpanel" role="tabpanel" tabindex="0" aria-labelledby="tab-overview"><strong>One calm surface.</strong><p>Keep related choices together, with a clear hierarchy and room to breathe.</p></div><div id="panel-activity" class="tabpanel" role="tabpanel" tabindex="0" aria-labelledby="tab-activity" hidden=""><strong>Everything in motion.</strong><p>See changes as they happen, with context attached to every event.</p></div><div id="panel-accessibility" class="tabpanel" role="tabpanel" tabindex="0" aria-labelledby="tab-accessibility" hidden=""><strong>Built for every user.</strong><p>Semantic structure, visible focus, and thoughtful defaults are part of the interface.</p></div></div><span class="hint">← → switch tabs · Tab enter panel</span></section><section class="card" id="combobox" aria-labelledby="combo-title"><div class="card-head"><div><span class="number">04</span><h2 id="combo-title">Combobox</h2></div><span class="tag">LIVE FILTER</span></div><p class="desc">Find a destination quickly with an input that stays in context.</p><div class="demo combo-demo"><label for="combo-input">Choose a city</label><div class="combo-wrap"><input id="combo-input" role="combobox" aria-expanded="false" aria-controls="city-list" aria-autocomplete="list" aria-activedescendant="" placeholder="Search cities…" autocomplete="off"><ul id="city-list" class="listbox" role="listbox" hidden=""></ul></div><div id="combo-status" class="status" aria-live="polite">Start typing to see options</div></div></section><section class="card" id="accordion" aria-labelledby="accordion-title"><div class="card-head"><div><span class="number">05</span><h2 id="accordion-title">Accordion</h2></div><span class="tag">DISCLOSURE</span></div><p class="desc">Progressive detail that lets people choose their own depth.</p><div class="accordion"><h3><button class="accordion-trigger" aria-expanded="true" aria-controls="acc-one">What makes this accessible? <span>+</span></button></h3><div id="acc-one" class="accordion-panel"><p>Every pattern has a semantic role, an intentional focus path, and a keyboard contract you can learn.</p></div><h3><button class="accordion-trigger" aria-expanded="false" aria-controls="acc-two">Can multiple items stay open? <span>+</span></button></h3><div id="acc-two" class="accordion-panel" hidden=""><p>Yes. Disclosure groups are independent, so opening one never steals context from another.</p></div><h3><button class="accordion-trigger" aria-expanded="false" aria-controls="acc-three">Does it respect reduced motion? <span>+</span></button></h3><div id="acc-three" class="accordion-panel" hidden=""><p>All transitions become instant when a user prefers reduced motion.</p></div></div><span class="hint">Enter or Space to toggle</span></section></div><footer><span>ASTER UI</span><span>Designed with clarity · Built with care</span></footer></main></div><div class="modal-backdrop" id="backdrop" hidden=""><section class="modal" id="dialog-panel" role="dialog" aria-modal="true" aria-labelledby="modal-heading" aria-describedby="modal-copy"><button class="icon-button close" id="close-dialog" aria-label="Close dialog">×</button><span class="modal-icon">✦</span><h2 id="modal-heading">A little more context</h2><p id="modal-copy">Good interfaces make the next step feel obvious. This dialog keeps your attention here until you choose to leave.</p><div class="modal-actions"><button class="button primary" id="confirm-dialog">Continue</button><button class="button ghost" id="cancel-dialog">Not now</button></div></section></div>
<script>
(function () {
'use strict';
var __mods = {};
var __cache = {};
var __map = {};
function __require(id) {
  if (__cache[id]) return __cache[id].exports;
  var module = __cache[id] = { exports: {} };
  var factory = __mods[id];
  if (!factory) throw new Error("Module not found: " + id);
  factory(module.exports, function (spec) {
    var target = (__map[id] && __map[id][spec]) || spec;
    return __require(target);
  }, module);
  return module.exports;
}

// ── module: src/main.ts ──
__mods["src/main.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// Aster UI accessible component showcase
const $ = (s) => document.querySelector(s);
const $$ = (s) => Array.from(document.querySelectorAll(s));
// Modal dialog: inert page + reusable focus trap
const backdrop = $('#backdrop'), dialog = $('#dialog-panel'), openDialog = $('#open-dialog');
let restore = openDialog;
function focusables(root) { return $$('button,[href],input,select,textarea,[tabindex]:not([tabindex="-1"])').filter(x => root.contains(x) && !x.hasAttribute('disabled')); }
function closeModal() { backdrop.hidden = true; document.body.removeAttribute('data-modal'); $('.shell').inert = false; restore.focus(); }
function openModal() { restore = document.activeElement instanceof HTMLElement ? document.activeElement : openDialog; backdrop.hidden = false; document.body.setAttribute('data-modal', 'true'); $('.shell').inert = true; focusables(dialog)[0]?.focus(); }
openDialog.addEventListener('click', openModal);
$('#close-dialog').addEventListener('click', closeModal);
$('#cancel-dialog').addEventListener('click', closeModal);
$('#confirm-dialog').addEventListener('click', () => { closeModal(); openDialog.textContent = 'Continued ✓'; });
backdrop.addEventListener('click', e => { if (e.target === backdrop)
    closeModal(); });
dialog.addEventListener('keydown', e => { if (e.key === 'Escape') {
    e.preventDefault();
    closeModal();
} if (e.key === 'Tab') {
    const f = focusables(dialog), i = f.indexOf(document.activeElement);
    if (!f.length)
        return;
    e.preventDefault();
    f[(i + (e.shiftKey ? -1 : 1) + f.length) % f.length].focus();
} });
// Dropdown menu with menuitem navigation
const menuButton = $('#menu-button'), menu = $('#action-menu'), menuItems = $$('[role="menuitem"]');
let menuIndex = 0;
function closeMenu(returnFocus = true) { menu.hidden = true; menuButton.setAttribute('aria-expanded', 'false'); menuItems.forEach(x => x.tabIndex = -1); if (returnFocus)
    menuButton.focus(); }
function openMenu(first = false) { menu.hidden = false; menuButton.setAttribute('aria-expanded', 'true'); menuIndex = first ? 0 : menuIndex; menuItems.forEach((x, i) => x.tabIndex = i === menuIndex ? 0 : -1); menuItems[menuIndex].focus(); }
menuButton.addEventListener('click', () => menu.hidden ? openMenu() : closeMenu());
menuButton.addEventListener('keydown', e => { if (['Enter', ' '].includes(e.key)) {
    e.preventDefault();
    openMenu();
} if (e.key === 'ArrowDown') {
    e.preventDefault();
    openMenu(true);
} });
menu.addEventListener('keydown', e => { if (e.key === 'Escape') {
    e.preventDefault();
    closeMenu();
}
else if (e.key === 'Tab') {
    closeMenu(false);
}
else if (['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(e.key)) {
    e.preventDefault();
    menuIndex = e.key === 'Home' ? 0 : e.key === 'End' ? menuItems.length - 1 : (menuIndex + (e.key === 'ArrowUp' ? -1 : 1) + menuItems.length) % menuItems.length;
    menuItems.forEach((x, i) => x.tabIndex = i === menuIndex ? 0 : -1);
    menuItems[menuIndex].focus();
} });
menuItems.forEach(item => item.addEventListener('click', () => { $('#menu-status').textContent = item.dataset.action || 'Action complete'; closeMenu(); }));
// Tabs: one tab stop, automatic arrow activation
const tabs = $$('[role="tab"]');
function activateTab(i) { tabs.forEach((t, n) => { const on = n === i; t.setAttribute('aria-selected', String(on)); t.tabIndex = on ? 0 : -1; const p = $('#' + t.getAttribute('aria-controls')); p.hidden = !on; }); }
tabs.forEach((t, i) => t.addEventListener('click', () => { activateTab(i); t.focus(); }));
tabs.forEach((t, i) => t.addEventListener('keydown', e => { if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') {
    e.preventDefault();
    activateTab((i + (e.key === 'ArrowRight' ? 1 : -1) + tabs.length) % tabs.length);
    tabs[(i + (e.key === 'ArrowRight' ? 1 : -1) + tabs.length) % tabs.length].focus();
} if (e.key === 'Home' || e.key === 'End') {
    e.preventDefault();
    const n = e.key === 'Home' ? 0 : tabs.length - 1;
    activateTab(n);
    tabs[n].focus();
} }));
// Combobox: aria-activedescendant listbox pattern
const input = $('#combo-input'), list = $('#city-list'), status = $('#combo-status');
const cities = ['Amsterdam', 'Austin', 'Barcelona', 'Berlin', 'Boston', 'Chicago', 'Copenhagen', 'Dublin', 'Helsinki', 'Lisbon', 'London', 'Melbourne', 'Montreal', 'New York', 'Oslo', 'Paris', 'Portland', 'Singapore', 'Stockholm', 'Tokyo'];
let filtered = cities, active = -1, previous = '';
function renderCities() { list.innerHTML = ''; filtered.forEach((city, i) => { const li = document.createElement('li'); li.id = 'city-option-' + i; li.role = 'option'; li.textContent = city; li.setAttribute('aria-selected', String(i === active)); li.addEventListener('mousedown', e => e.preventDefault()); li.addEventListener('click', () => selectCity(i)); list.append(li); }); input.setAttribute('aria-activedescendant', active >= 0 ? 'city-option-' + active : ''); }
function announceCount() { status.textContent = filtered.length ? (filtered.length + ' options available') : 'No options available'; }
function showList() { list.hidden = false; input.setAttribute('aria-expanded', 'true'); }
function hideList() { list.hidden = true; input.setAttribute('aria-expanded', 'false'); input.setAttribute('aria-activedescendant', ''); active = -1; }
function selectCity(i) { input.value = filtered[i]; status.textContent = filtered[i] + ' selected'; hideList(); }
input.addEventListener('focus', () => { previous = input.value; });
input.addEventListener('input', () => { filtered = cities.filter(c => c.toLowerCase().includes(input.value.toLowerCase())); active = filtered.length ? 0 : -1; renderCities(); showList(); announceCount(); });
input.addEventListener('keydown', e => { if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
    e.preventDefault();
    if (list.hidden)
        showList();
    if (filtered.length) {
        active = (active + (e.key === 'ArrowDown' ? 1 : -1) + filtered.length) % filtered.length;
        renderCities();
        $('#city-option-' + active)?.scrollIntoView({ block: 'nearest' });
    }
}
else if (e.key === 'Enter' && active >= 0 && !list.hidden) {
    e.preventDefault();
    selectCity(active);
}
else if (e.key === 'Escape') {
    if (!list.hidden) {
        input.value = previous;
        filtered = cities.filter(c => c.toLowerCase().includes(previous.toLowerCase()));
        renderCities();
        hideList();
        status.textContent = 'Search cleared';
    }
} });
input.addEventListener('blur', () => setTimeout(hideList, 120));
// Accordion: animate height while preserving hidden semantics
$$('.accordion-trigger').forEach(btn => btn.addEventListener('click', () => { const panel = $('#' + btn.getAttribute('aria-controls')); const open = btn.getAttribute('aria-expanded') === 'true'; btn.setAttribute('aria-expanded', String(!open)); if (open) {
    panel.style.maxHeight = panel.scrollHeight + 'px';
    requestAnimationFrame(() => panel.style.maxHeight = '0px');
    setTimeout(() => { if (btn.getAttribute('aria-expanded') === 'false') {
        panel.hidden = true;
        panel.style.maxHeight = '';
    } }, 260);
}
else {
    panel.hidden = false;
    panel.style.maxHeight = '0px';
    requestAnimationFrame(() => panel.style.maxHeight = panel.scrollHeight + 'px');
    setTimeout(() => panel.style.maxHeight = '', 260);
} }));
};

// ── entry ──
__require("src/main.ts");
})();
</script>
</body>
</html>
<!-- agent-meta {"model":"openai/gpt-5.6-luna-pro","provider":"openrouter","persona":"typescript","sessionId":"0ce87987-fcc7-447a-9aa5-5e8ef8c7a56c","tokensIn":866894,"tokensOut":19360,"tokensTotal":886254,"turns":21,"toolCalls":40,"failedToolCalls":5,"timestamp":"2026-07-31T02:55:22.118Z"} -->