どうも、くまだです。
アクセシビリティ対策で、WAI-ARIA使ってハンバーガーメニュー実装の試み。なお、WAI-ARIAについて軽く触れた程度の知識です。
WAI-ARIAでハンバーガーメニュー実装
WAI-ARIA(ウェイ アリア)はHTMLだけでは表すことのできない構造や状態を明示。コンテンツの役割を示すrole属性だったり、コンテンツの状態・性質を示すaria属性を定義できます。
(今回はrole属性使わない)
aria属性を使って、ハンバーガーメニューの実装をします。
〇 HTML
<div class="l-container">
<header class="l-header p-header">
<div class="p-header__inner">
<h1 class="p-header__title" id="logo-label">
<a href="" aria-labelledby="logo-label">ロゴ</a>
</h1>
<div class="p-header__hamburger">
<button class="c-hamburger" aria-expanded="false" aria-label="メニューを開く">
<span></span>
<span></span>
<span></span>
</button>
</div>
<nav class="p-header__nav p-nav" aria-expanded="false">
<div class="p-nav__inner">
<ul class="p-nav__list">
<li class="p-nav__item">
<a href="#" class="p-nav__link">メニュー</a>
</li>
<li class="p-nav__item">
<a href="#" class="p-nav__link">メニュー</a>
</li>
<li class="p-nav__item">
<a href="#" class="p-nav__link">メニュー</a>
</li>
<li class="p-nav__item">
<a href="#" class="p-nav__link">メニュー</a>
</li>
</ul>
</div>
</nav>
</div>
</header>
<main>
メインコンテンツ
</main>
</div>
〇 CSS
ul,li {
list-style: none;
}
p {
margin :0;
}
a {
text-decoration: none;
}
main {
padding-top: 72px;
background: skyblue;
height: 1200px;
}
.l-header {
display: block;
z-index: 999;
position: fixed;
top: 0;
right: 0;
left: 0;
width: 100%;
height: 72px;
background: #fff;
}
.c-hamburger {
position: relative;
width: inherit;
height: inherit;
margin: 0;
border: 1px solid lightblue;
background: lightblue;
cursor: pointer;
}
.c-hamburger span {
display: block;
position: relative;
left: 50%;
width: 30px;
height: 2px;
transform: translateX(-50%);
background: #fff;
transition: all 0.4s;
}
.c-hamburger span:nth-of-type(1) {
top: -4px;
}
.c-hamburger span:nth-of-type(2) {
top: 1px;
}
.c-hamburger span:nth-of-type(3) {
top: 6px;
}
.c-hamburger .c-hamburger__text {
display: block;
top: 12px;
background: transparent;
color: #fff;
font-size: 10px;
font-weight: 400;
line-height: 1;
text-transform: uppercase;
}
.c-hamburger[aria-expanded="true"] span:nth-of-type(1) {
top: 0;
transform: translateX(-50%) rotate(225deg);
}
.c-hamburger[aria-expanded="true"] span:nth-of-type(2) {
opacity: 0;
}
.c-hamburger[aria-expanded="true"] span:nth-of-type(3) {
top: -4px;
transform: translateX(-50%) rotate(-225deg);
}
.p-header__nav {
display: flex;
z-index: 10;
position: absolute;
top: 0;
right: -100%;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100vh;
background: transparent;
font-weight: 700;
opacity: 0;
transition: 0.6s;
}
@media screen and (min-width:768px) {
.p-header__nav {
position: static;
height: 100%;
opacity: initial;
align-items: flex-end;
}
}
.p-header__inner {
display: flex;
align-items: center;
justify-content: space-between;
height: inherit;
padding: 13px 20px;
}
.p-header__title {
width: 100%;
max-width: 120px;
height: 48px;
background: lightgray;
}
.p-header__title a {
display: block;
width: 100%;
height: auto;
}
.p-header__title a img {
height: 100%;
}
.p-header__hamburger {
z-index: 100;
position: absolute;
top: 0;
right: 0;
width: 95px;
height: 100%;
}
@media screen and (min-width:768px) {
.p-header__hamburger {
display: none;
}
}
.p-header__nav[aria-expanded="true"] {
position: fixed;
top: 0;
right: 0;
background: lightblue;
opacity: 1;
transition: 0.6s;
}
.p-nav {
padding-top: 72px;
padding-bottom: 72px;
}
@media screen and (min-width:768px) {
.p-nav {
padding-top: 16px;
padding-bottom: 16px
}
}
.p-nav__list {
display: block;
width: 100%;
padding-right: 20px;
padding-left: 20px;
background: lightblue;
}
@media screen and (min-width:768px) {
.p-nav__list {
display: flex;
background: #fff;
padding-right: 0;
}
}
.p-nav__item {
position: relative;
width: 100%;
}
.p-nav__link {
display: block;
width: 100%;
height: 100%;
padding: 20px;
text-align: center;
}
〇 JavaScript
window.addEventListener('DOMContentLoaded', () => {
const hamburger = document.querySelector('.c-hamburger');
const nav = document.querySelector('.p-header__nav');
hamburger.addEventListener('click', () => {
const hamburgerAriaValue = hamburger.getAttribute('aria-expanded');
if (hamburgerAriaValue === 'false') {
hamburger.setAttribute('aria-expanded', true);
nav.setAttribute('aria-expanded', true);
} else {
hamburger.setAttribute('aria-expanded', false);
nav.setAttribute('aria-expanded', false);
}
});
});
aria属性は、プロパティ(要素の性質)とステート(要素の状態)を設定できます。
今回使用したのは、
- aria-label:要素の目的や機能を説明
- aria-expanded:要素が展開されているか、折りたたまれているか
- aria-labelledby:要素とラベルを関連づける
ハンバーガーメニューの下記の部分、aria-labelでボタンの機能を説明。
<button class="c-hamburger" aria-expanded="false" aria-label="メニューを開く">
<span></span>
<span></span>
<span></span>
</button>
aria-expanded=”false”で、現在は要素が折りたたまれている状態を明示的に示しています。それを、JavaScriptでfalseからtrueに切り替え処理。
hamburger.addEventListener('click', () => {
const hamburgerAriaValue = hamburger.getAttribute('aria-expanded');
if (hamburgerAriaValue === 'false') {
hamburger.setAttribute('aria-expanded', true);
nav.setAttribute('aria-expanded', true);
} else {
hamburger.setAttribute('aria-expanded', false);
nav.setAttribute('aria-expanded', false);
}
});
getAttribute()でハンバーガーメニューの属性を取得。falseだったらsetAttributeでtrueに、そうでないときはfalseに書き換えます。
あとはCSSで属性でスタイルを指定すればOK。
.c-hamburger[aria-expanded="true"] span:nth-of-type(1) {
top: 0;
transform: translateX(-50%) rotate(225deg);
}
.c-hamburger[aria-expanded="true"] span:nth-of-type(2) {
opacity: 0;
}
.c-hamburger[aria-expanded="true"] span:nth-of-type(3) {
top: -4px;
transform: translateX(-50%) rotate(-225deg);
}
クリックしたらクラスの付け替えはよくやると思いますが、上記の例ではそれと同じことを属性で切り替えつつ、アクセシビリティ対応もできるので、こちらのほうがいいかも。
ここまで読んでくださりありがとうございました。