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;
}
}
}
emotion
でCSSを書いてビルドするとコンソールエラーが出るのを確認した。
エラー内容
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スタイリングを行う