WAI-ARIAを使ってアクセシビリティを上げながら挙動やスタイルを制御する

WAI-ARIAについての説明は今回省かせていただきます。詳しくは[Accessible Rich Internet Applications (WAI-ARIA) 1.1 日本語訳](https://momdo.github.io/wai-aria-1.1/)をご覧下さい。

## なぜWAI-ARIAを使って制御するのか
WAI-ARIAはステートおよびプロパティを明示するものがあります。それを使うことで、不要なclassでの管理を抑えることができます。
例えば以下のように`aria-checked`を使って、状態を表現する事もできます。

```html
<li role="menuitemcheckbox" aria-checked="true">Sort by Last Modified</li>
<style>
[aria-checked="true"] { font-weight: bold; }
[aria-checked="true"]::before { background-image: url(checked.gif); }
</style>
```
このように情報と見た目で挙動をコントロールしていきたいと思います。

## 実際にWAI-ARIAで制御する
今回は制作現場でもよく使用されるDialog、Accordionを作っていきたいと思います。

### Dialog
今回はこんな感じでDialogを作ってみました。
使ったWAI-ARIAは以下になります。

* **aria-hidden='true'**：スクリーンリーダーに読み上げられないように、要素を隠します。今回はこれを使ってCSSで挙動を制御してみます。
* **aria-labelledby='dialog-heading'**：見出しと関連付けています。
* **aria-describedbyy='dialog-desc'**：説明文と関連づけています。
* **tabindex='-1'**：Tabキーによるフォーカスの対象にしないが、フォーカスはできるようにします。

#### HTML
```html
<main id='js-main'>
  <p>Main Contents...</p>
  <button id='js-show-dialog'>Show Dialog!!</button>
</main>
<div id='js-dialog' class='dialog-overlay' aria-hidden='true'>
  <div class='dialog' 
       tabindex='-1'
       aria-labelledby='dialog-heading' 
       aria-describedby='dialog-desc'>
    <div class='dialog-header'>
      <h2 id='dialog-heading'>dialogの見出し</h2>
      <p id='dialog-desc'>dialogの説明</p>
      <span class='dialog-icon-close' aria-hidden='true'>×</span>
    </div>
    <div class='dialog-body'>
      Dialog Contents...  
    </div>
    <div class='dialog-footer'>
      <button id='js-close-dialog'>閉じる</button>
    </div>
  </div>
</div>
```

#### CSS
```css
main[aria-hidden='true'] {
  opacity: .05;
}

.dialog-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, .5);
}

.dialog-overlay[aria-hidden="true"] {
  display: none;
}

.dialog {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%);
  background-color: #fff;
  width: 480px;
}
```

#### JavaScript
```js
const main = document.getElementById('js-main');
const dialog = document.getElementById('js-dialog');
document.getElementById('js-show-dialog').addEventListener('click', () => {
  dialog.removeAttribute('aria-hidden');
  main.setAttribute('aria-hidden', true);
})
document.getElementById('js-close-dialog').addEventListener('click', () => {
  dialog.setAttribute('aria-hidden', true);
  main.removeAttribute('aria-hidden');
})
```

とても簡単なのですが、スクリーンリーダーが読まなくても良い部分に`aria-hidden='true'`をつけています。今回は`removeAttribute`を使って出し分けていますが`element.setAttribute('aria-hidden', false);`でもいいみたいです。なんとなくしっくりこなかったので、今回は属性ごと削除をしています。

### Accordion
Accordionで使ったWAI-ARIAは以下になります。
* **aria-expanded='false'**:要素の状態を表現します。ただこれは隠す要素に対して行うのではなく、制御する側の状態を示します。
* **aria-controls='acc-foo'**：コントロールする要素を示します。この場合は、`id='acc-foo'`の要素をコントロールする事を指しています。

#### HTML
```html
<div class='acc'>
  <div class='acc-header' aria-expanded='false' aria-controls='acc-foo'>
    Open Foo
  </div>
  <div id='acc-foo' class='acc-body'>
    <p>Foo.</p>
  </div>
  <div class='acc-header' aria-expanded='false' aria-controls='acc-bar'>
    Open Bar
  </div>
  <div id='acc-bar' class='acc-body'>
    <p>Bar.</p>
  </div>
  <div class='acc-header' aria-expanded='false' aria-controls='acc-baz'>
    Open Baz
  </div>
  <div id='acc-baz' class='acc-body'>
    <p>Baz.</p>
  </div>
</div>
```
#### CSS
```css

.acc {
  border: 1px #e3e3e3 solid;
}

.acc-header[aria-expanded='true'] {
  background-color: #eaa3a2;
  cursor: default;
}

.acc-body {
  height: 0;
  overflow: hidden;
}

.acc-header[aria-expanded='true'] + .acc-body {
  height: auto;
}

.acc-body p {
  padding: 10px;
}
```

#### JavaScript

```js
const triggers = document.querySelectorAll('[aria-controls^="acc-"]');
const targets = document.querySelectorAll('[id^="acc-"]');
triggers.forEach((trigger) => {
  trigger.addEventListener('click', function() {
    triggers.forEach((trigger) => {
      trigger.setAttribute('aria-expanded', false);
    });
    this.setAttribute('aria-expanded', true);
  });
});
```

今回は`aria-expanded`をコントロールできればいいので雑な実装になってます。

## まとめ
普段はなんでもclassを付与して、見た目や挙動の変更を行ってきましたが、アクセシビリティに沿った方法で変更する事ができました。
今回調べてみたきっかけは、BootstrapがWAI-ARIAを挙動に合わせてコントロールしていたので興味を持ちました。BootstrapのComponentsの中には、他の属性を使ったものもあると思うので興味のある人は参考にしてみてください。