Appearance
HTMLガイドライン
🔰 当ドキュメントは「コーディングガイドライン」の一部です。 基本的なガイドライン・ルールについては先にそれから確認してください。
🎯 HTMLガイドラインの目的
HTMLのガイドラインは次の3つを主な目的として規定します。
- セマンティック・アクセシビリティ
- コンテキスト(文章などの前後の脈絡・文脈)によって最適なマークアップができること
- 普遍的でアクセシブルなマークアップができること
- 一貫性
- 一貫したルールがあることで個人の好みや矜持による品質のバラツキを抑えること
- プロジェクトをまたいでも混乱が少ないこと
- メンテナンス性
- デザインの変更に対して柔軟で応用が効く汎用的な作りであること
- コンポーネントの単位で影響範囲を把握しやすく管理できること
HTMLの品質を支える規格
次の規格やガイドラインを参考に最適な実装をしていきます。
HTML Living Standard
原則としてHTML Living Standardの規定は例外なく従います。
WAI-ARIA
WAI-ARIAはWAI-ARIA 1.2を基本に、ブラウザや支援技術の実装状況を鑑みながら判断します。
WCAG (Web Content Accessibility Guideline)
WCAG 2.2の4原則(知覚可能、操作可能、理解可能、堅牢性)に基づいてマシンリーダブルなマークアップをします。
APG (ARIA Authoring Practices Guide)
ARIA Authoring Practices Guide (APG)を参考にUIを実装します。特にARIA属性やキーボードの操作については特別な理由がない限りこれに則って実装します。
💅 コードスタイル
editorconfig、Markuplint、pug-lint、Prettier それぞれに設定されているルールに則って記述します。エディタやコマンド実行時に警告が出た場合は必ず修正してください。
各設定は以下のパッケージを利用しています。
リントエラーについて
例外なく必ずリントエラーを修正してください。リンターのルールが現状にそぐわない場合はルールの見直しを行ってください。
改行・インデントのルール
原則としてネストしたタグは行を落としてインデントしてください。親子・兄弟関係を明確にし、エディタのコード折りたたみ機能が有効になりコードが読みやに繋がります。ただし要素のスタイルがインライン(inline
inline-block
など)で、前後のホワイトスペースがレンダリングに影響を及ぼす可能性のある箇所についてはこの限りではありません。
ルールの例
html
<!-- ✅ 良い例 -->
<ul>
<li>apple</li>
<li>orange</li>
<li>banana</li>
</ul>
<!-- ❌ 悪い例: インデントされていない -->
<ul>
<li>apple</li>
<li>orange</li>
<li>banana</li>
</ul>
<!-- ❌ 悪い例: インデントされている箇所とされてない箇所がある -->
<ul>
<li>apple</li>
<li>orange</li>
<li>banana</li>
</ul>
<!-- ✅ 良い例: インラインの要素は改行とインデントの必要はない -->
<div>
<p>This apple is <strong>red</strong></p>
</div>
<!-- ✅ 良い例: インラインの要素は改行とインデントの必要はない -->
<ul>
<li><a href="/apple/">apple</a></li>
<li><a href="/orange/">orange</a></li>
<li><a href="/banana/">banana</a></li>
</ul>
<!-- ❌ 悪い例: 明らかにa要素はインラインでない -->
<a href="/path/to/link"><div>
<img src="/path/to/image.png">
<p>lorem...</p>
</div></a>
<!-- ✅ 良い例: 明らかにa要素はインラインでない -->
<a href="/path/to/link">
<div>
<img src="/path/to/image.png">
<p>lorem...</p>
</div>
</a>
🔧 自動修正可能
このルールはPrettierによって自動修正されます。
タグのルール
- タグ名や属性名は小文字に統一します
- 空要素の閉じスラッシュをつけます
- 閉じタグは省略しません
ルールの例
タグ名や属性名は小文字を使用します。
html
<!-- ✅ 良い例 -->
<a href="/path/to/link">...</a>
<!-- ❌ 悪い例 -->
<a href="/path/to/link">...</a>
ただし SVG(インラインSVGも同様)は、属性名の大文字小文字を区別するため、仕様に従います。
html
<!-- ✅ 良い例 -->
<svg viewBox="0 0 400 300">...</svg>
<!-- ❌ 悪い例 svg要素は正しく大文字小文字を指定しないと動作しない -->
<svg viewbox="0 0 400 300">...</svg>
空要素の閉じスラッシュは記述します。本来必要ありませんがPrettierの挙動に従います。
html
<!-- ✅ 良い例 -->
<img src="/path/to/image.png" />
<!-- ❌ 悪い例 -->
<img src="/path/to/image.png">
閉じタグの省略はしません。
html
<!-- ✅ 良い例 -->
<ul>
<li>apple</li>
<li>orange</li>
<li>banana</li>
</ul>
<!-- ❌ 悪い例 -->
<ul>
<li>apple
<li>orange
<li>banana
</ul>
🔧 自動修正可能
このルールはPrettierによって自動修正されます。
属性の引用符ルール
属性値はダブルクォーテーション(二重引用符)を使用します。
ルールの例
html
<!-- ✅ 良い例: 属性の引用符にダブルクォーテーションを使用している -->
<a href="/path/to/link">...</a>
<!-- ❌ 悪い例: 属性の引用符にシングルクォーテーションを使用している -->
<a href='/path/to/link'>...</a>
<!-- ❌ 悪い例: 属性に引用符を使用しない -->
<input type=text />
🔧 自動修正可能
このルールはPrettierによって自動修正されます。
属性の値のルール
論理属性(値が不要の属性)に値を記述しません。
html
<!-- ✅ 良い例 -->
<input type="checkbox" checked />
<!-- ❌ 悪い例 -->
<input type="checkbox" checked="checked" />
デフォルトの属性値が決まっていて省略可能な属性は省略します。
html
<!-- ✅ 良い例 -->
<script src="/path/to/script.js"></script>
<!-- ❌ 悪い例: script要素のtype属性は省略できる -->
<script type="text/javascript" src="/path/to/script.js"></script>
Pugでの属性指定
- id属性は
#
リテラルを利用します - class属性は
.
リテラルを利用します - 指定は次の順番で記述します
- id属性
- class属性
- class/id以外の属性
pug
//- ✅ 良い例
div#id-name.c-class-name(data-attr="value")
//- ❌ 悪い例: 順番どおりでない、そしてclass属性にドットリテラルを使っていない。
div(data-attr="value" class="c-class-name")#id-name
🔧 自動修正可能
このルールはPrettierとpug-lintによって自動修正されます。
文字参照
次に挙げる文字は文字参照にします。
文字 | 文字参照 |
---|---|
< | < |
> | > |
& | & |
" | " |
上記以外の文字は、可読性の観点から文字参照にする必要はありません。コピーライトマークは©
でなく©
にします。
属性の文字列内でも文字参照に統一します。
html
<!-- ✅ 良い例: &が文字参照になっている -->
<a href="/path/to/link?key=val&key=val">...</a>
<!-- ❌ 悪い例: &が文字参照になっていない -->
<a href="/path/to/link?key=val&key=val">...</a>
Pugでの属性値の文字参照
Pugでは属性値の文字参照は自動で行われるため、手動で記述する必要はありません。
pug
//- 入力
a(href="/path/to/link?key=val&key=val")
html
<!-- 出力結果 -->
<a href="/path/to/link?key=val&key=val"></a>
コメント
html
<!-- ✅ 良い例: ハイフンとコメント文の間に最低1つはスペースか改行を入れる -->
<!-- コメント -->
<!-- ❌ 悪い例: ハイフンとコメント文の間にスペースがない -->
<!--コメント-->
🤔 コメントを書くかどうか
HTMLに記述したコメントは、製品ソースコード上に残るためエンドユーザが見える状態になります。 そのため、なるべく不用意なコメントは控えたほうがよいです。 CMSへの組み込みなどでメモ代わりに利用する場合は、組み込みの際に消してもらえるように依頼をしてください。
また、PugではHTMLに変換した際に削除されるコメント記法があるので、そういったものを積極的に活用してください。
📂 ファイル構成
ファイルは以下の構成で管理します。
# 開発ソースコード
📂 __assets/
├── 📂 htdocs/
│ ├── 📂 __tmpl/
│ │ ├── 000_home.pug
│ │ ︙
│ │ └── 302_form_complete.pug
│ ├── 📂 sub-dir/
│ │ ├── sub01.pug
│ │ ︙
│ │ └── sub99.pug
│ ├── index.pug
│ └── maintenance.html
└── 📂 _libs/
├── 📂 component/
│ ├── header.pug
│ └── footer.pug
├── 📂 data/
│ ├── helper.js
│ └── index.yaml
└── 📂 mixin/
└── meta.pug
# 製品ソースコード
📂 htdocs/
├── 📂 __tmpl/
│ ├── 000_home.html
│ ︙
│ └── 302_form_complete.html
├── 📂 sub-dir/
│ ├── sub01.html
│ ︙
│ └── sub99.html
├── index.html
└── maintenance.html
__assets/htdocs
のフォルダ構造を維持したままにドキュメントルートにあたるhtdocs
に出力されます。最終的に整形されたHTMLファイルをするため、開発ファイルはPugでもHTMLでもどちらでも構いません。
_libs/components
、_libs/mixin
フォルダはPugで利用する断片要素、_libs/data
フォルダはPugから参照できるオブジェクトや関数を管理します。
🍴 プリプロセッサー・コンパイル環境
@d-zero/builder
を通してPugからHTMLへの変換を行います。
製品ソースコードの納品要件によっては、改行コードや文字コードの変換が必要なケースがあります。その場合はeleventy.config.cjs
に変換オプションを追加してください。
js
module.exports = function (eleventyConfig) {
if (process.env.NODE_ENV === 'production') {
eleventyConfig.addGlobalData('prettier', true);
eleventyConfig.addGlobalData('minifier', { minifyJS: false });
eleventyConfig.addGlobalData('lineBreak', '\r\n');
eleventyConfig.addGlobalData('charset', 'shift_jis');
eleventyConfig.addGlobalData('pathFormat', 'preserve');
}
return eleventy(eleventyConfig);
};
変換オプション
eleventyConfig.addGlobalData
にオプションを渡します。
prettier
Prettierによる整形を行います。デフォルトはtrue
です。
minifier
HTMLMinifierによって最適化を行います。必要であれば設定を上書きしてください。
lineBreak
改行コードを変換します。CRLF(\r\n
)やLF(\n
)を指定してください。デフォルトはLF(\n
)です。
charset
文字コードを変換します。文字コードはUTF-8(utf8
)とShift-JIS(shift_jis
)のみ対応しています。デフォルトはUTF-8(utf8
)です。Shift-JISにするにはshift_jis
を指定し、別途iconv-lite
のインストールが必要です。
sh
yarn add -D iconv-lite
pathFormat
出力ファイルの形式を変更します。デフォルトはpreserve
です。
値 | 説明 |
---|---|
file | 各ページに対応するHTMLファイルを生成 |
directory | ディレクトリを生成しページに対応するindex.html ファイルをネスト |
preserve | ソースフォルダに表示される通りにHTMLファイルを生成 |
この設定はAstroのbuild.format
を参考にしています。
include
Pugでコンポーネントファイルなどをインクルードする場合、basedirオプションを設定することでルートパスを使ってファイルを指定できます。
変更したい場合は、eleventy.config.cjs
でオプションを設定してください。
js
eleventyConfig.setPugOptions({
basedir: path.resolve(__dirname, '__assets', '_libs'),
});
上記の設定の場合で、__assets/_libs/component/_c-header.pug
をインクルードする場合はこのような記述になります。
pug
body.c-page-sub
.c-page-sub__base
.c-page-sub__header
include /component/_c-header.pug
📜 DOCTYPE
DOCTYPEは必ず記述します。旧来の文書型宣言は使用しないでください。また、XML宣言は記述しません。
html
<!doctype html>
<html>
ただしPugの場合は自動付与されるので記述不要です。
pug
html
head
body
🔖 メタ要素
ビューポート
user-scalable
は無効なため記述しません。
html
<meta name="viewport" content="width=device-width" />
その他の必須要素
html
<!-- 数字の連続が電話番号に変換されるのを抑制する -->
<meta name="format-detection" content="telephone=no" />
🔗 パスとリンク
href
やsrc
属性に記述するパスは、原則 /
(スラッシュ)で始まるルート相対パスで記述します。 これはCMSなどの動的に生成されるページでも、どの階層からでも同じリンク先を参照できるようにするためです。
html
<!-- ✅ 良い例 -->
<a href="/path/to/link">...</a>
<!-- ❌ 悪い例 -->
<a href="path/to/link">...</a>
<a href="./path/to/link">...</a>
<a href="../path/to/link">...</a>
外部リンク
ソースコードの検索性を上げるために//
で始まるパスは避け、https://
で開始してください。
html
<!-- ✅ 良い例 -->
<a href="https://www.d-zero.co.jp" target="_blank" rel="noreferrer">...</a>
<!-- ❌ 悪い例 -->
<a href="//www.d-zero.co.jp" target="_blank" rel="noreferrer">...</a>
💎 コンポーネント
ページを構成するパーツをコンポーネントという単位で管理します。
これはスタイルシートのための規定であり、以下のような問題を未然に防ぐことを目的としています。
- クラス名のコンフリクト(重複)および予期しないスタイル上書き
- スタイル変更による子孫要素への予期しない影響
コンポーネントの構成とクラス命名規則
コンポーネントはそれ自身であるコンポーネントルートと子孫要素となるエレメントで構成されます。
BEMとの違い
基本的に似た概念をもちます。
BEMの概念 | ディーゼロの概念 |
---|---|
Block | コンポーネント |
Element | エレメント |
Modifier | 状態 |
と捉えて差し支えありません。classの命名規則はBEMの本家とは異なります。
コンポーネントルートはクラス名によって明示的に定義され、【c-
+ コンポーネント名
】というclassをもちます。 要素の種類・class以外の属性に関しては、コンポーネントに最適なものを選択しマークアップします。(👉セマンティックとアクセシビリティ)
html
<!-- "header"コンポーネントの場合 -->
<header class="c-header"></header>
コンポーネントはコンポーネントを内包することができます。
html
<!-- "header"コンポーネントの場合 -->
<header class="c-header">
<!-- ❗内包される"nav-global"コンポーネント -->
<nav class="c-nav-global"></nav>
</header>
コンポーネントルートの子孫要素は、コンポーネント以外はエレメントとなります。エレメントのclassは【c-
+ コンポーネント名
+ __
+ エレメント名
】という命名規則でつけます。※ただしこのclassはスタイルシートのために必要なもので、エレメントに対しては必ずしも必要なわけではありません。classのないエレメントが存在してもかまいません。
html
<!-- "header"コンポーネントの場合 -->
<header class="c-header">
<div class="c-header__body">
<div class="c-header__title">
<h1 class="c-header__site-name"><span>サイト名</span></h1>
<p class="c-header__description">概要文</p>
</div>
<div class="c-header__info">
<ul class="c-header__links">
<!-- ❗classのないエレメント -->
<li>
<a href="/about-us/"><span>我々について</span></a>
</li>
<li>
<a href="/sitemap/"><span>サイトマップ</span></a>
</li>
</ul>
<a class="c-header__tel" href="tel:0000000000"><span>00-0000-0000</span></a>
</div>
<div class="c-header__nav-global-wrapper">
<!-- 内包される"nav-global"コンポーネント -->
<nav class="c-nav-global"></nav>
</div>
</div>
</header>
エレメントの命名規則については次の理由から規定されています。
- 単純な子孫セレクタでは、内包するコンポーネントへの影響をコントロールできない
- 単純な子セレクタ・子孫セレクタでは、ライブラリなどで定義される外部のクラスのコンフリクトを完全に防げない
- どのコンポーネントに帰属するか明瞭
- エレメントの詳細度の差を小さくできる
内包したコンポーネントの子孫に、自分の子孫を定義してはいけません。
html
<!-- "header"コンポーネントの場合 -->
<header class="c-header">
<div class="c-header__c-nav-global">
<!-- 内包される"nav-global"コンポーネント -->
<nav class="c-nav-global">
<!-- ✅ 良い例: これは直属のコンポーネントのエレメントなので問題ない -->
<ul class="c-nav-global__list">
...
</ul>
<!-- ❌ 悪い例: 先祖にあるコンポーネントのエレメントはここには定義できない -->
<div class="c-header__nav-element">...</div>
</nav>
</div>
</header>
1つの要素に複数のclassを定義してはいけません。コンポーネントを拡張したい場合は、完全に別のコンポーネントを作ってください。
html
<!-- ✅ 良い例 -->
<header class="c-header-specific">
<!-- Elements... -->
</header>
<!-- ❌ 悪い例 -->
<header class="c-header c-header-specific">
<!-- Elements... -->
</header>
コンポーネントとエレメントの状態管理
要素の状態(BEMでいうところのModifier)は、原則classを用いません。 次の優先順位で状態を管理します。
- 各HTML要素のもつ属性(
disabled
属性、required
属性など) - ARIA属性(
aria-expanded
、aria-readonly
など) data-*
属性
例えば、disabled
属性を有効にすれば、暗黙的にaria-disabled
も有効になる性質があるので、関連する属性とARIA属性は二重定義しないようにしてください。
html
<!-- ✅ 良い例: ネイティブの属性があるものは属性で管理する -->
<button type="button" disabled>ボタン</button>
<!-- ❌ 悪い例: 属性で済むものをaria属性やdata属性に再定義したりする -->
<button type="button" aria-disabled="true" data-disabled="true">ボタン</button>
hidden
属性の扱い
hidden
属性とaria-hidden
の意味は異なるため、同じものとして扱ってはいけません。
aria-hidden
属性は単純に支援技術に要素を隠すためのARIAステートです。しかしhidden
属性は「現在のページの状態とその要素が関連性がまだない(将来は関連性が出てくる可能性がある)」もしくは「以前は関連性があったが現在はもう関連性がない」という意味を持つためです。値をuntil-found
として自動展開可能な要素に対してはhidden
属性を利用することはもちろんできますがaria-hidden
とはこれも性質が異なるため代替にはならないことに注意してください。
HTMLの属性にもWAI-ARIAにもどちらにもない状態を管理する場合(例えばスクロール位置でヘッダーの高さが変わる)などは、data-compact-mode
のようなdata-*
属性で定義します。
html
<!-- 通常は data-compact-mode が false -->
<header class="c-header" data-compact-mode="false"></header>
js
const header = document.querySelector('.c-header');
window.addEventListener(
'scroll',
() => {
// スクロール位置が100pxを超えたらコンパクトモードにする
header.dataset.compactMode = window.scrollY > 100 ? 'true' : 'false';
},
{ passive: true },
);
❌ スタイルのみの目的に data-*
属性を利用する
data-*
属性はあくまでもJavaScriptで状態を変化させるケースで扱います。スタイルのみの目的でdata-*
属性を利用することは避けてください。
html
<!-- ❌ 悪い例 -->
<header class="c-header">
<div data-header="inner">
<div data-header="logo">サイト名</div>
</div>
</header>
<!-- ✅ 良い例 -->
<header class="c-header">
<div class="c-header__inner">
<div class="c-header__logo">サイト名</div>
</div>
</header>
粒度とコンポーネントの例
コンポーネントに分割する粒度はプロジェクトごとに異なりますが、以下の例を参考にしてください。なお、再利用できるかどうかは重要ではなく、コンポーネントが独立して完結すること、他のコンポーネントに影響を与えないことが重要です。
メインコンテンツコンポーネントのみ専用のルールがあるので、メインコンテンツのエレメントとヘルパークラスを参照してください。
コンポーネント名 | クラス名 | 望ましい対象の HTML 要素 | 備考 |
---|---|---|---|
ページ | c-page | body 要素 | トップページ・下層ページでレイアウトなどが共通している場合。全体レイアウトのスタイルはここに定義する。 |
ページ(トップページ用) | c-page-home | body 要素 | トップページ限定。全体レイアウトのスタイルはここに定義する。 |
ページ(下層ページ用) | c-page-sub | body 要素 | 下層ページ全般。全体レイアウトのスタイルはここに定義する。 |
ヘッダー | c-header | header 要素 | ページで一番外側にあるヘッダー要素。ロゴのリンクや、グロナビ以外の小さなリンク集を内包することもある。 |
フッター | c-footer | footer 要素 | ページで外側にあるフッター要素。サイトマップのようなリンクリストや組織情報などを内包することもある。 |
グローバルナビゲーション | c-nav-global | nav 要素 | サイトの全体を横断するリンクメニュー。 |
ローカルナビゲーション | c-nav-local | 特になし | 自分のページの兄弟・下層のリンクメニュー。 |
メインコンテンツ | c-content-main | main 要素もしくはarticle 要素を先祖にもつ要素 | ページの主要素。CMSで管理されるページや記事の本文にあたる部分が該当する。このコンポーネントのみ扱いが特殊になる。(👉メインコンテンツのエレメントとヘルパークラス) |
コンテンツインデックス | c-content-index | main 要素を先祖にもつ要素 | 各ページへの目次・リンク集となっているページで利用されるコンポーネント。CMSで管理されるブログ記事一覧やカテゴリーインデックス・タグインデックスなどで利用される。 |
パンくずリスト | c-nav-breadcrumbs | 特になし | Google の推奨する構造化データを利用すること。形式は microdata(schema.org)が望ましい。HTML の構造上難しければ JSON-LD を利用すると良い。 |
ステップナビゲーション | c-nav-steps | 特になし | |
ページネーション | c-pagination | 特になし | |
メインビジュアル | c-hero | section | |
検索フォーム | c-search | 特になし |
コンポーネント化すべきでないパーツや要素
再利用よりも他の要素への影響がないことを優先するために、次に挙げるパーツ・要素はコンポーネント化を避けてください。
- ボタン単体
- フォームコントロール要素(テキストフィールド・ラジオボタン・セレクトボックスなど)単体
コンポーネントごとに微妙なサイズ感やインタラクションがあることが多いのでコンポーネントとして独立は避け、コンポーネントのエレメントとして扱ってください。同じデザインでも再利用よりも他への影響がないことを優先してください。
html
<header class="c-header">
<button class="c-header__btn">ボタン</button>
</header>
<footer class="c-footer">
<button class="c-footer__btn">ボタン</button>
</footer>
コンポーネント・エレメントの命名規則
クラス名は、ファイル名に関係するので機能から検索・サジェスト・ソートしやすいように、[機能名]-[修飾語・詳細]-[連番]
という組み合わせで命名します。文法上は不自然かもしれませんが機能性を優先させます。[機能名]
以外は任意です。それぞれの単語や形容詞はコーディング全般に関わる識別子の命名規則に準じてください(👉識別子の命名規則)。
機能名の例
page
header
footer
nav
修飾語・詳細が付いた例
nav-global
nav-local
nav-breadcrumbs
nav-steps
page-home
page-sub
page-contact
連番が付いた例
selection-01
selection-02
📖 メインコンテンツのエレメントとヘルパークラス
メインコンテンツ(content-main
コンポーネント)の内容はCMSなどで管理されるHTMLを含んだり、ページ独自のスタイルを扱うことが多いので例外的に専用のルールを設けます。
html
<div class="c-content-main">
<!-- ❗この部分がメインコンテンツ専用のルール -->
</div>
エレメントのクラスの例外とクラス追加のルール
メインコンテンツのエレメントは基本的に自由です。ただし、クラス名はc-
で開始しないでください。また、メインコンテンツの中にコンポーネントを内包してはいけません。
html
<div class="c-content-main">
<!-- ✅ 良い例 -->
<div class="foo">...</div>
<!-- ❌ 悪い例 -->
<div class="c-content-main__foo">...</div>
<!-- ❌ 悪い例 -->
<div class="c-foo">...</div>
</div>
🆔 id属性の利用
id
属性はスタイルシートの目的では指定しないでください。
🤔 セマンティックとアクセシビリティ
各要素のコンテンツモデルを理解し、コンポーネントや機能、文脈によって最適なタグを選択してください。
WAI-ARIA
WAI-ARIAをHTMLに付加することで、HTMLでは不足している要素のセマンティックを補うことができます。しかし、WAI-ARIAが必要なケースは多くなく、HTML標準では再現できないコンポーネントを作成するときのみ使用してください。その場合、ARIA Authoring Practices Guide (APG)を参考にして、インタラクションの要件や振る舞いを推奨されているものに近い実装をするようにしてください。
- 🙆 WAI-ARAIを使う必要があるケース
button
要素がaria-pressed
やaria-expanded
などの状態をもつ必要がある場合- タブやカルーセルなどHTMLにないコンポーネントを扱う場合
- 🙅 WAI-ARIAを使う必要がないケース
- HTML標準にコンポーネントが存在する場合
- Popoverなどで事済む場合
画像と代替テキスト
代替テキストはWCAGの達成基準 1.1.1 非テキストコンテンツを基準に考えてください。同等の目的が重要となるため、コーディングではなくデザインの段階で代替テキストが決まるように計画を立ててください。
alt
属性が空の場合のロール
alt
属性を空にすると、role="none"
(またはrole="presentation"
)を付与したことと同じ効果が得られます。そのためalt
属性が空の場合はrole="none"
を付与する必要はありません。
html
<!-- ❌ role="none"は不要 -->
<img src="/path/to/file.png" alt="" role="none" />
img
要素の性質を理解する
img
要素の扱いでは次のことに注意してください。
画像の出し分け
display: none
の状態でも画像リソースのリクエストはされます( loading="lazy"
になっている場合を除く)。そのため、レスポンシブデザインで画像を出し分ける場合は picture
要素をつかってください。
html
<!-- ✅ 良い例 -->
<picture>
<source srcset="/path/to/pict-some@xs.jpg" media="(max-width: 575px)" />
<img src="/path/to/pict-some.png" alt="代替テキスト" />
</picture>
<!-- ❌ 悪い例 -->
<img class="sp-only" src="/path/to/pict-some@xs.png" alt="代替テキスト" />
<img class="pc-only" src="/path/to/pict-some.png" alt="代替テキスト" />
レイアウトシフトを発生させない
width
属性とheight
属性を指定して縦横比を明示しないといけないケース- 親要素に依存せず画像を成り行きで表示させる場合
- 親要素によって幅は変わるが、高さが画像によって可変する場合
- 明示しなくていいケース
- CSS で
width
とheight
を同時に指定している場合(ただしheight
がauto
でない) - CSS で
width
かheight
のどちらかを指定していて、且つaspect-ratio
を指定している場合 - 幅と高さが親要素に完全に依存して、CSS の
object-fit
を使っている場合
- CSS で
遅延デコード・遅延読み込みの設定
decoding
属性はブラウザが適宜最適化しているため、指定しない- ファーストビュー以下の
img
要素にはなるべくloading="lazy"
を指定して、表示領域にない要素の自動ダウンロードを避ける
見出しとアウトライン
見出し要素のみでアウトラインを構成してもよいですし、セクショニング・コンテンツを利用してアウトラインを構成してもよいです。
html
<!-- ✅ 見出し要素のみでアウトラインを構成する -->
<h1>大見出し</h1>
<div>大見出しのコンテキスト</div>
<h2>中見出し</h2>
<div>中見出しのコンテキスト</div>
<h3>小見出し</h3>
<div>小見出しのコンテキスト</div>
<!-- ✅ セクショニング・コンテンツを利用した場合 -->
<h1>大見出し</h1>
<div>大見出しのコンテキスト</div>
<section>
<h2>中見出し</h2>
<div>中見出しのコンテキスト</div>
<section>
<h3>小見出し</h3>
<div>小見出しのコンテキスト</div>
</section>
</section>
見出しレベルの注意点
html
<!-- ❌ 廃止されたアウトラインアルゴリズムでランクを作る -->
<h1>大見出し</h1>
<div>大見出しのコンテキスト</div>
<section>
<h1>中見出し</h1>
<div>中見出しのコンテキスト</div>
<section>
<h1>小見出し</h1>
<div>小見出しのコンテキスト</div>
</section>
</section>
<!-- ❌ 見出しレベルのスキップ -->
<h1>大見出し</h1>
<div>大見出しのコンテキスト</div>
<section>
<h6>中見出し</h6>
<div>中見出しのコンテキスト</div>
<section>
<h4>小見出し</h4>
<div>小見出しのコンテキスト</div>
</section>
</section>
ランドマーク
各コンポーネントの先祖となる要素にはランドマークを設けること。もしくはコンポーネントルート自体をランドマークをもつ要素でマークアップしてください。
html
<body>
<header><!-- header は banner ランドマークをもつ --></header>
<nav><!-- nav は navigation ランドマークをもつ --></nav>
<main><!-- main は main ランドマークをもつ --></main>
<footer><!-- footer は contentinfo ランドマークをもつ --></footer>
</body>
html
<body>
<header><!-- ... --></header>
<div class="c-hero">
<!-- ❌ どのランドマークにも属していない -->
</div>
<nav><!-- ... --></nav>
<main>
<div class="c-hero">
<!-- ✅ mainランドマークに内包されている -->
</div>
</main>
<footer><!-- ... --></footer>
</body>
重複するランドマークロールの注意
ドキュメント内に複数のランドマーク要素がある場合、明示的にアクセシブルな名前を設定してください。
html
<body>
<header>
<nav aria-label="メインメニュー"><!-- ... --></nav>
</header>
<main>
<nav aria-label="ページ内メニュー"><!-- ... --></nav>
</main>
<footer>
<nav aria-labelledby="sitemap">
<h2 id="sitemap">ページ一覧</h2>
<!-- ... -->
</nav>
</footer>
</body>
p
要素を濫用しない
p
要素は段落を表しますが、他の要素で補えるテキストであればp
要素を使う必要はありません。また、テキストや文字の代替画像でないかぎり画像単体をp
要素で囲うのは不適切と言えます。
html
<!-- ❌ 悪い例 -->
<section>
<h2>見出し</h2>
<!-- 文章テキストの代替画像でなければ"段落"にする必要はない -->
<p><img src="/path/to/file.png" alt="画像" /></p>
<p>〜コンテキスト〜</p>
</section>