Basic Input
A labelled text input with helper text wired to it via aria-describedby.
6 個のフレームワーク初級
A disclosure over a fieldset of real checkboxes - no listbox, no virtual focus.
<!--
Deliberately NOT a listbox. The popup is a <fieldset> of real checkboxes,
which buys three things a role="listbox" cannot:
- No aria-activedescendant, no arrow-key handler, no virtual focus. Tab and
Space are the browser's, and they already work.
- Every box posts itself in a form submission. No hidden inputs to sync.
- "Checked"/"not checked" is announced by the platform, in the user's own
words, with no aria-selected to keep in step.
The cost is that Tab now walks every option rather than skipping the group -
fine for eight rows, wrong for eighty. Past that, use the listbox
(multiselect-chips) and pay for the keyboard handler.
The toggle still needs aria-expanded: it is a disclosure, and a button whose
popup state is invisible to a screen reader is a button that announces
nothing when pressed.
-->
<div class="mcheck">
<button
class="mcheck__toggle"
id="mcheck-toggle"
type="button"
aria-expanded="false"
aria-controls="mcheck-panel"
>
<span class="mcheck__summary">Notifications: 2 selected</span>
<svg class="mcheck__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="mcheck__panel" id="mcheck-panel" hidden>
<fieldset class="mcheck__fieldset">
<legend class="mcheck__legend">Notify me about</legend>
<label class="mcheck__row"><input class="mcheck__input" type="checkbox" name="notify" value="mentions" checked /><span>Mentions</span></label>
<label class="mcheck__row"><input class="mcheck__input" type="checkbox" name="notify" value="replies" checked /><span>Replies</span></label>
<label class="mcheck__row"><input class="mcheck__input" type="checkbox" name="notify" value="assignments" /><span>Assignments</span></label>
<label class="mcheck__row"><input class="mcheck__input" type="checkbox" name="notify" value="deploys" /><span>Deploys</span></label>
<label class="mcheck__row"><input class="mcheck__input" type="checkbox" name="notify" value="digest" /><span>Weekly digest</span></label>
<label class="mcheck__row"><input class="mcheck__input" type="checkbox" name="notify" value="billing" /><span>Billing alerts</span></label>
</fieldset>
</div>
</div>
<script>
document.querySelectorAll('.mcheck').forEach(function (root) {
var toggle = root.querySelector('.mcheck__toggle');
var panel = root.querySelector('.mcheck__panel');
var summary = root.querySelector('.mcheck__summary');
var inputs = Array.prototype.slice.call(root.querySelectorAll('.mcheck__input'));
function setOpen(open) {
panel.hidden = !open;
toggle.setAttribute('aria-expanded', String(open));
if (open && inputs[0]) inputs[0].focus();
}
function renderSummary() {
var count = inputs.filter(function (i) { return i.checked; }).length;
summary.textContent = 'Notifications: ' + (count === 0 ? 'none selected' : count + ' selected');
}
toggle.addEventListener('click', function () {
setOpen(toggle.getAttribute('aria-expanded') !== 'true');
});
inputs.forEach(function (input) {
input.addEventListener('change', renderSummary);
});
// Escape from anywhere inside the popup, including from a checkbox - the
// one key a disclosure must honour and the easiest one to forget.
root.addEventListener('keydown', function (event) {
if (event.key !== 'Escape') return;
setOpen(false);
toggle.focus();
});
document.addEventListener('mousedown', function (event) {
if (!root.contains(event.target)) setOpen(false);
});
});
</script>| Prop | 型 | デフォルト | 説明 |
|---|---|---|---|
label必須 | string | - | 読み込み中に読み上げられるアクセシブルなラベル。 |
items必須 | SelectItem[] | - | レンダリングする項目の配列。 |
value必須 | string[] | - | 指標の現在値。フォーマット済みの文字列を渡します。 |
onSelect | (next: string[]) => void | - | ユーザーが選択したメニュー項目を引数に呼ばれます。 |
disabled | boolean | false | 操作を無効にし、コントロールを淡色表示にします。 |
Pointedly not a listbox. Real `<input type="checkbox">` means the browser owns Tab and Space, the platform announces "checked" in the user’s own words, and there is no `aria-selected` to drift from the visual tick - the whole `aria-activedescendant` apparatus disappears. `fieldset` + `legend` is what makes six loose checkboxes one question. The trade is that Tab now walks every option instead of skipping the group: fine at eight rows, wrong at eighty. Past that, use `multiselect-chips` and pay for the keyboard handler. The toggle takes `aria-expanded` without `aria-haspopup="listbox"` - it is a disclosure, and promising a listbox that is not there is worse than promising nothing.