【アクセシビリティ】アクセシビリティを意識したランドマークの作り方

## はじめに
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390720/cea3dd0b-3d5f-69e9-cbe4-7b03212dab78.png)

みなさんアクセシビリティを意識して開発できていますか？

必要なところに`role属性`を記述したり、`tabキー`でフォーカスができるようにしたりなど、意識しないといけないことも多いです。
そのため、アクセシビリティを完璧にやろうとするのは一苦労です。

ただ、コンポーネントごとに区切って、アクセシビリティを理解しておけば、実装するタイミングに思い出しやすく、アクセシビリティも意識しやすいと思います。

そのため、この記事では「**ランドマーク**」に焦点を当てて、アクセシビリティを意識したランドマークの実装方法とランドマークで意識した方がいいアクセシビリティを解説しようと思います。

## アクセシビリティを意識したランドマークの仕様
### ⚪︎ ランドマークとは？
ランドマークは、ページの主要なセクションを区別するための8つの要素です。

各ランドマークは、支援技術を利用しているユーザーへ、配置・間隔・色・枠線の視覚的に伝えられるページ構造の特徴の始まりと終わりを知覚させることができます。
また構造を伝えるだけでなく、ブラウザや支援技術がページのセクション間で効率的なキーボードナビゲーションできるようにしています。

ランドマークロールは、以下の8つあります。

+ **Main**
+ **Navigation**
+ **Search**
+ **Banner**
+ **Contentinfo**
+ **Complementary**
+ **Form**
+ **Region**

#### 1. Mainランドマーク
Mainランドマークは、ページの中で、主要なコンテンツを区別するために使われるランドマークです。

Mainのランドマークは、各ページに１つ以上必要になります。
また、複数のMainランドマークがあるページは、それぞれにラベルが必要です。

```sample.html
<main aria-labelledby="title">
  <h1 id="title">タイトル</h1>
  <!-- Main コンテンツ -->
</main>
```

#### 2. Navigationランドマーク
Navigationランドマークは、サイト内かページ内のコンテンツをナビゲーションするコンテンツを区別するために使われるランドマークです。
ページに複数のナビゲーション・ランドマークがある場合は、それぞれに固有のラベルをつける必要があります。

```sample.html
<nav aria-labelledby="nav">
  <h2 id="nav">タイトル</h2>
  <ul>
    <!-- ナビゲーション コンテンツ -->
  </ul>
</nav>
```

#### 3. Searchランドマーク
Searchランドマークは、サイト全体のコンテンツへの検索機能を作り出すためのコンテンツを区別するために使われるランドマークです。

フォームランドマークを検索機能に使用する場合は、フォームランドマークの代わりにランドマークを使う必要があります。
また、ページに複数の検索ランドマークがある場合は、それぞれに固有のラベルを付ける必要があります。

```sample.html
<form role="search">
  <input type="search" aria-label="search text" size="20">
  <input type="submit" value="Search">
</form>
```

#### 4. Bannerランドマーク
Bannerランドマークは、サイト内の各ページの冒頭になるサイトの思考のコンテンツを区別するために使われるランドマークです。
Bannerランドマークには、サイトのロゴやアイデンティティ、サイト固有の検索ツールなどが含まれます。

```sample.html
<header>
  <h1>タイトル</h1>
  <!-- コンテンツ -->
</header>
```
<font color="gray">※ `<header>` が 以下の要素の子孫要素の場合、Bannerランドマークとはみなされません。
`<article>`・`<aside>`・`<main>`・`<nav>`・`<section>`</font>

#### 5. Contentinfoランドマーク
Contentinfoランドマークは、サイト内の各ページの下部にある共通のコンテンツを識別するために使われるランドマークです。
一般的には、「footer」と言われます。

```sample.html
<footer>
  <h2>タイトル</h2>
  <!-- コンテンツ -->
</footer>
```

<font color="gray">※ `<footer>` が 以下の要素の子孫要素の場合、Contentinfoランドマークとはみなされません。
`<article>`・`<aside>`・`<main>`・`<nav>`・`<section>`</font>

#### 6. Complementaryランドマーク
Complementaryランドマークは、メインコンテンツを補完・補助するためのコンテンツを識別するために使われるランドマークです。
Complementaryランドマークは、他のランドマークの子要素にならず、メインコンテンツと関連していない場合は、`role="region"` を指定する方がよい。

```sample.html
<aside aria-labelledby="comp">
  <h2 id="comp">タイトル</h2>
  <!-- コンテンツ -->
</aside>
```

#### 7. Formランドマーク
Formランドマークは、フォームを区別するために使われるランドマークです。

Formランドマークは、ユーザがフォームの目的を理解するための可視ラベルが必要です。
また、Formランドマークに含まれるコンテンツは、可能な限り以下の要素が望ましいです。
`Button`・`input`・`select`・`textarea`

```sample.html
<form aria-labelledby="contact">
  <fieldset>
    <legend id="contact">タイトル</legend>
    <!-- コンテンツ -->
  </fieldset>
</form>
```

#### 8. Regionランドマーク
Regionランドマークは、特定の目的に関連したセクションを区別するためのランドマークです。
Regionランドマークは、ページのサマリーに載るような重要な要素に使うのが望ましいです。

また、Regionランドマークは、ラベルを持つ必要があります。

```sample.html
<main>
  <h1>タイトル</h1>
  <section aria-labelledby="region1">
    <h2 id="region1">タイトル</h2>
    <!-- コンテンツ -->
  </section>
  <section aria-label="タイトル">
    <!-- コンテンツ -->
  </section>
</main>
```

### ⚪︎ キーボードインタラクション
特になし

### ⚪︎ WAI-ARIA の役割、状態、プロパティ
上記の通り

## アクセシビリティを意識したランドマークの完成形
<p class="codepen" data-height="500" data-default-tab="result" data-slug-hash="RwvRWop" data-editable="true" data-user="sp_degu" style="height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/sp_degu/pen/RwvRWop">
  Landmark Accessibility</a> by でぐぅー | Qiita (<a href="https://codepen.io/sp_degu">@sp_degu</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## アクセシビリティを意識したランドマークの作り方
### 1. HTMLを実装する
```sample.html
<div class="wrapper">
  <header>Banner</header>
  <form role="search">
    <input type="search" aria-label="search text" size="20" value="Seach">
  </form>
  <nav>Navigation</nav>
  <main aria-labelledby="main-title">
    <h1 id="main-title">Main</h1>
    <Form>Form</Form>
    <section>Region</section>
    <section>Region</section>
  </main>
  <aside>Complementary</aside>
  <footer>Content info</footer>
</div>
```

### ２. CSSを実装する
```sample.css
body {
  background-color: #212529;
  color: #fff;
  display: grid;
  height: calc(100vh - 40px);
  margin: 0;
  padding: 20px 0;
  place-items: center;
  width: 100vw;
}

.wrapper {
  background-color: rgb(128 128 128 / .3);
  border-radius: 24px;
  padding: 16px;
  position: relative;
  width: 400px;
  background-blend-mode: luminosity;
  backdrop-filter: blur(50px);
  display: grid;
  grid-template-columns: 110px 1fr 100px;
  grid-template-rows: 30px 1fr 30px 30px;
  gap: 8px 12px;
  &::before {
    background: linear-gradient(135deg, rgb(255 255 255 / .4) 0, rgb(255 255 255 / 0) 40%, rgb(255 255 255 / 0) 60%, rgb(255 255 255 / .1) 100%);
    border: 1.4px solid transparent;
    border-radius: 24px;
    content: "";
    inset: 0;
    position: absolute;
    -webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0) border-box;
    -webkit-mask-composite: destination-out;
    mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0) border-box;
    mask-composite: exclude;
    z-index: -1;
  }
}

header {
  grid-row: 1 / 2;
  grid-column: 1 / 3;
  border-radius: 8px;
  padding: 2px 12px;
  background: linear-gradient(0deg, rgba(0, 0, 0, .1) 0%, rgba(0, 0, 0, 0.10) 100%), rgba(0, 0, 0, .5);
  background-blend-mode: luminosity, color-burn;
  box-shadow: 1px 1.5px 4px 0px rgba(0, 0, 0, 0.10) inset, 1px 1.5px 4px 0px rgba(0, 0, 0, 0.08) inset, 0px -0.5px 1px 0px rgba(255, 255, 255, 0.25) inset, 0px -0.5px 1px 0px rgba(255, 255, 255, 0.30) inset;
}

form[role="search"] {
  grid-row: 1 / 2;
  grid-column: 3 / 4;
  & input {
    border: none;
    border-radius: 8px;
    color: #ffffff;
    width: 100%;
    height: 100%;
    padding: 2px 12px;
    background: linear-gradient(0deg, rgba(0, 0, 0, .1) 0%, rgba(0, 0, 0, 0.10) 100%), rgba(0, 0, 0, .5);
    background-blend-mode: luminosity, color-burn;
    box-shadow: 1px 1.5px 4px 0px rgba(0, 0, 0, 0.10) inset, 1px 1.5px 4px 0px rgba(0, 0, 0, 0.08) inset, 0px -0.5px 1px 0px rgba(255, 255, 255, 0.25) inset, 0px -0.5px 1px 0px rgba(255, 255, 255, 0.30) inset;
  }
}

nav {
  grid-row: 2 / 4;
  grid-column: 1 / 2;
  display: grid;
  place-items: center;
  border-radius: 8px;
  padding: 2px 12px;
  background: linear-gradient(0deg, rgba(0, 0, 0, .1) 0%, rgba(0, 0, 0, 0.10) 100%), rgba(0, 0, 0, .5);
  background-blend-mode: luminosity, color-burn;
  box-shadow: 1px 1.5px 4px 0px rgba(0, 0, 0, 0.10) inset, 1px 1.5px 4px 0px rgba(0, 0, 0, 0.08) inset, 0px -0.5px 1px 0px rgba(255, 255, 255, 0.25) inset, 0px -0.5px 1px 0px rgba(255, 255, 255, 0.30) inset;
}

main {
  grid-row: 2 / 3;
  grid-column: 2 / 4;
  border-radius: 8px;
  padding: 8px 12px 12px;
  background: linear-gradient(0deg, rgba(0, 0, 0, .1) 0%, rgba(0, 0, 0, 0.10) 100%), rgba(0, 0, 0, .5);
  background-blend-mode: luminosity, color-burn;
  box-shadow: 1px 1.5px 4px 0px rgba(0, 0, 0, 0.10) inset, 1px 1.5px 4px 0px rgba(0, 0, 0, 0.08) inset, 0px -0.5px 1px 0px rgba(255, 255, 255, 0.25) inset, 0px -0.5px 1px 0px rgba(255, 255, 255, 0.30) inset;
  display: grid;
  gap: 8px;
  & h1 {
    font-size: 16px;
    margin: 0;
  }
  & form, & section {
    background-color: rgba(128, 128, 128, 0.30);
    border-radius: 8px;
    padding: 8px 12px;
    text-align: center;
  }
}

aside {
  grid-row: 3 / 4;
  grid-column: 2 / 4;
  border-radius: 8px;
  padding: 2px 12px;
  background: linear-gradient(0deg, rgba(0, 0, 0, .1) 0%, rgba(0, 0, 0, 0.10) 100%), rgba(0, 0, 0, .5);
  background-blend-mode: luminosity, color-burn;
  box-shadow: 1px 1.5px 4px 0px rgba(0, 0, 0, 0.10) inset, 1px 1.5px 4px 0px rgba(0, 0, 0, 0.08) inset, 0px -0.5px 1px 0px rgba(255, 255, 255, 0.25) inset, 0px -0.5px 1px 0px rgba(255, 255, 255, 0.30) inset;
  display: grid;
}

footer {
  grid-row: 4 / 5;
  grid-column: 1 / 4;
  border-radius: 8px;
  padding: 2px 12px;
  background: linear-gradient(0deg, rgba(0, 0, 0, .1) 0%, rgba(0, 0, 0, 0.10) 100%), rgba(0, 0, 0, .5);
  background-blend-mode: luminosity, color-burn;
  box-shadow: 1px 1.5px 4px 0px rgba(0, 0, 0, 0.10) inset, 1px 1.5px 4px 0px rgba(0, 0, 0, 0.08) inset, 0px -0.5px 1px 0px rgba(255, 255, 255, 0.25) inset, 0px -0.5px 1px 0px rgba(255, 255, 255, 0.30) inset;
}
```


## まとめ
この記事では、「**ランドマーク**」に焦点を当てて、アクセシビリティを意識したランドマークの実装方法とランドマークで意識した方がいいアクセシビリティを解説しました。

ぜひこの記事をストックして、ランドマークを実装する時にアクセシビリティについて思い出してもらえると嬉しいです。

Advent Calendar 2023では、他のコンポーネントにも焦点を当てて、アクセシビリティについても解説しているので、ぜひ購読していてください。

https://qiita.com/advent-calendar/2023/degudegu2510

---

最後まで読んでくださってありがとうございます！

普段はデザインやフロントエンドを中心にQiitaに記事を投稿しているので、ぜひQiitaのフォローとX(Twitter)のフォローをお願いします。

https://twitter.com/degudegu2510