Upgrade to Pro — share decks privately, control downloads, hide ads and more …

アクセシブルなフロントエンド開発のこれまでとこれから / the past and future of accessible front-end development

Okuto Oyama
November 27, 2021

アクセシブルなフロントエンド開発のこれまでとこれから / the past and future of accessible front-end development

JSConfJP 2021 発表資料

昨今アクセシビリティに関する興味関心が増えてきた中で、SPAといったWebアプリケーションにおけるアクセシビリティを考慮した実装についてはまだ認識が広まっていないように感じます。 フロントエンドフレームワークを使ってより良いアクセシビリティ実装をするにはどうすればいいかの方法、その結果どういう形で伝わっているかを紹介しようと思います。 またWebアプリケーションでHTMLを使ってUI実装することに限界を感じており、それを解消するためのReactGUIやHeadless UIといった新たなアプローチにおける期待についても紹介したいと思っております。

Okuto Oyama

November 27, 2021
Tweet

More Decks by Okuto Oyama

Other Decks in Technology

Transcript

  1. େࢁԞਓʢ͓͓΍·͓͘ͱʣ • he/him • גࣜձࣾΫϥ΢υϫʔΫε • ϓϩμΫτຊ෦ ϓϩμΫτ։ൃ෦ 
 ϓϥοτϑΥʔϜ։ൃ̏άϧʔϓ

    • ࣗশϑϩϯτΤϯυσβΠφʔ • ΞΫηγϏϦςΟܒ໤΍OSSίϯτϦ Ϗϡʔτ׆ಈΛ͍ͯ͠·͢ • Ұࣇͷ෕
  2. Press Release: W3C Launches International Program Of fi ce for

    WAI “The power of the Web is in its universality. Access by everyone regardless of disability is an essential aspect.”
  3. HTML Λਖ਼͘͠࢖͏ͨΊʹ • Nu Html Checker Λ௨ͯ͡ߏจΤϥʔͷ֬ೝΛ͢Δ • ೖΕࢠؔ܎Λཧղͯ͠࢖༻͢Δ •

    ࢧԉٕज़ʹ௨͡ΔϚʔΫΞοϓ • CSS͕ͳͯ͘΋ҙຯ͕ཧղͰ͖ΔΑ͏ʹͳ͍ͬͯΔ
  4. RouteAnnouncer const { asPath } = useRouter( ) const [routeAnnouncement,

    setRouteAnnouncement] = React.useState('' ) const initialPathLoaded = React.useRef(false ) React.useEffect ( () => { if (!initialPathLoaded.current) { initialPathLoaded.current = tru e retur n } if (document.title) { setRouteAnnouncement(document.title ) } else { const pageHeader = document.querySelector('h1' ) const content = pageHeader?.innerText ?? pageHeader?.textConten t setRouteAnnouncement(content || asPath ) } } , [asPath ] )
  5. Async Pipe <div *ngIf="title$ | async as title" aria-live="polite" >

    <span [attr.aria-label]="title"></span > </div >
  6. LiveAnnouncer @Component( { selector: "app-component " providers: [LiveAnnouncer ] }

    ) export class AppComponent { constructor(liveAnnouncer: LiveAnnouncer) { liveAnnouncer.announce("live region!") ; } }
  7. vue-announcer { name: 'home' , path: '/' , component: Home

    , meta: { announcer: { // vue-announcer setting s message: 'ϗʔϜը໘ ' } } }
  8. React GUI • React DOM ޲͚ͷ GUI πʔϧΩοτ • υΩϡϝϯτϏϡϫʔ্ͰGUIΛදݱ͍ͤͨ͞

    • React Native for Web ͔Βͷܥේ • React Native ύʔπΛ࢖༻ͯ͠ Web ্Ͱදݱ͢Δ • RN for Web ͸Twitter Liteʢݱ Twitter WebʣͰ࢖༻
  9. Headless UI • Tailwind Labs ։ൃ • ݟͨ໨Λ࡞͍ͬͯͳ͍ UI ϥΠϒϥϦ

    • Tailwind CSS ͳͲͰఆٛͰ͖Δ • React.js ͱ Vue3 ͷΈରԠ
  10. React Aria • Adobe ։ൃ • React Spectrum σβΠϯγεςϜΛߏ੒͢Δ̍ཁૉ •

    UIͱͯ͠ͷڍಈɺΞΫηγϏϦςΟ࣮૷Λ Hooks Ͱ෼཭ • React.js ͷΈରԠ
  11. React A11y & TailwindCSS let { buttonProps, isPressed } =

    useButton(props, ref) ; let { focusProps, isFocusVisible } = useFocusRing() ; let className = classNames ( props.isDisabled ? "bg-gray-400" : isPressed ? "bg-blue-700" : "bg-blue-500" , "text-white", "font-bold" , "py-2", "px-4" , "rounded" , "cursor-default" , "focus:outline-none" , isFocusVisible ? "shadow-outline" : "" , "transition", "ease-in-out", "duration-150 " );
  12. useNumberField function NumberField(props) { let {locale} = useLocale() ; let

    state = useNumberFieldState({...props, locale}) ; let inputRef = React.useRef() ; let incrRef = React.useRef() ; let decRef = React.useRef() ; let { labelProps, groupProps, inputProps, incrementButtonProps, decrementButtonProps } = useNumberField(props, state, inputRef) ; let {buttonProps: incrementProps} = useButton(incrementButtonProps, incrRef) ; let {buttonProps: decrementProps} = useButton(decrementButtonProps, decRef) ; return ( <div > <label {...labelProps}>{props.label}</label > <div {...groupProps} > <button {...decrementProps} ref={incrRef} > - </button > <input {...inputProps} ref={inputRef} / > <button {...incrementProps} ref={decRef} > + </button > </div > </div > ) ; }
  13. Custom Elements Λੜ੒͢Δ class ToggleButton extends HTMLElement {
 connectedCallback() {

    this.setAttribute("role", "button") ; this.setAttribute("aria-pressed", “false") ; this.addEventListener("click", togglePressed) ; this.addEventListener("keydown", function (event) { if (event.key === "Enter" || event.key === "Space") { togglePressed() ; } }) ; } } customElements.define("toggle-button", ToggleButton);
  14. Custom Element & AOM Ͱදݱ͢Δ class ToggleButton extends HTMLElement {

    constructor() { super() ; this._internals = this.attachInternals() ; this._internals.role = "button";
 this._internals.ariaPressed = false ; } } ToggleButton.addEventListener('keydown', (event) => { if (event.key === "Enter" || event.key === "Space") { toggleButton() ; } }) ; customElements.define("toggle-button", ToggleButton);