Visually Hiddenパターンでアクセシビリティに配慮したマークアップを意識する
React Testing Library や Chrome の開発者ツールでアクセシビリティツリーが見えるようになるなど、最近はただ見た目通りにマークアップするだけではなく、アクセシビリティに配慮したマークアップも必要なスキルセットになってきている流れを感じます。
筆者といえば、最近HTMLやCSSをよく書くようになり、マークアップ完全に理解したからマークアップなんも分からんとなっている状況です。(アクセシビリティも同様)
今回はマークアップなんも分からんといった状況の筆者が、アクセシビリティに配慮したマークアップを意識するといった内容になります。HTML、CSS、アクセシビリティは難しいので鵜呑みにせずに適宜MDNなどのドキュメントをご確認ください。
INDEX
例えばチェックボックスを装飾したい
無性にチェックボックスを装飾したいとき、あると思います。
例えばこんな感じのチェックボックスですね。
inputタグのチェックボックスではできる装飾が限られているので、一定以上の装飾を施したいとなったら、疑似要素や他の要素に施した装飾でチェックボックスの擬似的な見た目を作り、input [type=”checkbox”] を隠すことが多いと思います。
この時点での開発者ツールのアクセシビリティツリーはこのようになっています。
※Chrome開発者ツールのアクセシビリティツリーの使用方法は こちら を参照
チェックボックスの装飾も終わったので、 input[type=”checkbox”] は display: none で隠すとしましょう。
いい感じですね。今夜は祝杯するしかない。
ここで開発者ツールのアクセシビリティツリーをもう一度確認してみます。
お分かりいただけたでしょうか。
チェックボックスがアクセシビリティツリー上からいなくなっています。
display: none の要素をスクリーンリーダーなどの支援技術は読み上げてくれない
CSSで display: none とした要素をスクリーンリーダーは読み上げてくれません。
直訳すると 表示:なし としてるんだから当然といえばその通り。
視覚的に隠したいけど、スクリーンリーダーには読み上げて欲しい
こういった時に役立つ Visually Hidden というパターンがあります。
Bootstrapや主要なCSSフレームワークでもユーティリティクラスとして提供されているようですね。
色々と流儀はあるようですが概ねこんな感じのCSSです。
.visually-hidden {
clip: rect(0 0 0 0);
clip-path: inset(50%);
width: 1px;
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
}
VoiceOverが読み上げるにはwidth, heightが1px以上必要で、
また white-space: nowrap を指定しないことでスクリーンリーダーがスペースをつなげて不自然な読み上げ(※1)をしてしまうことがあるようです。
※1 参考:Beware smushed off-screen accessible text
display: none を Visually Hidden に置き換える
inputタグに指定していた display: none を削除して、 .visually-hidden クラスをinputタグに指定するようにします。
見た目は display: none と変わりありません。
アクセシビリティツリーにもチェックボックスが表示されるようになりました。
今回のケースではここまで話題に挙げていませんでしたが、 display: none で隠したinput要素にはフォーカスが当たらなくなります。
筆者はフォームなどではキーボードのタブ移動を使用することが多いのですが、タブ移動ができないフォームはちょっとしたストレスです。
今回のケースでは Visually Hidden パターンを使用することで、タブ移動時にフォーカスが当たるようになる副次的なメリットもあります。
さいごに
今回実装したチェックボックスのコードを記載しておきます。
※create-viteを使用して作成した Vite + React + TypeScriptを使用
App.tsx
import { FC } from "react";
import './App.css'
export const App: FC = () => {
return (
<div className="App">
<label htmlFor="c-01" className="checkbox-wrapper">
<input type="checkbox" id="c-01" name="checkbox" className="visually-hidden" />
<span className="icon" />
<span className="label">Alice</span>
</label>
<label htmlFor="c-02" className="checkbox-wrapper">
<input type="checkbox" id="c-02" name="checkbox" className="visually-hidden" />
<span className="icon" />
<span className="label">Bob</span>
</label>
<label htmlFor="c-03" className="checkbox-wrapper">
<input type="checkbox" id="c-03" name="checkbox" className="visually-hidden" />
<span className="icon" />
<span className="label">Charlie</span>
</label>
</div>
)
}
App.css
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
}
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: 0;
overflow: hidden;
clip: rect(0 0 0 0);
clip-path: inset(50%);
white-space: nowrap;
border-width: 0;
}
label {
cursor: pointer;
}
.App {
display: flex;
flex-direction: column;
gap: 10px;
}
.checkbox-wrapper {
display: inline-flex;
gap: 15px;
font-size: 1.4rem;
}
.icon {
position: relative;
display: inline-block;
width: 20px;
height: 20px;
}
.icon::before,
.icon::after {
position: absolute;
content: "";
}
.icon::before {
width: 100%;
height: 100%;
border: 2px solid #99a9c1;
border-radius: 3px;
}
.icon::after {
top: 50%;
left: 60%;
width: 10px;
height: 5px;
margin-right: -50%;
border-bottom: 3px solid #2b9cce;
border-left: 3px solid #2b9cce;
opacity: 0;
transition: 0.5s;
transform: translate(-50%, -50%) scale(4);
}
.label {
position: relative;
}
input:not(focus):focus-visible + .icon::before {
border: 2px solid #2b9cce;
}
input:not(focus):focus-visible ~ .label::before {
position: absolute;
content: "";
width: 100%;
height: 100%;
border-bottom: 2px solid #2b9cce;
}
input:checked + .icon::after {
opacity: 1;
transform: translate(-50%, -50%) rotate(-45deg);
}
アクセシビリティに配慮することで、スクリーンリーダーのような支援技術が必要な方だけでなく、タブ移動をする方など全ての方に体験がよい(アクセシブルな)マークアップを日頃から意識していきたいですね。
この記事を書いた人
- 2013年にアーティスに入社。システムエンジニアとしてアーティスCMSを使用したWebサイトや受託システムの構築・保守に携わる。環境構築が好き。
関連記事
最新記事
FOLLOW US
最新の情報をお届けします
- facebookでフォロー
- Twitterでフォロー
- Feedlyでフォロー