DUICUO

Facebook や Instagram で使用されている CSS ソリューションがオープンソース化されました。

最近、Facebook(現Meta)は社内向けCSSソリューションであるStyleXをオープンソース化し、GitHubで既に3,200個のスターを獲得しています。Facebook、WhatsApp、Instagram、Workplace、Threadsなどの製品はすべて、StyleXをスタイリングソリューションとして採用しています。この記事では、StyleXの概念、機能、そして使い方について詳しく解説します。

StyleXの基本概念

StyleX は、Facebook が React Conf 2019 で初めて公開した内部 CSS-in-JS ライブラリです。

それ以来、Facebook、Instagram、WhatsApp に採用され、開発者から広く注目を集めています。しかし、CSS-in-JS ツールの人気は、高いパフォーマンスとのトレードオフにより、時間の経過とともに低下してきた可能性があります。しかし、StyleX は巧妙な Babel プラグインによってこれらの問題を解決しました。

StyleXは、強力で表現力豊か、決定論的、信頼性が高く、スケーラブルなスタイル設定システムです。他のスタイルライブラリの優れたアイデアを基に構築され、使い慣れながらも独自の機能を提供することで、開発者の開発プロセスを簡素化します。

StyleXは、CSS-in-JSライブラリの開発エクスペリエンスとコンパイル時ツールを組み合わせることで、静的CSSにおける高いパフォーマンスとスケーラビリティを実現します。しかし、StyleXは単なる新しいコンパイラベースのCSS-in-JSライブラリではありません。大規模アプリケーション、再利用可能なコンポーネントライブラリ、そして静的に型付けされたコードベースのニーズを満たすように設計されています。StyleXの主な機能は以下のとおりです。

  • CSS の表現サブセットをサポートし、複雑なセレクターを回避し、生成された CSS に特定の競合がないことを確認します。
  • スタイルを「アトミック」な CSS クラス名に変換、整理、最適化することで、個別のユーティリティ クラス名ライブラリを学習したり管理したりする必要がなくなります。
  • ファイルやコンポーネントの境界を越えてスタイルをマージできるため、ユーザーによるカスタマイズが可能なコンポーネント ライブラリに最適です。
  • コンポーネントが受け入れるプロパティと値を細かく制御できるようにするための型情報と型ツールを提供します。

特徴

StyleX の主な機能は次のとおりです。

  • 高速:StyleXはコンパイル時と実行時の両方で高いパフォーマンスを誇ります。Babel変換はビルドプロセスに大きな影響を与えません。実行時には、StyleXはJavaScriptによるスタイル挿入のオーバーヘッドを回避し、必要な場合にのみクラス名文字列を効率的に結合します。生成されたCSSは最適化されているため、大規模なウェブサイトのスタイルでもブラウザで迅速に解析できます。
  • スケーラブル:StyleXは、Metaのような非常に大規模なコードベースに対応するように設計されています。アトミックビルドとファイルレベルのキャッシュにより、Babelプラグインは数万ものコンポーネントのコンパイル時スタイル処理を処理できます。StyleXはスタイルをカプセル化するように設計されているため、新しいコンポーネントを独立した環境で開発し、他のコンポーネントで使用すれば予測可能なレンダリングが期待できます。
  • 予測可能性:StyleXはCSSセレクタの詳細度を自動的に管理し、生成されたルール間の競合を防ぎます。開発者に信頼性の高いスタイル適用システムを提供し、「最後に適用されたスタイルが常に機能する」ことを保証します。
  • 型安全性:TypeScriptまたはFlowの型を使用して、コンポーネントが受け入れるスタイルを制限します。各スタイルプロパティと変数には完全に定義された型が与えられます。これにより、コードの可読性と保守性が向上し、潜在的なエラーや競合が減少します。
  • スタイルの重複排除:StyleXは、スタイルとコンポーネントを同じファイルに記述することを推奨しています。このアプローチにより、スタイルの可読性と保守性は長期的に向上します。StyleXは静的解析ツールとビルド時ツールを活用して、コンポーネント間でスタイルの重複を排除し、使用されていないスタイルを削除します。
  • テスト容易性:StyleXは、機能的なアトミッククラス名ではなくデバッグクラス名を出力するように設定できます。これにより、スナップショットを生成し、軽微な変更が加えられた際にデザインが頻繁に変更されるのを防ぐことができます。これにより、開発者はスタイルの正確性をより簡単にテストおよび検証でき、開発効率と製品品質が向上します。

起源

Facebookウェブサイトの初期開発では、CSSモジュールのようなアプローチでスタイルを管理していましたが、この方法には多くの問題がありました。そこで、Facebookは新たなソリューション、CSS-in-JSの開発を思いつきました。一般ユーザーがfacebook.comにアクセスすると、数十MBものCSSがダウンロードされますが、その多くは使用されません。Facebookは初期読み込み速度を最適化するためにCSSの遅延読み込み戦略を採用しましたが、これは更新の速度低下を招きました。さらに、複雑なセレクタの使用はスタイルの競合につながる可能性がありました。これらの問題を解決するために、開発者はしばしば…   !important  あるいはセレクターがより複雑になり、スタイル システム全体がますます複雑になります。

数年前、Facebookがウェブサイトの再構築に着手した際、より優れたソリューションを切実に必要としていました。そこで、StyleXを設計・構築しました。

StyleXはスケーラビリティを考慮して設計されており、そのアーキテクチャは長年の使用実績によって実証されています。パフォーマンスやスケーラビリティを犠牲にすることなく、継続的に新機能が追加され、StyleXはさらに使いやすくなりました。StyleXの導入により、アプリケーションのスケーラビリティと表現力は大幅に向上しました。facebook.comのリファクタリングでは、CSSバンドルを数十MBの遅延読み込みCSSから数百KBの単一バンドルに削減することに成功しました。

Metaは、Web上のReact開発者のスタイル設定ニーズを満たすだけでなく、Webプラットフォームとネイティブプラットフォーム間でReactのスタイル設定ソリューションを統合するためにStyleXを開発しました。StyleXの導入により、クロスプラットフォームでのスタイルの一貫性が実現し、開発効率と製品品質の向上に繋がりました。

StyleXの基本的な使い方

Metaの目標は、StyleXを可能な限りシンプルで習得しやすいものにすることです。そのため、過剰なAPIを開発して複雑化させるのではなく、一般的なJavaScriptパターンを活用して、可能な限りシンプルなAPIインターフェースを提供することを目指しています。

基本的に、StyleX は次の 2 つの関数に簡略化できます。

  • stylex.create: スタイルを作成するために使用されます。
  • stylex.props: これらのスタイルを要素に適用するために使用されます。

これら2つの関数において、MetaはStyleX専用のAPIやパターンを導入するのではなく、一般的なJavaScriptパターンを利用することを選択しました。例えば、条件付きスタイル用のAPIを設計する代わりに、ブール値や三項式を用いて条件付きでスタイルを適用することをサポートしています。

ボタン コンポーネントを作成しましょう。

 import * as stylex from "@stylexjs/stylex"; const styles = stylex.create({ base: { appearance: "none", borderWidth: 0, borderStyle: "none", backgroundColor: "blue", color: "white", borderRadius: 4, paddingBlock: 4, paddingInline: 8, }, }); export default function Button({ onClick, children, }: Readonly<{ onClick: () => void; children: React.ReactNode; }>) { return ( <button {...stylex.props(styles.base)} onClick={onClick}> {children} </button> ); }

StyleXはスタイルとコンポーネントを緊密に統合しており、これは開発エクスペリエンスとコードの可読性の観点から大きなメリットとなります。CSSスタイルの記述にはEmotionのようなアプローチを採用しているため、コードの理解と保守が容易になります。さらに、StyleXはコンパイル時にCSSを処理できるため、ランタイムシステムでは提供できない利点が得られます。

しかし、Tailwindとは異なり、StyleXはTailwindのようなショートカットスタイルの利便性を提供していません。Tailwindのショートカットスタイルは失われる代わりに、スタイルをより細かく制御できるようになります。例えば、ユーザーがボタンの色と背景色のみを変更できるようにしたいとします。Tailwindコンポーネントでは、これに特化したプロパティを定義できますが、ユーザーがより多くのスタイルを調整できるようにしたい場合、このアプローチはあまり拡張性がありません。そのため、一部の開発者は追加の` extraClassesプロパティを許可し、ユーザーが自由にスタイルを追加できるようにしています。しかし、これによりユーザーは制限なくスタイルを変更できてしまい、後続のバージョン管理が困難になります。

StyleX にはこの問題に対する優れた解決策があります:

 import type { StyleXStyles } from "@stylexjs/stylex/lib/StyleXTypes"; export default function Button({ onClick, children, style, }: Readonly<{ onClick: () => void; children: React.ReactNode; style?: StyleXStyles<{ backgroundColor?: string; color?: string; }>; }>) { return ( <button {...stylex.props(styles.base, style)} onClick={onClick}> {children} </button> ); }

`style` というプロパティを追加し、オーバーライドしたいスタイルのみをオーバーライドするように制限しました。`stylex.props` 呼び出しで `styles.base` の後に `style` を配置することで、オーバーライドされたスタイルがベーススタイルを適切にオーバーライドすることを保証できます。これにより、どの CSS が変更可能で、どの CSS が変更不可能かが明確になり、ボタンのバージョン管理が可能になります。

スタイルをオーバーライドする場合は、次のように Button を使用できます。

 const buttonStyles = stylex.create({ red: { backgroundColor: "red", color: "blue", }, }); <StyleableButton onClick={onClick} **style={buttonStyles.red}**> Styleable Button </StyleableButton>

StyleXは条件付きスタイルと動的スタイルをサポートしています。ボタンに強調マークを追加してみましょう。

 import * as stylex from "@stylexjs/stylex"; const styles = stylex.create({ ..., emphasized: { fontWeight: "bold", }, }); export default function Button({ onClick, children, emphasized, }: Readonly<{ onClick: () => void; children: React.ReactNode; emphasized?: boolean; }>) { return ( <button {...stylex.props(styles.base, emphasized && styles.emphasized)} onClick={onClick} > {children} </button> ); }

強調スタイルを定義するためにスタイル定義に別のセクションを追加し、フラグに基づいて条件付きでスタイルを適用するだけです。とても簡単です。

これはStyleXが提供する機能のほんの一部に過ぎません。位置や色などの値を実行時に生成する必要がある場合、スタイルは動的にすることもできます。バリアントなどのオプションは、バリアントを定義する別の`stylex.create`を追加し、属性に基づいて適切なバリアントスタイルを使用するだけで簡単にサポートできます。

StyleX チームは OpenProps のすべてのコンテンツを StyleX に移植しました。つまり、さまざまな間隔オプション、色、アニメーションなどに簡単にアクセスできるようになります。

StyleXの仕組み

StyleX は、次のようなコラボレーション ツールのスイートです。

  • Babelプラグイン:StyleXの中核となるこのプラグインは、コンパイル時にソースコードからすべてのスタイル定義を検出・抽出し、アトミッククラス名に変換します。重複排除、ソート、CSSファイルへの書き込みなどの補助機能を通じて、バンドラープラグインの実装をサポートします。
  • ランタイムライブラリ:より高度な動的スタイル構成パターンを処理するための軽量ランタイムライブラリです。高パフォーマンスに最適化されており、結果キャッシュを利用して応答速度を向上させます。
  • ESLint プラグイン: ESLint と統合することで、このプラグインは開発中に StyleX を使用するコードをリアルタイムで検出して標準化し、ベスト プラクティスに従うことができます。
  • パッケージング ツールおよびフレームワークとの統合: StyleX は、さまざまなパッケージング ツールおよびフレームワークとの統合オプションを提供し、プロジェクトとのシームレスな統合を実現します。

パフォーマンスを最適化するため、Babelプラグインは可能な限り最終的なクラス名を事前計算します。これにより、同一ファイル内でのクラス名のマージを含め、実行時のパフォーマンスオーバーヘッドが排除されます。コンポーネントが静的に定義され、スタイルが同一ファイル内で使用されている場合、実行時オーバーヘッドは完全に排除されます。

Meta は StyleX をどのように使用しますか?

Metaは、社内ウェブインターフェース全体にわたるコンポーネントのスタイル設定にStyleXを推奨ソリューションとして確立しました。Facebook、WhatsApp、Instagram、Workplace、Threadsといった主要な社外向け製品や社内向け製品を問わず、MetaはReactコンポーネントのスタイル設定にStyleXを使用し、コンポーネントの記述方法を変革し、スタイルコンポーネントのカプセル化と拡張における従来の課題を解決しています。

MetaはStyleXの本来の機能を拡張するだけでなく、エンジニアが静的スタイルと動的スタイルの両方を記述できるようにしています。現在、MetaチームはStyleXのテーマAPIを使用して、様々なMeta製品で使用されている様々なデザインシステムのルック&フィールに適応できる「汎用」コンポーネントを開発しています。StyleXはReact Nativeスタイルシステムで導入されたカプセル化の原則に従っているため、Metaはクロスプラットフォームスタイルのサポートを徐々に強化しています。