デザインシステムのESLintプラグインから運用のヒントを学ぶ
はじめに
2021年のデザインシステムに関する調査によると、回答数があまり多くないものの、成功を収めているというデザインシステムは4割程度という結果が出ている。
But a majority of design systems ended up imploding. According to the 2021 Design Systems Survey, only 40% of the systems were successful.
また、alexpate/awesome-design-systems: 💅🏻 ⚒ A collection of awesome design systemsやAdeleといったデザインシステムの事例集のリンク先は、メンテナンスを継続してなかったり滞っているケースが散見される。
このようにデザインシステムは、継続が難しい(技術的な要素よりも組織的な要素が大きいと思うけど)ことが窺えるものだけど、ここではESLintのような静的解析からその継続の一助となるようなものがあるか探ってみたい。
なお、デザインシステムがコンポーネントライブラリーのみを指すわけじゃないけど、ここでは開発に寄った内容を扱うので便宜上デザインシステムやコンポーネントライブラリーなどの定義を細かく分けて扱わない。
デザインシステムのESLintプラグイン
一般的にJavaScriptやTypeScriptを利用したプロジェクトの開発では、ESLintのような静的解析でプラクティスやコーディング規約を機械的に強制したり推奨したりすると思う。
特定の概念に対するESLintのカスタムルールをまとめたESLintプラグインも数多く存在するけど、デザインシステムのESLintプラグインというのも幾つかある。
ここでは、AtlassianとGitHub、Sumup、それぞれのデザインシステムに関するESLintプラグインを見てみる。
@atlaskit/eslint-plugin-design-system
Atlassianには@atlaskit/eslint-plugin-design-system
というESLintプラグインがあり、 "Atlassian Design Systemを利用する際に必要不可欠なプラグイン" として扱われている。
The essential plugin for use with the Atlassian Design System.
このプラグインのルールを幾つかピックアップしてみる。
ensure-design-token-usage
デザイントークンの値を参照することを強制するルール。プロダクトにおいてテーマを切り替えた時、部分的にテーマが適用されない不具合を避ける目的のルール。
All experiences must use color tokens otherwise when switching between themes, unexpected incidents can occur where not all of the UI is themed.
no-banned-imports
デザインシステムのプライベートなモジュールや実験的なモジュールをimportしないようにというもの。exports
フィールドで制限できる部分かなと思うけど、exports
フィールドが登場する前からあるルールなのかもしれない。
デザインシステムのアップデートを難しくする密結合を防ぐ意図があると思う。
Using private or experimental packages is dangerous as they are not supported across major versions meaning you will not be able to migrate easily causing friction for yourself and the Atlassian Design System team.
no-css-tagged-template-expression
@emotion/react
やstyled-components
などのcss
のタグ付きテンプレートリテラルを禁止するルール。
テンプレートリテラルはオブジェクトリテラルと比較するとAST解析が難しいので、その利用を禁止している。
元々はAtlassianがOSSで開発しているCompiledのESLintプラグインのルールをポートしたもの。
なお、Atlassian Design SystemのESLintプラグインのルール一覧をみると、recommended
には入ってない。
no-deprecated-apis
非推奨となったデザインシステムのPropsの利用を禁止するルール。非推奨となったPropsが残っている間、そのPropsを含めてメンテナンスし続けていくということになるので、切り替えを強制するためにLintで機械的に弾くというもの。
no-deprecated-imports
非推奨となったデザインシステムのパッケージのimport
を禁止するルール。no-deprecated-apis
と同じように、速やかに非推奨のものを排除していくことが目的だと考えられる。
no-empty-styled-expression
以下のような空っぽの引数のstyled
式を禁止するルール。
const EmptyStyledDiv = styled.div();
const EmptyStyledOjbectDiv = styled.div({});
このような場合、CSS-in-JSライブラリーによって不要なビルドプロセスを経ることなく、直接div
を用いた方がパフォーマンス観点で望ましいというもの。
no-margin
コンポーネントに自身の外のレイアウトの関心を持たせるな、というのはよく言われることだけど、それをLintルールとして体現しているもの。
margin
の代わりに親要素へgap
を指定することで多くのレイアウトは実現できるはずで、コンポーネント駆動開発におけるコンポーザビリティーを阻害する大きな要因になると思うけど、margin
の利用自体をLintで機械的に禁止するというのはなかなか難しいように思える。
Atlassian Design SystemのESLintプラグインのルール一覧においてもno-margin
はRecommended
じゃないので、ルールの採用は局所的なのかもしれない。
no-nested-styles
ネストしたスタイルを禁止するルール。以下のように子要素を含めてスタイリングしていると、その子要素の変更によって予期せずレイアウト崩れが生じる可能性を持つ。
css({
div: {
display: "flex`",
},
});
まとめてスタイリングせずに、しっかりコンポーネントを分けて細かくコンポーネント毎にスタイリングしましょうというもの。
no-styled-tagged-template-expression
no-css-tagged-template-expression
と類似するもので、styled
のタグ付きテンプレートリテラルを禁止するルール。
CSSの文法上の誤りを静的に検知しにくく、型の制約を持ち込みにくく、シンタックスハイライトも充分なサポートを得られないケースがある。そのためオブジェクトリテラルで記述するべきというもの。
no-css-tagged-template-expression
同様にこちらもAtlassian Design SystemのESLintプラグインのルール一覧ではrecommended
に入ってない。
no-unsafe-style-overrides
デザインシステムのコンポーネントのスタイルを上書きすることを禁止するルール。ただし、xcss
propを利用する形でのスタイリングは許容している。
xcss
はAtlassian Design Systemのデザイントークンとプリミティブコンポーネントを繋ぐAPIで、デザイントークンの値のみをプロパティーとして利用する型安全性が保たれていて、ネストしたセレクターの利用を排除するもの。
- XCSS has type-safety that ensures token name usage for all CSS properties represented by design tokens.
- XCSS restricts nested selectors completely from usage
レイアウトプリミティブのコンポーネントに対してのみxcss
propが付与されている形になると思うので、ほとんどのコンポーネントにおいては、そのコンポーネントのスタイル上書きが禁止されることになる。
コンポーネントのスタイル上書きが必要な場合でも、一定の制約の下でスタイリングすることを強制している。
Lintルールにunsafe
と入っているように、コンポーネント内部のスタイルへの直接的な干渉は、コンポーネントの変更によって起こり得る予期しないレイアウト崩れ等を鑑みるとリスクが大きいはず。
eslint-plugin-primer-react
GitHubのデザインシステムであるPrimerでは、eslint-plugin-primer-react
がReact実装に対する実験的なプラグインとして開発されている。
実験的というのは、eslint-plugin-primer-react is experimental.と記載があることを指している。
まだ実験的な段階ということもあってか、上述の@atlaskit/eslint-plugin-design-system
に比べるとルール数は多くないものの、ReactにおけるPrimerのベストプラクティスを徹底させる目的のLintルールを持つものになっている。
Primer React offers an ESLint plugin to enforce best practices and fix common problems.
こちらも2つほどルールをピックアップしてみる。
direct-slot-children
Primerにおいては、いくつかのコンポーネントでslotsパターンによりサブコンポーネントを特定の場所へレンダリングしていて、そのサブコンポーネントがメインのコンポーネント直下に置かないといけない構成になっている。
slotsパターンとは、コンポーネントが渡された要素のレンダリング場所をコントロールするパターンのこと。
Slots allow us to pass elements to a component and tell the component where these elements should be rendered. A component may support an implicit default slot (unnamed) and optionally, one or more named slots.
このslotsパターンのメインコンポーネントとサブコンポーネントのコンポーネントツリーの構造をLintで強制するというもの。
a11y-tooltip-interactive-trigger
Tooltip
コンポーネントで非インタラクティブ要素をラップしないようにするa11y観点のルール。
状況に応じでどのような実装が望ましいのかを丁寧にLintエラーのメッセージで伝えてくれることがLintルールの実装から分かる。
@sumup/eslint-plugin-circuit-ui
SumupというFinTechのデザインシステムのUIライブラリーとしてCircuit UIというものがあり、そのESLintプラグインとして@sumup/eslint-plugin-circuit-ui
がある。
ESLint rules to help users follow best practices when using Circuit UI.
こちらもルール自体は多くないもののベストプラクティスを実践する助けになるプラグインとして用意されている。
このプラグインも3つほどルールをピックアップしてみる。
component-lifecycle-imports
広範に渡って利用されているデザインシステムではコンポーネントライフサイクルを定義しているケースがあると思うけど、Circuit UIもライフサイクルがある。
ステージによってコンポーネントのサポートレベルは異なり、Legacy
ステージにあるコンポーネントであればバグフィックスのみのサポートになる。
このようなサポート条件の違いをimport
パスの違いによって示していて、それをLintでauto fixできるものになっている。
no-deprecated-components
Deprecated
のステージにある非推奨コンポーネントは次のメジャーバージョンアップデートで削除するものとして扱っていて、段階を経て削除へと至る運用になっている。
- マイナーバージョンアップデートでコンポーネントを
Deprecated
に - 次のメジャーバージョンアップデートでコンポーネントを削除
マイグレーションガイドに削除されるコンポーネントに対する対応手段が示されるので、利用している方ではそのガイドに応じた対応を行なえば良く、一般的なOSSのライブラリーのメジャーバージョンアップデート時と同じように考えることができる。
no-renamed-props
一貫性を徹底するためにコンポーネントのpropをリネームすることがあり、古くなったpropの名前を自動で修正するためのルール。
no-deprecated-components
と同じように段階的な切り替えで運用される。
- マイナーバージョンアップデートで新しい名前のprop追加して、古い名前は同時に非推奨扱いへ
- 次のメジャーバージョンアップデートで古い名前は削除される
まとめ
それぞれのデザインシステムにおけるコンテキストに応じて多様なルールが存在するけど、プラクティスを機械的に強制してくれるルール以外だとデザインシステムのバージョンアップに追随していく助けとなるルールが興味深いなと思う。
他にもデザインシステムでESLintプラグイン作っているところがあるなら、どういうものがあるか見てみたいけど、公開されているものはあまりないのかもしれない。
参考
- How Eslint Can Enforce Your Design System Best Practices
- Why most design systems implode
- Why design systems are a single point of failure | by Dominic Nguyen | Medium
- The 2022 Design Systems Survey by Sparkbox
- UXA2023 Eva Plaisted, Klaus Paiva and Maria Christley - oing smaller, to go bigger: A design system evolution - Speaker Deck
Discussion