CSSだってJavaScriptの値が欲しい!

インターン先でreact + typescript + scssのお仕事をしている @haduki1208 と申します。
最近reactよりscssを多く書いてる気がします。割と楽しいです。

# TL;DR
- CSS変数をインラインスタイルで書く
- var()で読み込む

# CSSでJavaScriptの値を取得する
趣味で「任意の大きさ・色・向きを指定できる矢印アイコン:arrow_up::arrow_right::arrow_down::arrow_left:」を作っていた時に

:thinking:「CSSでJavaScriptの値を取得できないのか?」

と思ったのでやってみました。

cssに[attr()](https://developer.mozilla.org/ja/docs/Web/CSS/attr)という関数がありますが、content属性でしか動作しないようです。
今回はheight, width, background-color, rotateに任意の値を使いたいのですがダメです。微妙な仕様ですね。

# 環境
- typescript + react + scss および typescript + react + styled-components
- chromeで動作確認
- 動いたコードをcodepenに移植(codepenを使ってみたかった :heart: )

# ソースコード
<p class="codepen" data-height="500" data-theme-id="default" data-default-tab="css,result" data-user="haduki1208" data-slug-hash="zYxYXEJ" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Arrow01 react + ts + scss">
  <span>See the Pen <a href="https://codepen.io/haduki1208/pen/zYxYXEJ">
  Arrow01 react + ts + scss</a> by haduki1208 (<a href="https://codepen.io/haduki1208">@haduki1208</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

最初に作った矢印アイコンのソースコードです。

このコードの弱点は以下になります。

1. size, color, rotateの1つでも違う値を使いたい場合、新たにclassnameを作成しなければならない
2. tsxとscssの両ファイルに変更を加える必要がある
3. mixinを使っているため、トランスパイル後のcssの容量が大きくなる

1番、2番は面倒くさいです。
これらを全て解消してみます。

# 変更後のソースコード
<p class="codepen" data-height="500" data-theme-id="default" data-default-tab="css,result" data-user="haduki1208" data-slug-hash="YzPzMdy" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Arrow02 react + ts + scss">
  <span>See the Pen <a href="https://codepen.io/haduki1208/pen/YzPzMdy">
  Arrow02 react + ts + scss</a> by haduki1208 (<a href="https://codepen.io/haduki1208">@haduki1208</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

[CSS カスタムプロパティ (変数)](https://developer.mozilla.org/ja/docs/Web/CSS/Using_CSS_custom_properties)を使いました。

ポイントは以下になります。

1. style属性に指定したオブジェクトは、インラインスタイルに変換される
2. インラインスタイルで記述されているため、CSS変数が他の要素に影響しない
3. typescript環境の場合、React.CSSPropertie型以外のオブジェクトを代入するため「as any」する必要がある

これでscssを編集せず色々なアイコンを作ることができます！

# おまけ styled-componentsに置き換えたソースコード
こんなことするなら、最初からstyled-componentsにすれば良いじゃないと思った人もいらっしゃるでしょう。
僕は今までstyled-componentsを使ったことがなかったので、勉強のため置き換えをしてみました。

<p class="codepen" data-height="500" data-theme-id="default" data-default-tab="js,result" data-user="haduki1208" data-slug-hash="gObOJLx" style="height: 291px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Arrow03 react + ts + styled">
  <span>See the Pen <a href="https://codepen.io/haduki1208/pen/gObOJLx">
  Arrow03 react + ts + styled</a> by haduki1208 (<a href="https://codepen.io/haduki1208">@haduki1208</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

なんて素晴らしいライブラリなのでしょう。
scss、インラインスタイル、as any　全て消え去りました。
(ついでにwebpack.config.jsからstyle-loader, css-loader, sass-loader, postcss-loaderも消え去りました)

styled-componentsに惚れてしまいそうです。

# 感想
react + scssで開発をしているプロジェクトでJavaScripの変数を取得したくなった時はこの方法で解決できそうです。

既にscssで開発しているプロジェクトでは、styled-componentsに移行するにも時間がかかってしまうので、もしcssからjavascriptの値を使いたくなったら、ご検討ください。