Kohei Blog

夫・父親・医療系エンジニア

emotionで:first-child がエラーになる

今回はGatsbyで起こったが、emotionの仕様みたいなのでメモしておく。

"@emotion/react": "^11.4.1",
"@emotion/styled": "^11.3.0",

やりたいこと

こんな感じで first-childに対してスタイルを当てたかった。

`.Pagination__block {
      &:first-child.-active {
        margin-right: 8px;
        & ~ .-nextNext {
          display: block;
        }
      }
}

emotionCSSを書いてビルドするとコンソールエラーが出るのを確認した。

エラー内容

The pseudo class ":first-child" is potentially unsafe when doing server-side rendering. Try changing it to ":first-of-type".

疑似クラスの「:first-child」は、サーバーサイドレンダリングを行う際に安全でない可能性があります。:first-of-typeに変更してみてください。

「なんで???」

「emotion first-child」でググる

emotion-js に issueがあった!

The docs have taken a sharp downhill turn with v10 #1059

@jhoffmcd SSRは、コンポーネントの一部としてスタイル要素をレンダリングします(したがって、SSRを実行すると、多くのスタイル要素が作成されますが、それはパフォーマンスの問題ではありません)。そうすることで、SSRのみが機能します。最初の子はスタイルタグであるため、この疑似セレクターは「安全ではありません」という警告があります。全員がSSRを実行しているわけではないため、少なくともこの警告をオフにするとよいことに同意します。

Problems surrounding SSR injection of style and unreliability of :first-child selectors #1178

エモーションv10 +では、スタイル付きコンポーネントまたはcss prop値で、、、またはセレクターを使用する:first-childと:nth-child、新しい警告がスローされ:nth-last-childます。これは、SSRを使用する場合、style要素が関連するコンポーネントのすぐ上(先頭に追加)に注入されるためです。

issueのコメントを見る限り、Style要素がコンポーネントの先頭に差し込まれる形でレンダリングされるらしい。

なので、 first-child を指定してしまうと、 「style のことを指してるの?」となり、エラーが出てくる。

styleがコンポーネントの先頭に追加されるってどういうこと?

yarn build してみて、htmlファイルを生成してみる。

<div id="___gatsby">
   <div style="outline:none" tabindex="-1" id="gatsby-focus-wrapper">
      <style data-emotion="css 1j6r3sl">.css-1j6r3sl .postList{max-width:1000px;margin:0 auto;padding:60px 40px;}@media (max-width: 767px){.css-1j6r3sl .postList{padding:40px 16px;}}</style>
      <style data-emotion="css-global sjlswe">*:where(:not(iframe, canvas, img, svg, video):not(svg *)){all:unset;display:revert;}*,*::before,*::after{box-sizing:border-box;}ol,ul{list-style:none;}img{max-width:100%;}table{border-collapse:collapse;}html{font-size:62.5%;}body{font-family:sans-serif;color:#333;}@media (prefers-color-scheme: dark){body{background-color:#212122;color:#fefefe;}}*{box-sizing:border-box;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-weight:400;line-height:1.6em;letter-spacing:0;}img{display:block;width:100%;}a{display:block;}sup{font-size:80%;line-height:1em;color:inherit;font-weight:inherit;}pre{display:block;width:100%;white-space:pre-wrap;}pre[class*='language-']{background-color:#1c1b1b;display:block;margin:0 0 20px;padding-right:1rem;padding-left:2rem;border-radius:0 4px 4px 4px;}code[class*='language-'],pre[class*='language-']{color:#fff;}:not(pre)>code[class*='language-text']{padding:2px 10px 3px 12px;margin:0;color:#f14668;text-shadow:none;background-color:whitesmoke;}.gatsby-highlight-code-line{background:#545454;display:block;}.gatsby-code-title{background:#2e96b5;color:#eee;padding:6px 12px;font-size:0.8em;line-height:1;font-weight:bold;display:table;border-radius:4px 4px 0 0;}</style>
      <div class="css-1j6r3sl es84g1o0 -Top">

         <style data-emotion="css yl514p">.css-yl514p .BaseTemplate__container{padding:0 16px;}</style>
         <div class="BaseTemplate css-yl514p e2u8ox10 -Top">

            <style data-emotion="css 17zshvp">.css-17zshvp{padding:8px 16px;text-align:center;box-shadow:0 0 1em rgba(0, 0, 0, 0.1);}.css-17zshvp .Header__inner{max-width:960px;margin:0 auto;padding:0 16px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-webkit-justify-content:space-between;justify-content:space-between;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}.css-17zshvp .Header__logo{font-size:18px;font-weight:bold;cursor:pointer;}.css-17zshvp .Header__navMenu{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}.css-17zshvp .Header__navMenu li{margin:0 8px;}.css-17zshvp .Header__navLink{font-size:16px;cursor:pointer;}</style>
            <div class="Header css-17zshvp e1nmgjw0">
               <header>
                  <div class="Header__inner">
                     <a aria-current="page" class="Header__logo" href="/">Blog</a>
                     <nav class="Header__nav">

                     </nav>
                  </div>
               </header>
            </div>
            <main class="BaseTemplate__container">
               <ul class="postList">

                  <style data-emotion="css 2eh5bq">.css-2eh5bq .PostList__box{padding:16px;border:1px solid #eee;cursor:pointer;-webkit-transition:box-shadow 0.4s ease;transition:box-shadow 0.4s ease;}.css-2eh5bq .PostList__box:not(:last-child){margin-bottom:20px;}.css-2eh5bq .PostList__box:hover{box-shadow:0 0 1em rgba(0, 0, 0, 0.1);}.css-2eh5bq .PostList__title{font-size:24px;font-weight:bold;margin-bottom:0.5em;}.css-2eh5bq .PostList__date{font-size:14px;}@media (prefers-color-scheme: dark){.css-2eh5bq .PostList__box{border:1px solid #545454;}.css-2eh5bq .PostList__box:hover{box-shadow:0 0 1em rgba(255, 255, 255, 0.2);}}</style>
                  <div class="PostList css-2eh5bq e1rod1k60">

                  </div>
               </ul>
            </main>

            <style data-emotion="css 1pqwhda">.css-1pqwhda{width:100%;padding:16px;text-align:center;}.css-1pqwhda .Footer__copyright{font-size:14px;}</style>
            <div class="Footer css-1pqwhda eadoj9z0"><small class="Footer__copyright">© Kohei Kojima</small></div>
         </div>
      </div>
   </div>
</div>

確かに、buildされたhtmlファイルを見ると、コンポーネントの前(先頭)にstyleが追加されている。

これを見越して、エラーを出してくれていたのかー。

解決方法

The pseudo class ":first-child" is potentially unsafe when doing server-side rendering. Try changing it to ":first-of-type".

このメッセージ通り、first-of-type に置き換えるのよさそう

まとめ

  • emotionではfirst-childではなくfirst-of-typeを使う

  • <style>タグが差し込まれることを見越したCSSスタイリングを行う