WAI-ARIA対応のタブ型UIの作り方
(Vue.js編)

98
36
15

この記事は『WAI-ARIA対応のタブ型UIを実装する方法』の続きです。

WAI-ARIAはアクセシビリティーの改善に役立つと先の記事で紹介しました。この記事ではWAI-ARIAに対応したVue.jsでのタブのユーザーインターフェイスを解説します。

サンプルをGitHubにアップしているので、デモとソースコードをご覧ください。

前提として、@vue/cliで環境構築したものとします。@vue/cliを使っていなくても、記事「webpack入門(Vue.jsのサンプル付き)」で紹介しているようにwebpack等で環境構築されていても構いません。

Vue.jsでのステート管理

選択されたタブのIDをプロパティtabに保持することとします。

▼App.vueファイルの抜粋

export default {
  data() {
    return {
      tab: "panel1"
    };
  },

  //
  // (一部省略)
  //
};

※コードは抜粋で掲載しているので、コピーする際はGitHubの「App.vue」を参照ください。

HTMLの実装

HTMLの実装を紹介します。

▼App.vueファイルの抜粋

<template>
  <div>
    <h1>アクセシビリティー対応バージョン</h1>
    <ul role="tablist">
      <li role="presentation">
        <button
            aria-controls="panel1"
            role="tab"
            v-bind:aria-selected="tab === 'panel1' ? 'true' : 'false'"
            v-on:click="handleClick"
        >
          カベルネ・ソーヴィニョン
        </button>
      </li>
      <li role="presentation">
        <button
            aria-controls="panel2"
            role="tab"
            v-bind:aria-selected="tab === 'panel2' ? 'true' : 'false'"
            v-on:click="handleClick"
        >
          メルロー
        </button>
      </li>
      <li role="presentation">
        <button
            aria-controls="panel3"
            role="tab"
            v-bind:aria-selected="tab === 'panel3' ? 'true' : 'false'"
            v-on:click="handleClick"
        >
          ピノ・ノワール
        </button>
      </li>
    </ul>
    <div
        id="panel1"
        aria-labelledby="tab1"
        v-if="tab === 'panel1'"
        role="tabpanel"
        class="panel"
    >
      カベルネ・ソーヴィニョンはブドウの一品種。赤ワインの中でも渋くて重い味わいが特徴です。
    </div>
    <div
        id="panel2"
        aria-labelledby="tab2"
        v-if="tab === 'panel2'"
        role="tabpanel"
        class="panel"
    >
      メルローはブドウの一品種。味はカベルネ・ソーヴィニョンほど酸味やタンニンは強くなく、芳醇でまろやかで繊細な味わいです。
    </div>
    <div
        id="panel3"
        aria-labelledby="tab3"
        v-if="tab === 'panel3'"
        role="tabpanel"
        class="panel"
    >
      ピノ・ノワールはブドウの一品種。カベルネ・ソーヴィニョンと対照的で比較的軽口な味わいです。
    </div>
  </div>
</template>

※コードは抜粋で掲載しているので、コピーする際はGitHubの「App.vue」を参照ください。

HTMLコーディングのポイントとしては次の通り。

  • 期待どおりに読み上げられるようにrole属性を適切に利用します
  • タブとして機能するように、ul要素にrole="tablist"、 タブ部分となるbutton要素にrole="tab"、 パネル部分のdiv要素にrole="tabpanel"を追加します
    • 慣習にしたがってul>liでマークアップしましたが、読み上げの支障となるのでliタグにはrole="presentation"を指定してます(もしかしたらタブUIにul>liを使う必要はないかもしれません)
  • タブ側のボタンは a 要素ではなく button 要素を使ってます。macOS Safariだと a[href="任意"] 要素はデフォルトでタブキーで操作できないためです。Safariでは次のように「Tabキーを押したときにWebページ上の各項目を強調表示」を選択すると、a要素もタブキーで選択可能になります。ただ、このオプションを設定していなくてもタブキーで選択されたほうが望ましいと考えてのことです

macOS Safariでの設定ウインドウの「Tabキーを押したときにWebページ上の各項目を強調表示」

JavaScriptとVue.jsに絡んでくるポイントは次の通り。

  • タブとなるbutton要素とパネルのdiv要素の関連性を示すためaria-controls属性を指定します。値は任意でid属性を指定します
  • button要素にタブの選択状態を伝えるために、aria-selectedを真偽値で指定します
    • Vue.jsのプロパティの値で動的とします。こうすれば半自動的にaria-selected属性が切り替わります
    • Vue.jsの場合は、aria-selectedを真偽値は文字列でtruefalseを設定しなければなりません。真偽値のfalseだとaria-selected属性自体が外れてしまうからです

ボタン要素のイベントハンドラー

ボタン要素のイベントハンドラーのコードを紹介します。ボタンとパネルの紐付けは、意味的に合致している aria-controls 属性を利用してます。JavaScriptの制御が必要なものは独自の変数ではなく、可能な限り aria-* 属性で代替するのがベターなやり方と思います。

▼App.vueファイルの抜粋

export default {
  data() {
    return {
      tab: "panel1",
    };
  },
  methods: {
    /**
     * クリックしたときのイベントハンドラーです。
     * @param event イベントオブジェクトです。
     * @private
     */
    handleClick(event) {
      // イベント発生源の要素を取得
      const element = event.currentTarget;
      // aria-controls 属性の値を取得
      const tabState = element.getAttribute("aria-controls");
      // プロパティーを更新
      this.tab = tabState;
    },
  },
};

※コードは抜粋で掲載しているので、コピーする際はGitHubの「App.vue」を参照ください。

CSSの実装

CSSはなるべく class 属性を使わず、aria-* 属性をセレクターとして指定しています。こうすれば、余計なクラス属性を増やす必要がなくなります。

▼ app.component.cssの抜粋

/* UI制御のための指定 */
[aria-selected="true"] {
  background-color: royalblue;
  color: white;
}

※コードは抜粋で掲載しているので、コピーする際はGitHubの「App.vue」を参照ください。

CSSの実装はCodeGridの記事「WAI-ARIAを活用したフロントエンド実装」で紹介されている「aria属性をCSSセレクターとして利用する」「独自に名前を付けるくらいなら、意味的に合致するaria属性を利用して、アクセシビリティーを確保しましょう」の提案をアイデアとしています。

まとめ

Vue.jsで実装する場合はタブの状態はいずれかのプロパティで管理しているはずです。その値を間借りして aria-* 属性に適用すれば、簡単にアクセシビリティーを向上できます。今回はタブ型UIで紹介しましたが、これは一例に過ぎません。さまざまなユーザーインターフェイスに利用できるので応用くださいませ。

昨年の記事「脱jQueryのためにしたこと」でも紹介したように、AngularやVue.js等のJSライブラリとWAI-ARIAの相性は抜群です。ほんの少しWAI-ARIAの理解が進めば、Vue.jsユーザーの皆さんは簡単に利用できるでしょう。私個人としても多くのプロダクトで積極的にWAI-ARIAを実装しています(パブリックな事例としては「ICS MEDIA」、「Beautifl」など)。この記事によって、音声読み上げを求めているエンドユーザーへの配慮が少しでも進めばと考えています。

この方法はAngularやReactでも実装できるので、次の記事で紹介しています。ぜひご利用ください。

補足

記事を作成するにあたり複数のサンプルを用意して音声読み上げソフト(macOSの「VoiceOver」や「NVDA日本語版」)で検証しました。

※この記事が公開されたのは5年前ですが、1年前の2022年11月に内容をメンテナンスしています。

池田 泰延

ICS代表。筑波大学 非常勤講師。ICS MEDIA編集長。個人実験サイト「ClockMaker Labs」のようなビジュアルプログラミングとUIデザインが得意分野です。

この担当の記事一覧