CSS から React コンポーネントを生成する MistCSS
MistCSS は CSS in JS になぞらえた JS from CSS というコンセプトで、CSS から React コンポーネントを生成するツールです。ピュアな CSS を記述できるので、学習コストが低い、パフォーマンスに影響がないといったメリットがあります。
昨今のフロントエンド開発では、CSS の手法が多様化しています。特に React での開発では以下のような手法があげられます。
- グローバル CSS(エントリーポイントで 1 つの CSS ファイルを読み込む)
- CSS Modules
- CSS in JS
- Tailwind CSS
CSS の手法に新たな選択肢が加わりました。それが MistCSS です。MistCSS は CSS in JS になぞらえた JS from CSS というコンセプトで、CSS から React コンポーネントを生成するツールです。
MistCSS のメリットとして、以下のような点が挙げられます。
- ピュアな CSS を記述できるので、学習コストが低い
- 生成されたコンポーネントは自動で型安全になる
- コンポーネントがシンプルでステートレスな設計になる
- ゼロランタイムで動作するので、パフォーマンスに影響がない
MistCSS を使ってみる
実際に MistCSS を使ってみましょう。以下のコマンドで MistCSS をインストールします。
npm install --save-dev mistcss
JS From CSS というコンセプトのとおり、まずは CSS を記述します。MistCSS の自動生成の対象とする CSS ファイルはいくつかのルールがあります。
- CSS ファイルの拡張子は
.mist.css
であること - コンポーネント名を @scope で指定する。このクラス名はプロジェクトで一意である必要がある
- :scope 擬似クラスでコンポーネントのルート要素を指定する
- Props で受け取る値を
data-
で指定する
例として、以下の Props を受け取る <Button>
コンポーネントを作成してみましょう。
variant
:primary
またはsecondary
disabled
:true
またはfalse
/*
* @scope() の値に .button を指定すると Button コンポーネントが生成される
*/
@scope (.button) {
/** <button> 要素がコンポーネントのルート要素になる */
button:scope {
/* 共通のスタイル */
padding: 8px 16px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
/**
* <Button variant="primary"> の場合
*/
&[data-variant='primary'] {
background-color: #007bff;
color: #fff;
}
/**
* <Button variant="secondary"> の場合
*/
&[data-variant='secondary'] {
background-color: #6c757d;
color: #fff;
}
/**
* <Button disabled> の場合
*/
&[data-disabled] {
opacity: 0.5;
cursor: not-allowed;
}
}
}
2024 年 3 月現在、属性の値は必ずシングルクォート('
)で囲む必要があるようです。ダブルクォートで囲むと Props の型が正しく生成されませんでした。
CSS の記述が完了したら、以下のコマンドで React コンポーネントを生成します。
npx mistcss ./src
コマンドの実行に成功すると、src/components/button.mist.css.tsx
が生成されます。
// Generated by MistCSS, do not modify
import './button.mist.css'
type ButtonProps = {
children?: React.ReactNode
variant?: 'primary' | 'secondary'
disabled?: boolean
} & JSX.IntrinsicElements['button']
export function Button({ children, variant, disabled, ...props }: ButtonProps) {
return (
<button {...props} className="Button" data-variant={variant} data-disabled={disabled}>
{children}
</button>
)
}
姿勢されたコンポーネントは以下のように使用できます。
import { Button } from './components/Button'
export function App() {
return (
<div>
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button disabled>Disabled</Button>
</div>
)
}
1 つの CSS ファイルに複数のコンポーネントを記述する
複数の @scope
を記述することで、1 つの CSS ファイルから複数のコンポーネントを生成することができます。これは <DialogHeader>
、<DialogBody>
、<DialogFooter>
などのように、複数のコンポーネントで 1 つの UI を構成する場合に便利です。
@scope (.dialog) { /** */ }
@scope (.dialog-header) { /** */ }
@scope (.dialog-body) { /** */ }
@scope (.dialog-footer) { /** */ }
import {
Dialog,
DialogHeader,
DialogBody,
DialogFooter,
} from "./components/Dialog";
CSS による論理演算
/* foo=bar かつ baz=qux の場合 */
&[data-foo="bar"]&[data-baz="qux"] {
/** */
}
/* foo=bar または baz=qux の場合 */
&[data-foo="bar"],
&[data-baz="qux"] {
/** */
}
まとめ
- MistCSS は CSS から React コンポーネントを生成するツール
- ピュアな CSS を記述できるので、学習コストが低い、パフォーマンスに影響がないといったメリットがある
- CSS を以下のルールに従って記述し、コマンドを実行することでコンポーネントが生成される
- CSS ファイルの拡張子は
.mist.css
- コンポーネント名を
@scope
で指定する :scope
擬似クラスでコンポーネントのルート要素を指定する- Props で受け取る値を
data-
で指定する
- CSS ファイルの拡張子は