Open15

ReactのCSS周り追う

ReactでCSSほとんど書いたことない。Freshでスタイルの指定方法何が使えて何が使えないのか理解するためにReactのCSS戦略古い順(多分)に追う。

まずは直書きCSS。

function App() {
  return (
    <div className="App">
      <style>
        {`
        h1{
          color: red;
        }
        `}
      </style>
      <h1>Hello, World</h1>
    </div>
  );
}

Viewがアップデートされるたびに再評価されてパフォーマンスが悪いとかどっかで見た気がする。スコープがない。

CSSの直import。

import "./App.css";

function App() {
  return (
    <div className="App">
      <h1>Hello, World</h1>
    </div>
  );
}

これはヘッダに展開される。

スコープはない。

これの問題というか、Deno使ってる立場だとCSSはimportで読めないんだよな。入るかもしれないが…

https://github.com/denoland/deno/issues/11961

CSS Modules.

import AppModule from "./App.module.css";

function App() {
  return (
    <div className="App">
      <h1 className={AppModule.MyClass}>Hello, World</h1>
    </div>
  );
}

これはこのようにオシャレに処理される。

クレバーでなかなかかっこいいですね。
これがデフォルトで使えるのはなかなかすごいことだなあ。
いつどんな経緯で入ったんだろう?

https://github.com/css-modules/css-modules#history

あーこれReact関係なくて、PostCSSで議論されてたやつをWebpackが実装したんで当たり前に使われてるってことなのかな?

styled-components.

import styled from "styled-components";

const Heading = styled.h1`
  color: red;
  :hover {
    color: blue;
  }
`;

function App() {
  return (
    <div className="App">
      <Heading>Hello, World</Heading>
    </div>
  );
}

export default App;

うっ、良さがわからん。

ヘッダに展開されてるな~って思ったけど、そういえばこれviteの機能でもおかしくないな(HMRのためとか)。この記事全然正確じゃないけどごめんね。

スタイルが設計にまで影響してくるのは嫌だなぁ。これ見るとtailwindのほうが楽やんけとなっちゃうかもしれない。でもなんかメリットがどこかにあるんでしょう。

styled-jsx.

…はviteで動かすのしんどそうなのでパスします。何がおきるかはここに書いてある。

https://github.com/vercel/styled-jsx#how-it-works
import _JSXStyle from 'styled-jsx/style'

export default () => (
  <div className="jsx-123">
    <p className="jsx-123">only this paragraph will get the style :)</p>
    <_JSXStyle id="123">{`p.jsx-123 {color: red;}`}</_JSXStyle>
  </div>
)

こんなんなりますよと。親切。
_JSXStyleというのはスタイルタグじゃないんで、ランタイムで処理するということだろうね。

  • render時にスタイルを注入する
  • コンポーネントに一度だけスタイルが注入されるようにする(複数回importされていても)
  • 未使用のスタイルを削除する
  • サーバサイドレンダリングでスタイルの利用状況を追跡する

みたいなことをしてくれると書いてある。
ちなみにタグ付きテンプレートリテラルで記述するんだけどこれを使って動的なスタイルを当てられる。

ランタイムがいるということで、freshとかで動くかはわからんな。

では本命のemotionをば。

import { css, cx } from "@emotion/css";

function App() {
  return (
    <div className="App">
      <h1
        className={css`
          color: red;
          &:hover {
            color: blue;
          }
        `}
      >
        Hello, World
      </h1>
    </div>
  );
}

おお~。もうほぼtwind + freshの書き方。
入れ子もできてsassいらず。いいっすね。

いいじゃんいいじゃん。

tailwind inspired 三兄弟。

いつもどれがどれだっけ?ってなるけど、UnoCSSがこの中で一番後発で、作者のAntFuはWindyCSSの開発者でもあります。機能の差は正直わからん。後発ほど速いとかかな?

調べてたらなんか揉め事の形跡が見つかったな。

https://antfu.me/posts/windicss-and-tailwind-jit

twind.

import { tw } from "twind";
import { css } from "twind/css";

const styles = css`
color: red;
&:hover {
  color: blue;
}
`;

function App() {
  return (
    <div className="App">
      <h1 className={tw(styles)}>
        Hello, World
      </h1>
      <h1 className={tw`text-green-500 hover:text-yellow-500`}>
        Twind!
      </h1>
    </div>
  );
}

emotion的な書き方もできる。tailwindで書きづらそうなところはこう書いちゃえばいいのかな。

なんかstyleタグの中身が見えない。そういうもの?

WindiCSS.

function App() {
  return (
    <div className="App">
      <h1 className="text-green-500 hover:text-yellow-500">
        Hello, World
      </h1>
    </div>
  );
}

viteプラグインがある。少し準備が必要な代わりにtagged templateは不要。

おおー、こうなるんだ。viteのレンダリングに割り込んでクラスかき集めてヘッダに入れてるのかな?

あと @apply という機能があり、CSS importしてきたCSSの中で使うとutility classが使えたりする。ただしインラインでCSSを書く機能はなさそう?

UnoCSS.

基本的にWindiCSSと同じっぽい。導入方法も含めて違いが見られない。

細かく見ないと違いがわからないかなー。
tailwindから外れたものをどう書くか悩ましい。CSS importしてくればいいんだけどできない環境ではどうしたものかな。グローバルに書くしかないかね。

というわけでfresh + twindに戻ってきた。

import { h } from "preact";
import { tw } from "@twind";

export default function Home() {
  return (
    <div>
      <h1 class={tw`text-red-500 text-4xl`}>Hello</h1>
    </div>
  );
}

このように書けてこんな感じと。

/** @jsx h */
import { h } from "preact";
import { tw } from "@twind";
import { css } from "twind/css";

const style = css`
&:hover{
  color: green;
}
@media (min-width: 768px) {
  background: yellow;
}
`;

export default function Home() {
  return (
    <div>
      <h1 class={tw`text-red-500 text-4xl ${style}`}>Hello</h1>
    </div>
  );
}

なるほど。こんな書き方もできるのか。
cssのtagged templateはクラスを返すのでこういうことができるのね。

DenoでもReactのコンポーネントライブラリ使いたいって人はよくいて(MUIとかheadless-uiとかchakra-uiとか)、実際使えるとは思ってなかったんだけど、esm.shがCSS importを頑張って解決してくれるみたい(Package CSSを参照)。

https://esm.sh/

Headless UIに関しては動くよってここで言ってる。

https://github.com/denoland/fresh/discussions/606

ちょっとexample repoがほしい感じかなあ。とりあえずここまで。

とりま今のところはtwindがemotion的なこともできて便利度が高いなという印象。UnoCSSもcss moduleが使える環境では良さそうかなあ。
freshでのUIライブラリのインポートはあんまり試せてないんだけどどうなんだろう。あんまり使えたよって人を見ない。単純なコンポーネントに関してはもうIsland集を作っちゃったほうが早いのではないだろうか。

aleph.jsはコンポーネント使えてそうな雰囲気がするんだけどどうなんだろうか。

ログインするとコメントできます