🥢

React の CSS ライブラリ Stitches の所感

2022/05/10に公開約5,400字

Stitches という多分マイナー目な CSS ライブラリがあって最近使ってみたのですが、結構好みだったので紹介したいと思います。
あまりガッツリした技術選定というよりは自分が気持ち良かったところを語る感じなので TECH でなく IDEA 枠での投稿です。

基本情報


https://stitches.dev/

すごいざっくりと特徴をまとめると

  • TypeScript で型安全に書ける
  • near zero runtime で速い

な CSS-in-JS です。強化版 styled-components といった趣です。
むしろ型がよく効いて嬉しいので CSS-in-TS と呼んだ方がいいのかもしれません。

基本的な書き方はこんな感じです。
書き味的には styled-components が TS っぽくなったという感覚です。

const Button = styled("button", {
  padding: "12px 20px",
  borderRadius: "4px",
  backgroundColor: "$white"
});

ちなみに near zero runtime によるパフォーマンスに関してはベンチマークが公開されていて、総じて styled-components や Emotion より速いぜという結果が載せられています。

少なくとも Runtime な CSS-in-JS よりは速そうですね。競合の linaria や vanilla-extract と比べてどうかはちょっと謎です。
また near zero runtime って具体的にどういうことやねんと思われた方もいらっしゃるかもしれませんが、実は私もよく分かっていません。

ドキュメントには Stitches avoids unnecessary prop interpolations at runtime, making it significantly more performant than other styling libraries. と書いてあります。より具体的な仕組みについては将来コードリーディングしてみようかなと思いますが、もしご存知の方がいらっしゃればコメントでお教えいただけると大変嬉しいです。

次に CSS-in-JS として最低限この辺は求められるだろうな、という機能を紹介していきます。

複合

既にスタイルを定義したものをベースに作りたい場合はシンプルにそのコンポーネントをタグ名の代わりに引数に与えてあげれば良いです。

const BaseButton = styled("button", {
  typography: "title",
});

const PrimaryStyledButton = styled(BaseButton, {
  backgroundColor: "$primaryButton",
});

theme 管理

こんな感じに書いて styled を使う側で import するだけです。

// stitches.config.ts
import { createStitches } from '@stitches/react';

export const {
  styled,
  css,
  globalCss,
  keyframes,
  getCssText,
  theme,
  createTheme,
  config,
} = createStitches({
  theme: {
    colors: {
      gray400: 'gainsboro',
      gray500: 'lightgray',
    },
  },
  media: {
    bp1: '(min-width: 480px)',
  },
  utils: {
    marginX: (value) => ({ marginLeft: value, marginRight: value }),
  },
});

theme の値は $ を先頭につけることによってアクセスできます。

const BaseButton = styled("button", {
  backgroundColor: "$gray400",
});

引数を受け取って分岐

variants という機能があるので、それを使うとこんな感じです。
variants というプロパティの中に指定したものが Props となる感じですね。

const Button = styled('button', {
  // base styles

  variants: {
    color: {
      violet: { ...violetStyles },
      gray: { ...grayStyles },
    },
    size: {
      small: {
        fontSize: '13px',
        height: '25px',
        paddingRight: '10px',
        paddingLeft: '10px',
      },
      large: {
        fontSize: '15px',
        height: '35px',
        paddingLeft: '15px',
        paddingRight: '15px',
      },
    },
  },
});

() => (
  <Button color="violet" size="large">
    Button
  </Button>
);

これの他に boolean を取ったり、複数の条件の組み合わせも指定できたりするのでご興味あればドキュメントを見てみてください。

https://stitches.dev/docs/variants

レスポンシブ

こんな感じで config でブレークポイントの設定を用意しておきます。

export const { styled, css } = createStitches({
  media: {
    bp1: '(min-width: 640px)',
    bp2: '(min-width: 768px)',
    bp3: '(min-width: 1024px)',
  },
});

そしたら使う側ではこんな感じで config で定義したブレークポイントの名前をプロパティとしてスタイルを書いていけば良いです。

const Button = styled('button', {
  // base styles

  '@bp1': {
    // Styles for bp1
  },
});

https://stitches.dev/docs/responsive-styles

好きなところ

という訳で大体ベーシックな機能はありそうだということを確認したところで自分が触ってみて好きだったところを語ります。

正直個人的には styled-components にさして不満もなく「変えるだけでパフォーマンス良くなるなら試してみるか〜」くらいの感じだったのですが、使い始めて好きに感じたのが「とにかくいろんなところで補完が効く」というところです。CSS-in-TS なメリットがフルに感じられます。

テーマのサジェストが効く

例えば色の値を入力する時に型のおかげでこんな感じでサジェストされます。

ちなみに theme に設定したのが color であれば color, backgroundColor, borderColor などの色系のプロパティでしかこの補完は出ず、他のプロパティを入力する時の邪魔にはなりません。

こんな感じで theme でのプロパティと CSS のプロパティがうまい具合にマッピングされています。

https://stitches.dev/docs/tokens

また、typography のように line-height, font-size, font-weight...など複数のプロパティも次のように stiches.config.ts で定義して

import { createStitches } from '@stitches/react';

export const {
  styled,
} = createStitches({
  utils: {
    typography: (value: 'title' | 'header') => {
      switch (value) {
        case 'title':
          return {
            fontSize: 15,
	    fontWeight: "400",
          };
        case 'header':
          return {
            fontSize: 18,
	    fontWeight: "700",
          };
        default:
          return {
            fontSize: 14,
          };
      }
    },
  },
});

すると使う側では typography という名のプロパティが出現し、中身の string 部分にも型が効いてます。気持ちいい!

Variants も補完効く

当たり前かもしれませんが variants もこんな感じで定義してたら

const Fuga = styled("button", {
  // base styles

  variants: {
    color: {
      violet: { color: "$border" },
      gray: { color: "gray" },
    },
  },
});

ちゃんと出てきます。

ブレークポイントも補完効く

さっきのこういうのも

export const { styled, css } = createStitches({
  media: {
    bp1: '(min-width: 640px)',
    bp2: '(min-width: 768px)',
    bp3: '(min-width: 1024px)',
  },
});

出てきます。

まとめ

という感じでテーマをしっかり定義しつつ、補完がほぼ全てで聞いてすごいストレスフリーにスタイルを書いていけるところがいいなと思いました。

React の CSS の選択肢は色々ありますが、一つの選択肢としていかがでしょうか!

Discussion

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