アクセシビリティ向上のためにマークアップ時に行うこと、イロイロ

普段からフツーにやっている、アクセシビリティ向上のために行っていることを、書いていきます。思いついたら書き足していくので、どんどん増えていくことになるカモ。コレを書いている途中で気づいて、慌てて既存サイトに適用したモノもいくつかありますよ。

ちなみに、これまでいかなる制作現場に於いても「アクセシビリティ対応して頂戴」と依頼されたことはありません。ドコの制作現場もアクセシビリティ意識低め…。

マークアップ

ランドマークロールを適切に設定する

ランドマークロールとは、Webページ内の大きめの構成要素の役割を明示的に表したもの。タグ内にrole属性を書くことで設定できます。スクリーンリーダー等の支援技術には、各ランドマークにジャンプする機能があるため、ランドマークロールが適切に設定されていれば、要素間の移動が容易になります。

実はheader, footer, main, nav, asideにはデフォルトでrole属性が設定されている(header,footerはbody要素の直接の子要素である場合のみ)ので、通常のWebページであれば上記要素を適切に配置しさえすれば、改めてrole属性を書く必要はありません。敢えて行うとしたら、検索フォームにrole=”search”と書くくらいでしょうか。

ランドマークロールは8種類(カッコ内はデフォルトでそのrole属性を持つ要素)
  • application
  • banner (header)
  • complementary (aside)
  • contentinfo (foote)
  • main (main)
  • navigation (nav)
  • search

見出しを適切に設置し、アウトラインを正しく生成する

トップページのh1見出しはロゴマーク、下層ページはそのページの内容を表す見出しをh1要素としています。ヘッダーの共通パーツをインクルードしている場合は、条件分岐による要素の出し分けが必要となります。

デザインカンプには見出しは無いが、内容から見出しを設置したほうがよいと思われるセクションがあった場合、sr-only等のclassを付け、表示されないがスクリーンリーダーに認識される形で見出しを設置することもあります(ソースコードはbootstrapのやつをまんま流用)。

あるいは、見た目は見出しだけども明らかにリード文と思われる内容のときは、p要素でマークアップし、見えない見出しを付けておくこともあります。

このへんは設計段階できちんと考えられているべきなのだとは思いますが、もはやデザインに口出しできない段階でコーディングに取り掛かる場合がほとんどなので…(O_O)

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

見出しを含むセクションはsection要素でマークアップすることが推奨されているようですが、ソースコードがワヤになるので、あまり対応していません。見出しを設置することによって「暗黙のセクション」が生成されると、スクリーンリーダーでもだいたい適切に読み上げてくれるようですし。

アウトラインの生成具合の確認にはchrome拡張機能のhedingsMapHTML5 Outlinerを使っています。

見出しに付随する余計な要素はマークアップしない

ニホンゴのWebサイトで、非常によく見かけるのが、英単語を付記した見出しやグローバルナビゲーション。エーゴが一緒に表示されているとなんとなくカッコいい、とゆー理由で飾りとして置いているのかと思われますが、おそらく、英語ネイティブのヒトから見たら至極滑稽なんじゃないかなー、と、筆者は密かに思っています。

英単語が付記された見出し

ジブンの母語が同様に扱われている場合を想像してみましょう。例えば、日本関連のコンテンツを扱っているわけでもないフランス語のWebサイトの見出しに、唐突にニホンゴの単語が添えられていたら、「え?なんで?」って思いますよね。綴りが間違っていたり、そもそも単語の選定が適切でなかったら、「ぷぷ」と失笑すること必至かと。こんなケースはまず無いとは思いますけども。

とはいえ、「ニホンゴ見出しに英単語が添えられている」デザインは、だいたいほとんど全てと言っていいくらい筆者が受け取るデザインカンプの中に存在するので、無視するわけにもいかず、とりあえず見えるようにはしますが…beforeまたはafter疑似要素、もしくは背景画像として表示し、テキストとしてマークアップしないようにしています。時として適切な単語でなかったり、はたまた綴りが間違っていたりすることすらあり(気づいたら直しますが)、すなわち情報として必要でないことは明らかなわけで、ともすれば間違っているかもしれない不要な情報をスクリーンリーダーで読み上げられるようにしておくのは適切ではなさそうなので。

上記のような見出しを、ニホンゴはh2で、英語はh3でマークアップしていたりするのをたまに見かけますが、見出しのマークアップとしてはまったくもって適切ではありません。どうしてもテキストとしてマークアップしたいのであれば(疑似要素や背景にするとスタイルシートに書かなきゃならんので、管理がめんどうですからね。)、せめてspanとかpにしておいたほうが無難カモ。さらにはaria-hidden属性をtrueにしておくと良いかも。

a要素とbutton要素の区別

ハイパーリンクはa要素、クリック時に何かアクションを起こさせるもの(メニューボタン、送信ボタン等)はbutton要素としてマークアップします(フォーム要素に対して何も行わないボタンはtype=”button”を設定)。何らかの理由でボタンの役割を担う要素をbutton要素でマークアップできないときは、タグにrole=”button”とrole属性を追加します。

スマホ用メニューの属性切り替え

アクセシビリティ向上を目指してスマホメニュー開閉時の属性切り替え機能を実装しようとしたことがあります。最終的にたどり着いたのが以下のソース。

See the Pen css grid02 by keiko (@kamekon) on CodePen.dark

この状態で十分にアクセシブルなのかどうか心もとないのですが、ホカから「こうこうこうして頂戴」とリクエストがあるわけでもないので、現在はこの形のソースを提供することが多いです。改善提案等あれば承りたいと思っています。

開閉ボタンの属性値は以下のようになっています。

id="js-menu"
aria-label="メニューを開く"
aria-expanded="false"
aria-controls="js-gnav"

ボタンをクリックするとメニューが開き、属性値は以下のように変わります。

aria-label="メニューを閉じる"
aria-expanded="true"

メニューの属性値は以下のようになっています。

id="js-gnav"
aria-labelledby="js-menu"
aria-hidden="true"

メニューが開いている時、属性値は以下のように変わります。

aria-hidden="false"

フォーム要素のマークアップ

input要素とラベルはセットにする

input要素とその要素が何のための入力欄なのか等を表すラベルテキストは、必ず関連付けておく必要があります。label要素でinput要素とテキストを囲むか、テキストを囲んだlabel要素にfor属性を付け、input要素に付けたid属性の値を指定します。

<label>text 必須<input type="text" aria-required="true"></label>
<label for="input01">text 必須</label>
<input type="text" id="input01" aria-required="true">

ラベルをクリックすると、ラベルによって関連付けられているinput要素にフォーカスが移るので、画面が見えているユーザーにとっても親切な設計となります。

検索ボックスなど、スペースに制限があるなどの理由でラベルを配置したくない場合は、input要素にtitle属性を付記し、入力すべき内容などを記載します。

//検索フォーム
<input type="text" title="検索キーワード">
<button>検索</button>

//電話番号の入力欄等
<input type="tel" id="input04" title="市外局番"> - <input type="tel" title="市内局番"> - <input type="tel" title="加入者番号">

input要素の注釈との関連付け

文字数制限・パスワードのフォーマットなど、入力する内容に制限がある場合、そのことを説明する注釈はinput要素の前に記載すべきです。デザイン上の理由等で注釈をinput要素の後に記載しなければならない場合は、注釈を任意のタグで囲んでid属性を設定した上で、input要素にaria-describedby属性を追加し、注釈のidを指定します。

<input type="password" id="input03" aria-describedby="input03-note">
<p id="input03-note">半角英数字5文字以上で入力してください</p>

キーボードでinput要素をたどる際は、注釈がinput要素の前に記載されていても読み上げられないので、説明文の位置に関わらず上記の措置を行ったほうがよいカモ

checkboxとradioボタンのフォーカス時の見た目を整える

デフォルト以外の見た目にカスタマイズしたcheckboxとradioボタンは、フォーカスした時にそうだとわかるように見た目の調整を行います。

筆者個人としては、checkboxとradioボタンはデフォルトのままのほうがアクセシブルでいいのではないかと思うのですが、キレイにデザインされたカンプを渡されると、渋々見た目のカスタマイズを行っています。するとデフォルトのinput要素を見えないようにして(※)疑似要素に対して見た目を整えることになるので、それに対してフォーカス時の見た目も設定しなければなりません。

以下の例ではチェックボックスにフォーカスした時box-shadowを表示するようにしています。

input要素に対してdisplay:noneとしていると、キーボードでinput要素をたどる時にスキップされてしまうので、sr-onlyと同様に要素を隠す処理が必要です。

See the Pen wvGvVyR by keiko (@kamekon) on CodePen.dark

複数のinput要素に対してラベルの代替になるものを付ける

input要素が複数に分かれているものは、見出しはlegend要素でマークアップ、legendと複数のinput要素をfieldset要素で囲むことで、両者の関連付けを行うことが出来ます。

<fieldset>
  <legend>tel</legend>
  <input type="tel"> - <input type="tel"> - <input type="tel">
</fieldset>

table要素等でマークアップしてあり、fieldset要素が使えない場合は、role=”group”属性を付けたタグで複数のinput要素を囲み、aria-labelledby属性に見出しにつけたidを指定します。

<dl>
  <dt><label id="tel">tel</label></dt>
  <dd>
    <div aria-labelledby="tel" role="group">
      <input type="tel"> - <input type="tel"> - <input type="tel">
    </div>
  </dd>
</dl>

または、複数のinput要素全体のためのラベルにid属性を設定し、各々のinput要素にaria-describedby属性を付与してidを指定します。

<dl>
  <dt><label id="tel">tel</label></dt>
  <dd>
    <input type="tel" aria-describedby="tel"> - <input type="tel" aria-describedby="tel"> - <input type="tel" aria-describedby="tel">
  </dd>
</dl>

画像のマークアップ

alt属性に不要なテキストは入力しない

img要素のデフォルトのrole属性は”img”になっていますが、alt属性を空にするとrole属性は”presentation”になり、役割がない状態となるため、スクリーンリーダーで「画像」と読み上げられなくなります。すなわち、画像自体に重要な意味がない場合は、img要素のalt属性は単純に空にしておけば良いことになります。

figcaption要素で画像キャプションがマークアップされている場合も、alt属性は空にします。少なくとも、キャプションと全く同じテキストを入れることはありません。キャプション以上に詳細な説明を要する場合(グラフやフローチャート等)は別途対応します。

以前、ある企業のコーディング規約の中に「キャプションがない画像は、一番近くの見出しのテキストをalt属性として入れる」というものがあって、驚いたことがあります。この規約に沿って制作されたページをスクリーンリーダーユーザーが閲覧したら、同じテキストが何度も無闇に読み上げられることになってしまいます。

この企業ではSEO対策に力を入れていたため、SEOのコンサルから指導を受けてそのようにしていたのかもしれません。別の企業に勤めていた知人が、SEO対策の出来不出来を機械的に計測するツールで高得点を出すために(本末転倒w)、すべてのalt属性になんらかのテキストを入れるようにと言われて困惑していたことがあったので。

上記のことはアクセシビリティ的には完全にアウトな措置ですが、画像のalt属性として適切でないものを挿入することがSEO的に効果があるとも思えません…

レスポンシブイメージ

スマホとPCで表示する画像が異なる場合はpicture要素を使います。

See the Pen VwaYrwO by keiko (@kamekon) on CodePen.dark

PC用とスマホ用とそれぞれ別のimg要素を配置し、cssで表示を切り替える、という方法もありますが、本来一つであるべき要素が2つあるのはおかしいですし、それぞれのimg要素に全く同じaltテキストを書き入れるのも、運用上良くない気がします。

リンク要素の見た目に気をつける

リンクテキストはそうであることがわかるようにする

具体的には、デフォルトの仕様通り下線を付けておくのが無難かと思います。テキストの色のみが通常テキストと異なり、下線がないデザインが支給されることがありますが、そういう場合はデザイナー等に確認した上で下線をつけるようにしています。
リンクテキストの色が異なる場合は、背景色とのコントラスト比に問題がないかどうかも確認したほうが良いでしょう。

ボタンはボタンらしく

リンクボタンであることがわかるようなデザインになっていない場合は、デザイナー等に確認した上で、矢印アイコンを付けるなどの対応をします。hoverエフェクトがあればリンクボタンであることがわかりやすいので、特に指定がなくても簡単なエフェクトを付けるようにします。

a:focusにスタイルを設定する

キーボードでリンクを移動している時に現在位置がわかるように、a:focusにもスタイルを設定します。a:hoverと同じスタイルにするか、outline: noneではない状態にしておけば良いと思います。

同一URLのa要素は1ページあたり1つとする

記事一覧ページなどで、1つの記事に対して、サムネイル画像と記事タイトル等2箇所にリンクがついていると、スクリーンリーダーで読み上げ時に、同じリンク先が2回読み上げられることになります。記事タイトルのみに設定するか、記事情報全体に対してリンクを設置するようにしたほうが無難かと思います。

リンクテキストがリンク先の内容を表すようにする

スクリーンリーダーにはリンク要素のみをたどる機能があるのですが、その場合、リンク先の具体的な内容がリンクテキストに含まれていない状態(リンクテキストが「こちら」のみである等)だと、周囲のテキストを拾い直してリンク先の情報を確認しなければならなくなってしまいます。a要素でマークアップするテキストには必ずリンク先の情報を含めるようにします。

記事一覧などによく設置される「続きを読む」というリンクには、title属性に記事タイトルを入れるか、「続きを読む」というリンクを削除して記事タイトルそのものをa要素でマークアップする等の対応が必要です。

デバイスフォントの見た目に気をつける

フォントサイズの単位はremとする

CSSでフォントサイズの単位にpxを使用していると、ブラウザの設定でフォントサイズを変更しても、その箇所はフォントサイズが変わりません。

remはルート要素(html)のフォントサイズを基準としているため、ブラウザのフォントサイズの設定が変わる(=ルートのフォントサイズが変わる)と、それに準じてページ内の要素のフォントサイズが変わります。また、emや%と異なり、ルート以外の親要素の影響を受けないため、制御しやすいというのもremを採用する理由です。

ルートのフォントサイズは指定しない

html { font-seiz: 62.5% } とし、ルートのフォントサイズを10pxとした上で(殆どのブラウザのデフォルトフォントサイズは16pxなので)、以降のフォントサイズを16pxにしたいときは1.6remと記述するやり方が流行ったことがありました。ですが、ルートのフォントサイズを指定してしまうと、ブラウザの設定でフォントサイズを変更した際にユーザーの意図通りの大きさにならない可能性があります。

大抵のデザインカンプではpx単位でフォントサイズが指定されています。ルートのフォントサイズを指定せず、かつremを使用しつつデザイン通りのフォントサイズで表示するとなると、単純な数値では表現できなくなってしまいます。ですので、Sassのmixinを使用して記述するのが無難カモ。

@mixin rem($size, $base: 16) {
  font-size: ($size / $base) + rem;
}
body {
  @include rem(14);
}
//compiled css
body {
  font-size: 0.875rem;
}

デザインカンプ上で小さすぎるテキストはそれなりの大きさに変更する

デバイスサイズの2倍幅でデザインを作成していながら、ボーダー幅やフォントサイズは何故か1倍となっており、デザインどおりに画面を作成すると文字が極端に小さくなってしまう、ということがたまにあります。明らかにデザイナーのミスなので、こういうときは相談もせず、こちらの判断でテキトーな文字サイズに直してしまいますよ。

とはいえ全体のバランスが微妙に崩れてしまいがちなので、デザイナーの皆さん、気をつけてね。ちなみにAdobeXDでカンプを制作するときは、2倍幅ではなく実寸で制作すればOKです(^^)/

パフォーマンス最適化

アクセシビリティを高める目的は、万人に対してアクセシブルなサイトとすることなので、パフォーマンス最適化を行って高速化を図ることもアクセシビリティ対応の一部かと思います。パフォーマンス最適化のキホンは「クリティカル・リソース」の読み込みを早くすることなので、対応としては「余計なコトを書かない、圧縮する、少なくする」に尽きます。

用意するソースに対して行うのは以下のようなことでしょうか。効果としては微々たるものかと思いますが。

  • 無駄なタグは設置しない
  • タグのネストを深くしない
  • cssの不要な記述は削除する(コメント等を残しておきたい場合もあるので、Sassなどプリプロセッサを利用するのがよいカモ。そして可能ならモードはcompressedにする)
  • cssセレクタのネストを深くしない
  • webpack等でjsファイルも圧縮する
  • 画像の遅延読み込みを利用する
  • 画像のファイルサイズをできるだけ小さくする
  • Webフォントはできるだけ使わない。とはいえよりによって本文全体トカに使用することを指定してくるヒトがとても多いのが現実で、不本意ながら使っている場合が多い(O_O) ほんの一部分のみに使うときはサブセット化する。
//google fontsのサブセット化
<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap&text=使う文字" rel="stylesheet">

まったくもって余談ですが、以前、とあるデザイナーのプロモーションサイトをマークアップしたときのこと。メインビジュアル内にビミョーな感じの英語のリード文(「ワタシはクライアントのためにガンバリます」みたいな、直訳すると割と陳腐に聞こえる感じの文章)があったのですが、「これは単なる飾り」と判断してSVG画像として表示させることにしました。ところが、そのデザインカンプを仕上げたデザイナー本人が、ココはどーしてもWebフォントにしてくれと言うので…そのヒトの顧客に英語ネイティブはいませんし、いたとしてもこのビミョーな英文が検索ワードとして引っかかってしまうのはむしろ恥ずかしいしことだし、テキストとしてマークアップする意味は著しく皆無だなーとおもいつつ、まー、仕方なく従いましたw。

Webフォントを使ったのはこの無意味なリード文だけだったので、このときこそサブセット化しておけばよかったと、今になって気づいた次第です。

アクセシビリティ対応は、新規制作時にやるのが吉

とある制作現場で「運用中のサイトにアクセシビリティ対応をしたいんで、作業時間の目安を割り出してほしい」と言われたことがありました。ヨロコんで作業項目ごとの時間を一覧にして提出したら、想定されるコストがあまりにも高くなることがわかったので、1万数千ページのサイト中の特定の数10ページのみ対応するとゆー方針になりましたw その上で「アクセシビリティ対応やってますページ」を作るとゆーのだから、厚顔無恥もイイトコです。つまりクライアントは「アクセシビリティ対応やってますページ」を作りたかっただけで、本当にアクセシビリティの向上を目指してたわけではなかったよーなんですね、そもそも。

その後、その現場からは離れたので、実際にどのような対応が行われたのかは知りませんが、筆者が聞いていた通りのことしか行わないのであれば、コストを掛けるだけ無駄であるような気がします。

既存のサイトに後から特定の対応を施すのは手間がかかりすぎるのでとてもムリ!という事にならざるを得ないので、リニューアル時などにアクセシビリティ対応の方針をしっかり固めてから制作するのがいいんじゃないかな、と思います。

つまりは、普段から意識して「フツーにやっているアクセシビリティ対応」の項目を可能な限り増やしておくのがいいのではないかなーと思います。