ARIA attribute values
Attribute names are typed from the WAI-ARIA 1.2 vocabulary: an unknown aria-* name is a type error, and role accepts only real roles.
Attribute names are typed from the WAI-ARIA 1.2 vocabulary: an unknown aria-* name is a type error, and
role accepts only real roles. The runtime then writes values with these rules (from
docs/contracts/dom-runtime.md):
| You write | aria-*, data-*, draggable, spellcheck, contenteditable |
any other attribute |
|---|---|---|
true |
"true" |
present with an empty value |
false |
"false" |
removed |
null, undefined |
removed | removed |
| a string or number | String(value) |
String(value) |
The first column is why aria-expanded={open()} works: false is a meaningful state ("false") and
must not remove the attribute. The second column is why hidden={!open()} works: false means "not
hidden", which is the absence of the attribute. Boolean attributes such as disabled, hidden,
checked and open are set as DOM properties on HTML elements, which reflect to attributes.
import { useId, useStore } from "@shadow-js/core";
export function Disclosure() {
const [open, setOpen] = useStore(false);
const panel = useId();
return (
<div>
<button
type="button"
aria-expanded={open()}
aria-controls={panel}
onClick={() => setOpen((value) => !value)}
>
Details
</button>
<div id={panel} role="region" aria-label="More information" hidden={!open()}>
Everything there is to know.
</div>
</div>
);
}
Prefer the visible, native state over ARIA when one exists: disabled over aria-disabled, checked
over aria-checked, <details> over a hand-made disclosure. ARIA that contradicts the native state
is worse than none.