画面をスクロールしたら固定ヘッダーに境界線がつくUIをCSSのみで実現する

画面をスクロールしたら固定ヘッダーに境界線がつくUIをCSSのみで実現する方法を紹介します。どんな時にそのようなUIを採用したくなるかというと、画面が読み込まれた直後はヘッダーとコンテンツの境界をシームレスに表現したいが、スクロールをしたらヘッダーが重なるので境界をはっきりさせたい。そんなときです。

これを実現したいと思った時にパッと思いつく方法は、スクロールされたかどうかをJavaScriptでチェックしてclassを付与するという方法ですが、この記事で紹介する作り方をすればCSSだけで実現できます。

以下のデモ画面をスクロールしてみてください。

JavaScriptを利用せずにCSSだけで実現しているのでいくつか制約がありますが、これらの制約を受け入れられるのであれば、JavaScriptを書かなくてもいいのでおすすめです。

  • 境界線を表示するタイミングはスクロール直後である
  • 背景色がベタ塗りである必要がある

コードサンプル

では、どのように実現しているのかを見ていきましょう。コードを見た方が理解が早いと思うので、まずはコードをご覧いただきます。必要なところだけに絞っているので、完全なサンプルはCodePenのデモを参照してください。

HTMLはこうです:

<div id="container">
  <header>Title</header>
  <div id="border"></div>
  <div id="article">
    <div></div>
    <div></div>
    <div></div>
    ...
  </div>
</div>

この例だと、div#border が境界線になります。

そしてSCSSはこちら:

$header-height: 50px;

#container {
  padding-top: $header-height;
}

header {
  height: $header-height;
  background: #fff;
  position: fixed;
  top: 0;
  width: 100%;
  z-index: 2;
}

#border {
  position: relative;
  z-index: 1;
  &::before,
  &::after {
    content: "";
    width: 100%;
  }
  &::before {
    position: fixed;
    border-bottom: 1px solid gray;
  }
  &::after {
    position: absolute;
    border-bottom: 1px solid white;
  }
}

ポイントとしては、境界線として見せたいborderと、スクロールされていない時にそれを覆い隠す(背景色と同じ色の)borderを重ねるということです。

borderを重ねる理由

背景色と同じ色のborderを重ねると、そこにborderがあっても視覚的にはborderはないように見えます。これでページ読み込み直後のシームレスな見た目を実現します。

そして、画面をスクロールした時にどうなるのかというと、

  • 境界線として見せたいborder(この例では ::before)は position: fixed なのでその位置に留まり続ける
  • 覆い隠す方のborder(この例では ::after)は position: absolute なので、スクロールとともに動く

というかんじで、覆い隠されていたborderが見えるようになるわけです。これで、スクロール前は隠す、スクロールしたら表示するということができました。

単純なことをしているだけなので、考え方を覚えてしまえば簡単に再現ができると思います。ぜひご利用ください。