Basic Input
A labelled text input with helper text wired to it via aria-describedby.
6 फ्रेमवर्कशुरुआती
A listbox with labelled groups - the ARIA analogue of `<optgroup>`, with a flat keyboard.
<!--
A grouped listbox - the ARIA analogue of <optgroup>. Two rules make the
grouping real rather than decorative:
1. The heading is NOT an option. It sits in a role="group" that carries
aria-labelledby pointing at it, so a screen reader announces "Americas,
group" as it crosses the boundary and never lets the user select the word
"Americas".
2. The keyboard treats the list as flat. Arrowing runs straight through the
headings - group boundaries are an announcement, not a stop.
If your rows are plain text and single-select, note that <select><optgroup>
does all of this natively and cannot regress. This markup earns its keep only
once the rows need more than text.
-->
<div class="gsel">
<span class="gsel__label" id="gsel-label">Region</span>
<button class="gsel__trigger" id="gsel-trigger" type="button" aria-haspopup="listbox" aria-expanded="false" aria-controls="gsel-listbox" aria-labelledby="gsel-label gsel-trigger">
<span class="gsel__value">Germany</span>
<svg class="gsel__chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m6 9 6 6 6-6" /></svg>
</button>
<div class="gsel__listbox" id="gsel-listbox" role="listbox" aria-labelledby="gsel-label" tabindex="-1" hidden>
<div role="group" aria-labelledby="gsel-group-americas">
<div class="gsel__heading" id="gsel-group-americas" role="presentation">Americas</div>
<div class="gsel__option" id="gsel-opt-us" role="option" data-value="us" aria-selected="false"><span class="gsel__marker" aria-hidden="true"></span><span class="gsel__option-label">United States</span></div>
<div class="gsel__option" id="gsel-opt-ca" role="option" data-value="ca" aria-selected="false"><span class="gsel__marker" aria-hidden="true"></span><span class="gsel__option-label">Canada</span></div>
<div class="gsel__option" id="gsel-opt-br" role="option" data-value="br" aria-selected="false"><span class="gsel__marker" aria-hidden="true"></span><span class="gsel__option-label">Brazil</span></div>
</div>
<div role="group" aria-labelledby="gsel-group-emea">
<div class="gsel__heading" id="gsel-group-emea" role="presentation">EMEA</div>
<div class="gsel__option" id="gsel-opt-de" role="option" data-value="de" aria-selected="true"><span class="gsel__marker" aria-hidden="true"></span><span class="gsel__option-label">Germany</span></div>
<div class="gsel__option" id="gsel-opt-fr" role="option" data-value="fr" aria-selected="false"><span class="gsel__marker" aria-hidden="true"></span><span class="gsel__option-label">France</span></div>
<div class="gsel__option" id="gsel-opt-uk" role="option" data-value="uk" aria-selected="false"><span class="gsel__marker" aria-hidden="true"></span><span class="gsel__option-label">United Kingdom</span></div>
</div>
<div role="group" aria-labelledby="gsel-group-apac">
<div class="gsel__heading" id="gsel-group-apac" role="presentation">APAC</div>
<div class="gsel__option" id="gsel-opt-jp" role="option" data-value="jp" aria-selected="false"><span class="gsel__marker" aria-hidden="true"></span><span class="gsel__option-label">Japan</span></div>
<div class="gsel__option" id="gsel-opt-au" role="option" data-value="au" aria-selected="false"><span class="gsel__marker" aria-hidden="true"></span><span class="gsel__option-label">Australia</span></div>
</div>
</div>
</div>
<script>
document.querySelectorAll('.gsel').forEach(function (root) {
var trigger = root.querySelector('.gsel__trigger');
var listbox = root.querySelector('.gsel__listbox');
var valueEl = root.querySelector('.gsel__value');
// Flattened on purpose: the headings are skipped entirely, so ArrowDown at
// the end of Americas lands on Germany, not on the word "EMEA".
var options = Array.prototype.slice.call(root.querySelectorAll('.gsel__option'));
var active = Math.max(0, options.findIndex(function (o) { return o.getAttribute('aria-selected') === 'true'; }));
function paint() {
options.forEach(function (option, index) {
option.classList.toggle('gsel__option--active', index === active);
});
listbox.setAttribute('aria-activedescendant', options[active] ? options[active].id : '');
if (options[active]) options[active].scrollIntoView({ block: 'nearest' });
}
function setOpen(open) {
listbox.hidden = !open;
trigger.setAttribute('aria-expanded', String(open));
if (open) paint();
}
function close() { setOpen(false); trigger.focus(); }
function commit(index) {
var option = options[index];
if (!option) return;
options.forEach(function (o) { o.setAttribute('aria-selected', String(o === option)); });
valueEl.textContent = option.querySelector('.gsel__option-label').textContent;
active = index;
close();
}
trigger.addEventListener('click', function () {
setOpen(trigger.getAttribute('aria-expanded') !== 'true');
});
options.forEach(function (option, index) {
option.addEventListener('click', function () { commit(index); });
option.addEventListener('mousemove', function () { active = index; paint(); });
});
trigger.addEventListener('keydown', function (event) {
var open = trigger.getAttribute('aria-expanded') === 'true';
var key = event.key;
if (!open) {
if (key === 'ArrowDown' || key === 'ArrowUp' || key === 'Enter' || key === ' ') {
event.preventDefault();
setOpen(true);
}
return;
}
if (key === 'Escape') { event.preventDefault(); close(); return; }
if (key === 'Enter' || key === ' ') { event.preventDefault(); commit(active); return; }
if (key === 'Tab') { setOpen(false); return; }
var next = active;
if (key === 'ArrowDown') next = active + 1;
else if (key === 'ArrowUp') next = active - 1;
else if (key === 'Home') next = 0;
else if (key === 'End') next = options.length - 1;
else return;
event.preventDefault();
active = (next + options.length) % options.length;
paint();
});
document.addEventListener('mousedown', function (event) {
if (!root.contains(event.target)) setOpen(false);
});
});
</script>| Prop | टाइप | डिफ़ॉल्ट | विवरण |
|---|---|---|---|
labelआवश्यक | string | - | लोड होते समय पढ़ा जाने वाला एक्सेसिबल लेबल। |
itemsआवश्यक | OptionGroup[] | - | रेंडर की जाने वाली आइटम की सूची। |
value | string | - | मेट्रिक की मौजूदा वैल्यू, पहले से फ़ॉर्मैट की हुई। |
onSelect | (value: string) => void | - | यूज़र द्वारा चुने गए मेन्यू आइटम के साथ चलता है। |
disabled | boolean | false | इंटरैक्शन रोकता है और कंट्रोल को धुँधला कर देता है। |
Two rules make the grouping real rather than decorative. The heading is not an option: it lives in a `role="group"` carrying `aria-labelledby`, so a screen reader announces "Americas, group" at the boundary and the word "Americas" is never selectable. And the keyboard treats the list as flat - the options are flattened once with `flatMap`, so ArrowDown at the bottom of one group falls into the top of the next with no special case. Home/End are absolute, not per-group. If your rows are plain text and single-select, `<select><optgroup>` does all of this natively and cannot regress - this markup earns its keep only past that point.