ブログ的なレイアウトだと、サイドバーをスクロールに合わせて固定させたいということがよくありますよね。一般的にこれは JavaScript で実装されますが、せっかくなので CSS の position: sticky で試してみました。

position: sticky とは

使用頻度の高い position プロパティにあって sticky なる値は聞いたことがないかもしれませんが、これは CSS Positioned Layout Module Level 3 にて追加された新しいもので、なかなか説明するのがムズカシイのですが、fixedrelative を合わせたようなものだと MDN では説明されています。

position: sticky と しきい値 (top, right, bottom, left プロパティ)を指定することで、画面上の絶対位置がしきい値より大きいときは relative のように、オフセット値に達すると fixed のように振る舞うという感じです。

対応ブラウザ

EdgeとIE以外の主要ブラウザが対応していますが、Google Chrome は 56 以降と最近再実装されたばかりです。
スクリーンショット: Can I Use

Edge と IE が対応していないのでまだ迂闊に使えないかもしれませんが、 未対応が致命的なレイアウト系のプロパティとちがって数%の Edge と IE ユーザーがスティッキーしないだけなので、これが悪影響にならないと判断できれば使い道はありそう。未対応ブラウザのみ JS を使うという手もあります。

Sticky なサイドバーの例たち

百聞は一見にしかずということで、 position: sticky を使った追尾型サイドバーを何バリエーションか作ってみました。

まずはベーシックなスタイルから。

See the Pen position: sticky; Sticky sidebar(standard) by holy0201 (@holy0201) on CodePen.light

スクロールするとサイドバーのウィジェットが追尾してくるのが確認できると思います。

.main {
  flex: 0 0 66.6666%;
  padding: 1rem;
  height: 1000px;
  margin-right: 2rem;
  background: #fafafa;
}

.sidebar {
  display: flex;
  
  flex: 0 0 33.3333%;
  background: white;
  
  &__inner {
    position: sticky;
    top: 30px;
    flex: 0 0 100%;
    align-self: flex-start;
  }
  
  &__widget {
    background: skyblue;
    height: 250px;
    padding: 1rem;
  }
}

サイドバーのラッパーに position: sticky を指定しています。ぴったりくっつかれるとダサいので、top: 30px でやや余白を設けています。しきい値となる top, right, bottom, left の初期値は auto なので、sticky を利用するときには何らかの値を指定する必要があります。

また、スティッキーしたい要素の高さが親要素と同じだと意味が無いです。上のコードではラッパーに align-self: flex-start を指定して Flex アイテムの高さをコンテンツに合わしています。

下に固定する

続きまして、サイドバーが長めのサイトで嬉しい、下にスティッキーするタイプです。

See the Pen position: sticky; Sticky sidebar(bottom) by holy0201 (@holy0201) on CodePen.light

&__inner {
    position: sticky;
    bottom: 30px;
    min-height: calc(100vh - 60px);
    flex: 0 0 100%;

    @supports (position: sticky) {
      align-self: flex-end;
    }
  }

align-self: flex-end で要素を下揃えにし、bottom: 30px で下に固定しています。ただ、未対応の Edge と IE で下揃えになっていると困るので、@supports を使って position: sticky に対応している場合に限定しています。

また、これだとサイドバーの高さがビューポート未満の場合に下に固定されてダサいので、 min-height: calc(100vh - 60px) で高さを持たせて、上に固定されている(ように見える)ようにしています。

ただ、これだと見た目上完全に下までスティッキーしないので、完璧ではないですね。悔C!

一部の要素だけ固定する

よく知らないのでアレですが、AdSense 広告は追尾させちゃダメらしいですね。というわけで、そういうときに使える、一部のウィジェットだけ追尾させるやつです。目次とかにも使えるね。

See the Pen position: sticky; Sticky sidebar(with fixed) by holy0201 (@holy0201) on CodePen.light

特筆することもなくシンプルです。スティッキーさせるラッパーの外にウィジェットを置いています。

まとめ

JavaScript でやるよりも描画が滑らかでパフォーマンス的にも良い(計測とかしてないけどおそらく) position: sticky。今回はサイドバーを扱いましたが、ほかにもいろいろな場面で利用できると思います。

また新たなバリエーションを思いついたら書き足していきます。といいつつしないんだろうな!