Stitchesとは
2021年8月31日にver1がリリースされたcss-in-jsライブラリー(公式サイト)
Near-Zero Runtimeを謳っており、Emotionやstyled-componentsといった他のcss-in-jsライブラリーより高速(ベンチマーク)
VariantsやToken、theme機能をサポートしており、『デザインシステムの構築と運用を支援する強力なcss-in-jsソリューション』という佇まいを見せている。
前置き: Stitchesに興味をもった個人的背景
個人的に仕事でもプライベートでもNext.jsをよく使うが、これまでNext.jsでプロジェクトをするときは公式の推しに乗ってCSS Modulesを利用してきた。
CSS Modulesはwebpackのcss-loaderで『名前空間(クラス名)の安全性を確保する』という特定の問題の解決にのみフォーカスした特にモダンでもない技術で、新しいもの好きの知的好奇心や冒険心を掻き立てたりはしてくれないものの、生cssという標準技術との距離が圧倒的に近いこともあり、確立されたエディターの補完やstylelintなどのエコシステムの恩恵を存分に受けることができ、また、技術の流行り廃りに伴う将来的な技術的負債化リスクが低いと考えられ、また、パフォーマンス的にもcss-in-jsなどに比べて優位なことから、割と消去法的選択としてCSS Modulesを選択してきた。
CSS Modulesは確立されたしっかりした技術でViteなどの次世代ビルドツールにも標準サポートされているし、State of CSS in 2020のレポートによると他のcss-in-jsのソリューションに比べてもいまだに一番使用されてもいるし、安牌ではあると今も思っている。今年の6月には、Tailwind CSSの爆発的な広がりに対してそれでも私がTailwind CSSではなく、CSS Modulesを推す理由などの記事も個人的に書かせていただいたりして、それなりの人に読んでいただけたりもした(賛否は不明)。
しかし、一方で近年フロントエンド界隈でデザインシステムの構築と運用を支える技術的アプローチへの関心が急激に高まってきているのを感じており、CSS Modulesの運用におけるscssで定義する定数集やmixin集よりもっと強力な技術的な支援が必要なのかもしれないとも思っていた‥。
Stitchesのことを初めて知ったのは、ShopifyのデザインシステムPolarisをSassから何に移行するかというGithubの議論で、個人的にも今度また仕事でNext.jsで新規プロジェクトをやる予定があったところ、丁度ver1がリリースされ、改めて公式ドキュメントなどを見たところ、Stitchesは明確に『デザインシステムの構築と運用を支援するcss-in-jsソリューション』といった佇まいを見せている(気がした)ので、プロダクションへの投入の検討に、ある程度まとまった量のスタイルを実際にStitchesで書いてみて、検証してみることにした。
Stitches(in Next.js)で作ってみたデモページ
こんな感じの擬似ブログ的なもののトップページを今回作ってみました(Stitchesの検証用のため、機能はついていない)。
Vercelでデプロイしてありますので、よろしければチェックしてみてください(こちら)。
また、今回のデモのコードはhttps://github.com/70ki8suda/stitches-demo にアップしてあります。
ページを構成するコンポーネント
今回はザックリとコンポーネントを4つ、Header・SideMenu・FeaturedContent(右上に大きく表示される記事)・ArticleItem(4つ並ぶ記事)作ってページを構成した。
以下、Storybookのスクショ




tokenを定義するconfigファイル
Stitchesでは、サイト全体で使われる定数などをstitches.config.jsに記述する。
import { createStitches } from '@stitches/react';
export const { config, createTheme, css, getCssText, globalCss, styled, theme, keyframes } = createStitches({
theme: {
colors: {
textBlack: '#0b0d0e',
grayBorder: '#F3F3F4',
accentBlue1: '#3B86CB',
},
space: {
1: '5px',
2: '10px',
3: '15px',
4: '20px',
5: '25px',
6: '35px',
},
sizes: {
1: '5px',
2: '10px',
3: '15px',
4: '20px',
5: '25px',
6: '35px',
},
fontSizes: {
1: '12px',
2: '13px',
3: '15px',
4: '17px',
5: '19px',
6: '21px',
7: '24px',
titleSize: '30px',
},
fonts: {
system: 'system-ui',
},
},
utils: {
marginX: (value) => ({
marginLeft: value,
marginRight: value,
}),
marginY: (value) => ({
marginTop: value,
marginBottom: value,
}),
paddingX: (value) => ({
paddingLeft: value,
paddingRight: value,
}),
paddingY: (value) => ({
paddingTop: value,
paddingBottom: value,
}),
},
media: {
sp: '(max-width: 959px)',
pc: '(min-width: 960px)',
},
});
このような形でサイト全体で共通して使用したい、色・フォント・フォントサイズ・マージン・メディアクエリーなどをこの設定ファイルに記述する。また、この設定ファイル内のutilsでユーティリティプロパティを設定することもできる(今回は使用しなかった)。
case1: pages/index.jsx
トップページのjsx部分は以下のようにコーディングしてみた
//省略
<MainContainer>
<SideMenu />
<MainArea>
<FeaturedContent color='yellow' />
<ArticlesArea>
<h2 className='ArticlesArea__lead'>Recent update project</h2>
<ul className='ArticlesArea__list'>
{articles &&
articles.length > 0 &&
articles.map((article, i) => <ArticleItem title={articles[i].title} body={articles[i].body} i={i} />)}
</ul>
</ArticlesArea>
</MainArea>
</MainContainer>
トップページのstitchesによるスタイリング部分
const MainContainer = styled('div', {
display: 'flex',
'@sp': {
display: 'block',
},
});
const ArticlesArea = styled('div', {
padding: '30px',
'& .ArticlesArea__list': {
display: 'flex',
gap: '20px 20px',
'@sp': {
flexWrap: 'wrap',
},
},
'& .ArticlesArea__lead': {
fontSize: '$5',
marginBottom: '1.4em',
},
});
こんな感じでDOMの詳細なタグ名などを隠蔽して、styled-componentsのようにスタイルコンポーネントを書くことが出来る。
styled-componentではテンプレートリテラル記法で書けるが、Stitchesはobject-literal記法しかサポートしていない。
しかし、エディターの補完が効くので、意外とそこまで面倒くさくはなかった。
Reactで定義したコンポーネントかスタイルコンポーネントかパッと見でわからないなどはあるものの、個人的にはタグ名をセマンティックに書けるのは気に入っている。
'@sp': {
flexWrap: 'wrap',
},
みたいに、メディアクエリーを省略してスタイル書けるのはとても楽。
今回、スタイルコンポーネントとして定義するのはBEMでいうブロック要素単位で行ってみることにした。
全てをスタイルコンポーネントとして定義するのでなく、ブロックのエレメント要素にはクラス名でスタイルをあてている。
これによって、若干、コーディングの面倒くささが減る。また、スタイルコンポーネントに対してあてたスタイルはハッシュ値のクラス名を最終的に付与されてレンダリングされるが、スタイルコンポーネントの子孫要素としてクラス名であてたスタイルのクラス名は残る(上記の例の.ArticlesArea__listで定義したスタイルは .[ハッシュ] .ArticlesArea__listとして出力される)ので、デバッグ時にdeveloper toolなどでクラス名から修正箇所を探す際などのフックにもなって保守の際、便利になるのでは?という気がしている。
case2: components/FeaturedContent.jsx
export default function FeaturedContent({ color }) {
return (
<Wrapper>
<div className='image-wrap'>
<img src='https://www.syfy.com/sites/syfy/files/styles/1140x640_hero/public/screen-shot-2021-09-09-at-9.07.24-am.png' />
<p className='image-credit'>
Credit: The Matrix Resurrections – Official Trailer 1 / Warner Bros. Pictures YouTube
</p>
<div className='featured-lead-wrap'>
<FeaturedLead className='featured-lead' color={color}>
'THE MATRIX RESURRECTIONS'
<br /> TRAILER SENDS TWITTER DOWN A RABBIT HOLE OF FAN THEORIES
</FeaturedLead>
</div>
</div>
</Wrapper>
);
}
このコンポーネントでは、Stitchesの強力な機能であるVariantsを使ってみた。
Qiita埋め込み用
— 70ki8suda (@70ki8suda) September 13, 2021
Stitches: Variants demo pic.twitter.com/o50ArPiMNt
<FeaturedLead className='featured-lead' color={color}>
'THE MATRIX RESURRECTIONS'
<br /> TRAILER SENDS TWITTER DOWN A RABBIT HOLE OF FAN THEORIES
</FeaturedLead>
const FeaturedLead = styled('h2', {
zIndex: '2',
display: 'inline',
fontSize: '2.4vw',
maxWidth: '60%',
background: '#fff',
'@sp': {
fontSize: '4vw',
},
variants: {
color: {
yellow: {
boxShadow: 'inset 0 -2.4px #fff, inset 0 -11.2px #ffff00, 5px 5px 0 5px #fff, -5px 5px 0 5px #fff',
},
red: {
boxShadow: 'inset 0 -2.4px #fff, inset 0 -11.2px #ff0000, 5px 5px 0 5px #fff, -5px 5px 0 5px #fff',
},
blue: {
boxShadow: 'inset 0 -2.4px #fff, inset 0 -11.2px #0000ff, 5px 5px 0 5px #fff, -5px 5px 0 5px #fff',
},
},
},
});
StitchesはCSS Modulesを越える選択肢になり得るか?
今回、はっきりいってStitchesを使ってCSSを書くDXはとても良かった。
最終的にコード量が増えてくるとコード分割するだろうとはいえ、CSS Modulesと違って、jsxの同ファイルにいきなりスタイルを書き始めることが出来るのは、DXが良い。
CSS Modulesだと、普通のcssファイルで書くので、font-weight:bold;とか書きたいときは、短縮記法でfwb+tabとかのコマンドをターンと入力すれば速攻でcssを書いていけるが、css-in-jsはそういうのは無理。
しかし、TypeScriptの補完でfonくらいまで入力すればfontWeightという選択肢がエディター上に表示されるので、そこまでストレスは感じなかった。
デザインシステムという観点から言うと、stitches.config.jsファイルで定数を入力することによるデザイントークンの強制力はcss modulesの定数集とさして変わることはない。
Variantsだって、Stitchesのシステムはエレガントではあるものの、propsでクラス名を差し替えればcss modulesで同じことは出来る。
はっきり言って CSS Modulesで既に書いたプロジェクトをStitchesに書き換える必要が出てくるほどの機能的な優位性などはないと思う(ランタイムのパフォーマンスもcss modulesの方がはやいし)
しかしStitchesにはTokenやVariantsを駆使してスタイルコンポーネントをうまく作ってデザインシステムを構築していこうというレールが機能として敷かれているので、自然とデザインシステムに対するメンバーの意識は上がり易いと思われる
ここの部分にどこまで価値を見出せるかだと思う。
**Stitchesっていう最高にイケてるcss-in-jsライブラリーを使って、最高の俺らだけのデザインシステムを作ろうぜ!**ってテンションになって開発できるのならば、自分は十分Stitchesを採用する価値はあると思う。
だから、個人的には次のプロジェクトの技術選定ではStitchesを推していきたいと思ったし、個人プロジェクトではほぼ100%使うと思う。
今回の記事で言及しなかったが、Stitchesの開発元のmodulzはRadix-uiというUIコンポーネントライブラリーも開発している。
Radix-UIではcssのついていないプレーンなアクセシビリティ(wai-aria)など対応済みのコンポーネントを配布してくれており、これらをStitchesと共に利用すれば、各サービスのデザインや要件に応じたコンポーネントの開発スピードをブーストしてくれそうだなと思っている。
Modulzの開発者がこれらの技術を使ったデザインシステムの構築に関して非常に興味深いブログ記事Why I Build Design Systems with Stitches and Radixを書いてくれていたりもするので、是非参照なさってください!(近日中にこちらの記事の方も翻訳します)
追記
その後も色々考えたのですが、結局、新規の業務でのStitchesの採用は見送り、引き続きCSS Modulesを選択することになりそうです。理由ですが、
結局、ランタイムのパフォーマンスでCSS Modulesが優位
Stitchesは書き方の選択肢が多様すぎてコーディング担当者によってばらつきが出そう
生CSSから遠くなることによる将来的な移行の際のコストの懸念
などです。
Stitchesを使用したプロジェクトの例として、Stitchesのドキュメントサイトのコードがgithubで公開されています(こちら)。
Stitchesはさまざまな書き方ができるので、実務などで導入するにはある程度のコーディング規約などが必要となるでしょう。導入を検討する際には上記の公式ドキュメントのコード例などを見て、チームで記述方法についてある程度の意思の統一が必要になるかもしれません。