デモ
どのようなものなのか実際の動きを見たほうが早いと思うのではじめにデモを紹介すると、このようにメニュー下にあるアンダーラインがホバーしたメニュー下へスライドしていくという動きになり、JavaScriptは使用せずにCSSのみで実装しています。
※動きはPCで確認してください。
HTML
HTMLは下記のようなものを使用し、a
要素は単純なリンクで.nav-underline
が追従してくるアンダーラインで使用する要素になります。
また、ハイライト表示している付与されているclassはよくあるカレント表示用に見栄えを異なるものにするのに使用するclassになっており、これをそのまま使用した場合は上のデモにあるように3つ目のメニュー下にアンダーラインがあるのが初期表示となります。
html
<nav>
<a href="#">Primary</a>
<a href="#">Secondary</a>
<a href="#" class="is-current">Tertiary</a>
<a href="#">Quaternary</a>
<a href="#">Quinary</a>
<div class="nav-underline"></div>
</nav>
CSS
CSSはデモ用の装飾に関する部分を省いた最低限必要なコードのみ紹介すると下記のように記述しています。
具体的にはアンダーライン用の要素として用意した.nav-underline
をposition: absolute;
で配置し、間接セレクタを利用してleft
の値を各メニューホバー時に異なる値になるよう指定することで動きを実装しています。
CSS
nav {
position: relative;
display: flex;
}
nav a {
display: block;
width: 20%;
}
.nav-underline {
position: absolute;
left: 0;
bottom: -2px;
width: 20%;
height: 2px;
background: #333;
transition: all .3s ease-in-out;
}
nav a:nth-child(1).is-current ~ .nav-underline {
left: 0;
}
nav a:nth-child(2).is-current ~ .nav-underline {
left: 20%;
}
nav a:nth-child(3).is-current ~ .nav-underline {
left: 40%;
}
nav a:nth-child(4).is-current ~ .nav-underline {
left: 60%;
}
nav a:nth-child(5).is-current ~ .nav-underline {
left: 80%;
}
nav a:nth-child(1):hover ~ .nav-underline {
left: 0;
}
nav a:nth-child(2):hover ~ .nav-underline {
left: 20%;
}
nav a:nth-child(3):hover ~ .nav-underline {
left: 40%;
}
nav a:nth-child(4):hover ~ .nav-underline {
left: 60%;
}
nav a:nth-child(5):hover ~ .nav-underline {
left: 80%;
}
CSS Variablesとcalc()を利用する
上で紹介したCSSで実装は可能ですが、これをCSS Variablesとcalc()
を利用して実装すればleft
の値を計算する手間を省くことができ、特にメニュー数を変更することが多いような場所で少しではありますが再指定が楽になります。
CSS
:root {
--nav-length: 5;
--nav-width: calc(100% / var(--nav-length));
}
nav {
position: relative;
display: flex;
}
nav a {
display: block;
width: var(--nav-width);
}
.nav-underline {
position: absolute;
left: 0;
bottom: -2px;
width: var(--nav-width);
height: 2px;
background: #333;
transition: all .3s ease-in-out;
}
nav a:nth-child(1).is-current ~ .nav-underline {
left: calc(var(--nav-width) * 0);
}
nav a:nth-child(2).is-current ~ .nav-underline {
left: calc(var(--nav-width) * 1);
}
nav a:nth-child(3).is-current ~ .nav-underline {
left: calc(var(--nav-width) * 2);
}
nav a:nth-child(4).is-current ~ .nav-underline {
left: calc(var(--nav-width) * 3);
}
nav a:nth-child(5).is-current ~ .nav-underline {
left: calc(var(--nav-width) * 4);
}
nav a:nth-child(1):hover ~ .nav-underline {
left: calc(var(--nav-width) * 0);
}
nav a:nth-child(2):hover ~ .nav-underline {
left: calc(var(--nav-width) * 1);
}
nav a:nth-child(3):hover ~ .nav-underline {
left: calc(var(--nav-width) * 2);
}
nav a:nth-child(4):hover ~ .nav-underline {
left: calc(var(--nav-width) * 3);
}
nav a:nth-child(5):hover ~ .nav-underline {
left: calc(var(--nav-width) * 4);
}
--nav-length
で実装したいメニューの数を、--nav-width
でアンダーラインとメニューひとつ分の大きさをそれぞれ変数にしておき、あとはそれを利用する形でそれぞれ指定していけば大きさをいちいち計算して記述する手間を減らせることができます。
ただし、この方法はCSS Variablesとcalc()
の入れ子使用に対応しているブラウザである必要があるので、これらに非対応のブラウザをサポートする場合は利用できません。
Sassを利用する
Sassを利用すれば同じく計算の手間を省くことができる他、特にメニュー数を変更することが多いような場所で指定が楽になりますし、こちらであれば上の「CSS Variablesとcalc()を利用する」とは違いコンパイル後に計算後の値が記述されるので、先ほどの方法で非対応だったブラウザでもサポート可能です。
また、メニューの数によって:nth-child
の値をいちいち増減させたりする必要があったのも@for
を利用することで省略することができ、先頭の$nav-length
の値を任意のメニュー数にするだけで容易に対応できます。
SCSS
$nav-length: 5;
$nav-width: 100% / $nav-length;
nav {
position: relative;
display: flex;
a {
display: block;
width: $nav-width;
@for $i from 1 through $nav-length {
&:nth-child(#{$i}).is-current ~ .nav-underline {
left: #{$nav-width * ($i - 1)};
}
}
@for $i from 1 through $nav-length {
&:nth-child(#{$i}):hover ~ .nav-underline {
left: #{$nav-width * ($i - 1)};
}
}
}
}
.nav-underline {
position: absolute;
left: 0;
bottom: -2px;
width: $nav-width;
height: 2px;
background: #333;
transition: all .3s ease-in-out;
}