CSSでアンダーラインが追従するメニューを実装する方法

Tips

CSSでアンダーラインが追従するメニューを実装する方法

以前であればJavaScriptを利用して実装していた、メニュー下にあるアンダーラインをホバーしたメニューへスライドしながら追従する動きをCSSのみを使って実装する方法です。

デモ

どのようなものなのか実際の動きを見たほうが早いと思うのではじめにデモを紹介すると、このようにメニュー下にあるアンダーラインがホバーしたメニュー下へスライドしていくという動きになり、JavaScriptは使用せずにCSSのみで実装しています。

※動きはPCで確認してください。

See the Pen CSS slide underline by Naoya (@nxworld) on CodePen.

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-underlineposition: 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;
}

Posted on

Category : Tips

Close the search window,
please press close button or esc key.