Making the web accessible
Dynamic content on a web page can be particularly problematic for users who, for whatever reason, are unable to view the screen. Stock tickers, live twitter feed updates, progress indicators, and similar content modify the DOM in ways that an assistive technology (AT) may not be aware of. That's where ARIA comes in.
from MDN
<select>
<option value="A">A</option>
<option value="B">B</option>
</select>
For example...
<div>
<button
type="button"
role="listbox"
aria-activedescendant="option-B"
aria-owns="menu-modal-1">
Preferences
</button>
<div id="menu-modal-1">
<button
id="option-A"
role="option"
aria-seleced="false">
A
</button>
<button
id="option-B"
role="option"
aria-selected>
B
</button>
</div>
</div>
ARIA | 意義 |
---|---|
aria-activedescendant | 已選取的 id |
aria-owns | 父子關係 |
aria-selected | 選項狀態 |
Clear semantics
ARIA | Description | 情境 |
---|---|---|
aria-label | 描述物件(會取代原本描述) | 無描述 or 看不見時 |
aria-labelledby | 給 id, 描述物件(會取代原本描述) | 描述在其他地方時 |
aria-describedby | 補充描述(會接在原本描述後方) | 補充描述物件 |
aria-hidden | 略過不念(有互動性的無法略過) | 裝飾性物件 |
aria-owns | 表達父子關係(千萬不要用在補充描述上) | 類 <select> |
aria-controls | 表達某物件受控於自己 | |
aria-live | 資料發生變化,自動報讀 polite: 有空閒時報讀 assertive: 立即報讀 (中斷目前的) |
control / value 是分開時(只會報讀離最近的) |
aria-expanded | 描述物件是否展開 | 有展開/收合時 |
aria-required | 必填欄位 | 必填時 |
<!--self close-->
<div id="section-closed" />
<!--neighbor-->
<div id="section-1" />
<div id="section-2">
...
</div>
ARIA Roles are important
<select>
<option>
<dialog>
or we called Modal
<body>
<div id="root">
<button id="selector" aria-owns="portal-1">
Open dialog
</button>
<button id="button-2">
Button
</button>
</div>
<div id="portal-container">
<dialog id="portal-1">
...
</dialog>
</div>
</body>
<dialog>
Required JavaScript features
<dialog>
<table>
<table>
<thead>
<tr>
<th colspan="2">The table header</th>
</tr>
</thead>
<tbody>
<tr>
<td>The table body</td>
<td>with two columns</td>
</tr>
</tbody>
</table>
<table>
<div role="table" aria-label="Semantic Elements" aria-rowcount="3" aria-colcount="2">
<div role="rowgroup">
<div role="row" aria-rowindex="1">
<span role="columnheader">ARIA Role</span>
<span role="columnheader">Semantic Element</span>
</div>
</div>
<div role="rowgroup">
<div role="row" aria-rowindex="2">
<span role="cell">row</span>
<span role="cell">tr</span>
</div>
<div role="row" aria-rowindex="3">
<span role="cell">rowgroup</span>
<span role="cell">thead</span>
</div>
</div>
</div>
ARIA Role | Semantic Element |
---|---|
row | tr |
rowgroup | thead, tbody |
row = 1
row = 2
row = 3
col = 1
col = 2
<table>
<tr>, <th>, <td>
<div
role="presentation"
onClick={() => {}}>
I'm clickable
</div>
role="presentation" ?
The presentation role is used to remove semantic meaning from an element
Bad Accessibility !!
<div
tabIndex="0"
aria-label="I'm focusable">
...
</div>
tabIndex ?
css focus-within
const wrapper = css`
width: 100%;
display: none;
&[open] {
display: block;
}
&:focus-within {
border: 1px solid #1a1a1a;
}
`;
return (
<dialog open="true" className={wrapper}>
<input type="text" />
<button type="button">
button
</button>
</dialog>
);
css focus-within
const wrapper = css`
width: 100%;
transition: opacity 0.2s ease-in;
opacity: 0;
pointer-events: none;
visibility: hidden;
&[open] {
opacity: 1;
pointer-events: auto;
visibility: visible;
}
&[open]:not(:focus-within) {
opacity: 0.99;
transition: opacity 0.01s ease-in;
}
`;
function Modal() {
const ref = useRef();
useEffect(() => {
const { current: modal } = ref;
function forceFocus() {
const modalFocusableNodes = modal.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
if (modalFocusableNodes.length) {
modalFocusableNodes[0].focus();
}
}
if (modal) {
modal.addEventListener('transitionend', forceFocus);
}
return () => {
if (modal) {
modal.removeEventListener('transitionend', forceFocus);
}
};
}, []);
return (
<dialog ref={ref} open="true" className={wrapper}>
<input type="text" />
<button type="button">
button
</button>
</dialog>
);
}
DEMO
Thanks