접근성과 Shadow DOM
그리드는 WAI-ARIA grid 패턴을 따릅니다. 행 영역에 role="grid"(그룹이나 트리일 때는 treegrid)가 있고, 헤더 행과 셀에 행·열 번호가 붙으며, 포커스된 셀은 aria-activedescendant로 스크린리더에 읽힙니다. 키보드만으로 모든 기능을…
실행 예제접근성과 Shadow DOM
ARIA grid 구조, 스크린리더가 읽는 포커스 셀, Shadow DOM 안의 그리드.
이 데모의 코드
import { createGrid } from '@freegridjs/core';
import css from '@freegridjs/core/styles.css?inline';
import { demoColumns, makeRows } from '../data';
import { button, separator, text, tr, type DemoContext } from '../ui';
export default function mount(ctx: DemoContext) {
const { lang } = ctx;
const h = (en: string, ko: string) => tr(lang, { en, ko });
// the grid lives in a shadow root, as in a web component; the stylesheet goes into the shadow root
const shadow = ctx.host.attachShadow({ mode: 'open' });
const style = document.createElement('style');
style.textContent = css;
const box = document.createElement('div');
box.style.height = '100%';
shadow.append(style, box);
const grid = createGrid(box, { columns: demoColumns(lang), data: makeRows(200), locale: lang, rowSelection: 'multiple', groupPanel: true });
grid.on('focusChange', e => {
// what a screen reader announces: the focused cell, through aria-activedescendant
const gridEl = shadow.querySelector('.fg-grid')!;
const cell = shadow.getElementById(gridEl.getAttribute('aria-activedescendant') ?? '');
ctx.log(`${h('focus', '포커스')} → ${h('row', '행')} ${e.rowIndex + 1}, ${e.colId}: ${cell?.textContent ?? ''}`);
});
text(ctx, h('This grid sits inside a Shadow DOM. Tab into it and use the arrow keys: the log shows what a screen reader reads. Menus and filters open inside the shadow root.',
'이 그리드는 Shadow DOM 안에 있습니다. Tab으로 들어가 방향키로 움직이면 스크린리더가 읽는 내용이 아래에 표시됩니다. 메뉴와 필터도 shadow root 안에서 열립니다.'));
separator(ctx);
button(ctx, h('Show ARIA roles', 'ARIA 역할 보기'), () => {
const g = shadow.querySelector('.fg-grid')!;
ctx.log(`role=${g.getAttribute('role')} aria-rowcount=${g.getAttribute('aria-rowcount')} aria-colcount=${g.getAttribute('aria-colcount')} aria-multiselectable=${g.getAttribute('aria-multiselectable')}`);
ctx.log(`${h('header rows', '헤더 행')}: ${shadow.querySelectorAll('.fg-header [role=row]').length}, ${h('column headers', '열 머리글')}: ${shadow.querySelectorAll('[role=columnheader]').length}`);
});
button(ctx, h('Group by country (becomes a treegrid)', '국가별 묶기 (treegrid로 바뀜)'), () => {
grid.setGroupBy(grid.getGroupBy().length ? [] : ['country']);
setTimeout(() => ctx.log(`role=${shadow.querySelector('.fg-grid')!.getAttribute('role')}`), 50);
});
return grid;
}그리드는 WAI-ARIA grid 패턴을 따릅니다. 행 영역에 role="grid"(그룹이나 트리일 때는 treegrid)가 있고, 헤더 행과 셀에 행·열 번호가 붙으며, 포커스된 셀은 aria-activedescendant로 스크린리더에 읽힙니다. 키보드만으로 모든 기능을 쓸 수 있고, axe 자동 검사를 테스트에 넣어 관리합니다.
웹 컴포넌트처럼 Shadow DOM 안에 그리드를 넣어도 됩니다. 메뉴와 팝업이 같은 shadow root 안에 열리므로, 스타일시트를 그 shadow root에 넣어 주세요.