Open4
Panda CSS
Zero runtime, Type safeをうたうCSS-in-JSライブラリ
Webサイトにある代表例3つ
1. style-function
import { css } from "./styled-system/css";
import { circle, stack } from "./styled-system/patterns";
function App() {
return (
<div className={stack({ direction: "row", p: "4" })}>
<div className={circle({ size: "5rem", overflow: "hidden" })}>
<img src="https://via.placeholder.com/150" alt="avatar" />
</div>
<div className={css({ mt: "4", fontSize: "xl", fontWeight: "semibold" })}>
John Doe
</div>
<div className={css({ mt: "2", fontSize: "sm", color: "gray.600" })}>
john@doe.com
</div>
</div>
);
-
css()
がスタイルとクラス名を生成して色々いい感じにしてくれる。-
mt
とか Chakra UIでもお馴染みな感じの省略記法がある -
_hover
みたいなのもある
-
-
circle()
とかstack()
とかよく使いそうな Pattern が定義されている。追加や拡張もできる- 🤔 UIライブラリとして再配布したい場合にこれらはエクスポートできるんだろうか
2. style-props
import { Box, Stack, Circle } from './styled-system/jsx'
function App() {
return (
<Stack direction="row" p="4" rounded="md" shadow="lg" bg="white">
<Circle size="5rem" overflow="hidden">
<img src="https://via.placeholder.com/150" alt="avatar" />
</Circle>
<Box mt="4" fontSize="xl" fontWeight="semibold">
John Doe
</Box>
<Box mt="2" fontSize="sm" color="gray.600">
john@doe.com
</Box>
</Stack>
)
}
- JSXで使う前提
- PatternをJSXエレメントとして扱える。Chakraにあるようなやつ
- 設定(panda.config.ts)で
jsxFramework
を指定する -
const StyledButton = styled(Button)
もある。なんでもあるな… - 「実行時プロパティ名を変更するのはできないよ」とある
3. style-recipes
import { styled } from './styled-system/jsx'
import { cva } from './styled-system/css'
export const badge = cva({
base: {
fontWeight: 'medium',
borderRadius: 'md',
},
variants: {
status: {
default: {
color: 'white',
bg: 'gray.500',
},
success: {
color: 'white',
bg: 'green.500',
},
},
}
})
export const Badge = styled('span', badge)
- このcvaはあのcvaとは違いPanda用に作られたもの
- This API was inspired by Stitches(opens in a new tab), Vanilla Extract(opens in a new tab), and Class Variance Authority(opens in a new tab).と書いてある
- UIコンポーネントはこれとテーマ/トークンを組み合わせて作っていくという感じになりそう
styled-sysytemってなんだ
- CSS-in-JSの構文(オブジェクトまたはテンプレートリテラル)をクラス名の文字列に変換するために、軽量なランタイムが必要
-
panda
またはpanda codegen
コマンドを実行する際、出力ディレクトリにランタイムが生成される - パターン、レシピ、JSXコンポーネントにも使用され、すべてのスタイルがcss関数を介して処理される
- Astro や RSC などでコンポーネントを静的HTMLにプリレンダリングする場合、css関数などはビルド時に削除され、生成されたクラス名に置き換えられる
ということは、テーマやデザイントークンを更新したりすると、コードジェネレートが必要になるのかな。
動的にスタイリングしたい場合
- CSSを事前に生成するため色々制約がある
- UIコンポーネントを提供するケースでは、事前に生成済みのものを使うという感じになるので考慮が必要
ダメな例
// ❌ Avoid: Runtime value (without config.`staticCss`)
const Button = () => {
const [color, setColor] = useState('red.300')
return <styled.button color={color} />
}
// ❌ Avoid: Referenced value (not statically analyzable or from another file)
<styled.div color={getColor()} />
<styled.div color={colors[getColorName()]} />
<styled.div color={colors[colorFromAnotherFile]} />
const CustomCircle = (props) => {
const { circleSize = '3' } = props
return (
<Circle
// ❌ Avoid: Panda can't determine the value of circleSize at build-time
size={circleSize}
/>
)
}
Using Panda in a Component Library
PandaCSSを使って作ったデザインシステム(というかデザイントークンとUIコンポーネントの詰め合わせ)を展開する場合
おすすめパターン
↑のドキュメントで4パターン紹介されていた
- npmに公開せず、アプリケーションコードがPandaを使用している場合 → use ship build info approach
- アプリケーションコードがPandaを使用していない場合 → use the static css file approach
- アプリケーションコードが内部のモノレポにある場合 → use the include src files approach
- ライブラリコードがコンポーネントを提供せず、トークン、パターン、レシピのみを提供する場合 → use the ship preset approach
下2つは該当しなさそうなので上2つについて考えればいいか?
npmで公開する場合はどうなるんだ? → Use Panda as external package のところがそれに該当するっぽい
static css file approach
- コンポーネントライブラリとCSSをビルドして、それらを使ってもらう
- わかりやすい
- 生成されたCSSをカスタマイズできない
- これをどのくらい許容できるかだな
- そうは言ってもアプリケーション側でもスタイリングはする
- Pandaを使う → このパターンにマッチしなくなる、、
- Tailwindを使う → デザインシステムの管理どうするの、、
Use Panda as external package
-
outdir
用のワークスペースを作る- そこに styled-system を生成する
- なので、スタイルのために
@acme-org/styled-system
、UIコンポーネントのために@acme-org/components
と2つ(以上)のパッケージを持つ感じになる- いや、別にUIコンポーネントも同梱したければすればいいのか
-
@acme-org/components
からも@acme-org/styled-system
を使うしアプリケーションからはどちらも使う- アプリ側で
panda.config.ts
の設定が必要になる- アプリ側でテーマやトークンの拡張もできるのかな?できそうな気がする。
- アプリ側で
あらゆるところにPandaがいるという感じがするけど、アプリ側で個別にスタイリングできるし、拡張性もありそう。
アプリ側でPandaのインストールや設定が必要になるけど、まあそれはそういうものだしなー…
Ship the build info file
- ↑のアプローチに似てるけど、ソースコードをshipするのではなくて、build info fileをshipする
- なるほどー??
- build info fileには静的抽出結果が含まれている。それを元にビルドすすれば同じ結果が得られる
- アプリ側でこれを生成する必要がない
- んー。presetをシップするパターンと比べた優位性がわからない、、
- UIコンポーネントとトークンを分離するならプリセットが共有されればいいのでは?
- んー。presetをシップするパターンと比べた優位性がわからない、、
- アプリ側でこれを生成する必要がない