🐷

【CSS】スムーススクロールの実践

2024/03/14に公開

本記事の目的

この記事では、

  • スムーススクロールを使ったレイアウトをCSS だけで実現する

ことを目的とし、コードを通してポイントをまとめます。

完成したレイアウトは、以下のようになります。

scroll-smooth.gif

結論:コードのポイントまとめ

  • アニメーション防止には、:focus-within を設定する
  • フォーカスを当てたい遷移先のHTML要素には、tabindex 属性を付ける
  • @media(prefers-reduced-motion: no-preference) で、「視差効果を減らす」設定を有効化できる
  • 解決できない問題点がある
    • スクロールスピードの調整
    • イージングが聞かない
    • サイト内全てのアンカーに反映される
    • 他のページから飛んできたとき、一度ヘッダーまで戻り、スムーススクロールが実行される。
    • リンクが付く

レイアウトの実装

HTML

<body>

    <header class="header commonGrid">
        <nav class="header-nav grid12">
            <ul>
                <li><a href="#about_anchor">About</a></li>
                <li><a href="#product_anchor">Product</a></li>
                <li><a href="#news_anchor">News</a></li>
            </ul>
        </nav>
    </header>

    <section id="about_anchor" tabindex="-1" class="about commonGrid">
        <div class="grid12">
            <div class="text">
                <h2>About</h2>
                <p>あああああああああああああああああああ<br>
                    あああああああああああああああああああああ<br>
                    <br>
                    あああああああああああああ<br>
                    ああああああああああああああああ
                    <br><br>
                    ああああああああああ
                </p>
            </div>
            <figure>
                <img src="images/test2.jpg" alt="">
            </figure>
        </div>
    </section>

    <section id="product_anchor" tabindex="-1" class="product commonGrid">
        <div class="grid12">
            <div class="text">
                <h2>Product</h2>
                <p>あああああああああああああああああああ<br>
                    あああああああああああああああああああああ<br>
                    <br>
                    あああああああああああああ<br>
                    ああああああああああああああああ
                    <br><br>
                    ああああああああああ
                </p>
            </div>

            <figure>
                <img src="images/test3.jpg" alt="">
            </figure>
        </div>
    </section>

    <section id="news_anchor" tabindex="-1" class="news commonGrid">
        <div class="grid12">
            <div class="text">
                <h2>News</h2>
                <p>あああああああああああああああああああ<br>
                    あああああああああああああああああああああ<br>
                    <br>
                    あああああああああああああ<br>
                    ああああああああああああああああ
                    <br><br>
                    ああああああああああ
                </p>
            </div>

            <figure>
                <img src="images/test4.jpg" alt="">
            </figure>
        </div>
    </section>

</body>

CSS

@charset "UTF-8";

/* スムーススクロール */
@media (prefers-reduced-motion: no-preference) {
    html:focus-within {
        scroll-behavior: smooth;
    }
}

/* 基本 */
body {
    color: #222222;
    font-family: sans-serif;
    line-height: 1.5;
}

* {
    margin: 0;
    padding: 0;
    list-style: none;
}

a {
    color: inherit;
    text-decoration: none;
}

h2 {
    font-size: 32px;
    margin-bottom: 30px;
    line-height: 1.8;
}

p {
    font-size: 20px;
    line-height: 1.8;
}

img {
    width: 100%;
    height: 100%;
    object-fit: cover;

    max-width: 100%;
    vertical-align: bottom;
}

/* パーツグリッド */
.commonGrid {
    display: grid;
    grid-template-columns: minmax(8vw, 1fr) minmax(auto, 1088px) minmax(8vw, 1fr);
}

.commonGrid>* {
    grid-column: 2;
}

/* 12分割グリッド */
.grid12 {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    column-gap: 16px;
}

.grid12>* {
    grid-column: 1 / -1;
}

/* ヘッダー */
.header.commonGrid {
    height: 100vh;
    background-image: url(images/test1.jpg);
    background-size: cover;
    background-position: center center;
}

.header-nav ul {
    grid-column: 9 / -1;
    display: grid;
    column-gap: 40px;
    color: #ffffff;
    font-size: 18px;
    margin-top: 39px;
}

.header-nav ul>li {
    grid-row: 1;
}

/* About */
.about.commonGrid {
    height: 100vh;
    align-items: center;
}

.about .text {
    grid-column: 8 / span 6;
    grid-row: 1;
}

.about figure {
    grid-column: span 6;
    grid-row: 1;
}

/* Product */
.product.commonGrid {
    height: 100vh;
    background-color: #222222;
    align-items: center;
}

.product .text {
    grid-column: span 6;
    grid-row: 1;
    color: #ffffff;
}

.product figure {
    grid-column: 8 / span 6;
    grid-row: 1;
}

/* News */
.news.commonGrid {
    height: 100vh;
    align-items: center;
}

.news .text {
    grid-column: 8 / span 6;
    grid-row: 1;
}

.news .text h2 {
    grid-row: 1;
}

.news .text p {
    grid-row: 1;
}

.news figure {
    grid-column: span 6;
    grid-row: 1;
}

scroll-smooth.gif

ポイント

コードのポイントは、下記の記事を参考に作成しています。

詳しく知りたい方は、一度ご覧ください。

https://qiita.com/k-fuseya/items/5648cd09e26a0ab2c6b3

ページ検索でのアニメーション防止

コードの下記の部分です。

html:focus-within {
    scroll-behavior: smooth;
}

html要素に :focus-within を付けることで、ページ検索によるアニメーションを防止できます。

この:focus-within は、その要素 または 子孫要素にフォーカスがあれば、実行する動作になっています。

しかし、:focus-within だけでは、Chrome・Firefox ブラウザでは動作されません。

そのため、フォーカスを当てたい遷移先のHTML要素に tabindex 属性を付けます。

コードの下記の部分です。

<section id="about_anchor" tabindex="-1" class="about partsGrid">
...
</section>

<section id="product_anchor" tabindex="-1" class="product partsGrid">
...
</section>

<section id="news_anchor" tabindex="-1" class="news partsGrid">
...
</section>

これにより、正常な動作を保ちながら、ページ検索でのアニメーションを防止できます。

「視差効果を減らす」設定がオンのときの無効化

コードの下記の部分です。

/* mediaの部分 */
@media (prefers-reduced-motion: no-preference) {
    html:focus-within {
        scroll-behavior: smooth;
    }
}

このメディアクエリにより、「視差効果を減らす」設定のとき、必要不可欠でないアニメーションを無効化できます。

prefers-reduced-motion は、「余計な動きを最小化する」設定を検出します。

no-preference は、「ユーザーが設定を行っていない場合」を検出する値です。

つまり no-preference を指定することで、ユーザーが「視差効果を減らす」を設定しているなら、アニメーションを無効化する動作ができます。

※「ユーザーが設定している場合」を検出する値は、reduce です。

解決できていない問題点

上記が解決しても、CSSでのスムーススクロールには以下の欠点が存在します。

  • スクロールスピードの調整
  • イージングが聞かない
  • サイト内全てのアンカーに反映される
  • 他のページから飛んできたとき、一度ヘッダーまで戻り、スムーススクロールが実行される。
  • リンクが付く

結論

  • アニメーション防止には、:focus-within を設定する
  • フォーカスを当てたい遷移先のHTML要素には、tabindex 属性を付ける
  • @media(prefers-reduced-motion: no-preference) で、「視差効果を減らす」設定を有効化できる
  • 解決できない問題点がある
    • スクロールスピードの調整
    • イージングが聞かない
    • サイト内全てのアンカーに反映される
    • 他のページから飛んできたとき、一度ヘッダーまで戻り、スムーススクロールが実行される。
    • リンクが付く

CSS と JS の使い分けになりそうです。

参考資料

https://qiita.com/k-fuseya/items/5648cd09e26a0ab2c6b3

https://note.com/eightbe/n/n9eac9c7b3cb0

https://developer.mozilla.org/ja/docs/Web/CSS/scroll-behavior

https://drafts.csswg.org/css-overflow/#smooth-scrolling

https://pengi-n.co.jp/blog/css-scroll-behavior/

Discussion