水戸地図(β)

2025年08月11日

Panda CSS v1で導入されたJSX Style ContextでSlot Recipeがより便利に

概要

2025年8月、 Panda CSS v1が正式リリースされました
このバージョンからJSX Style Context機能が標準搭載され、複合コンポーネントの利用がより柔軟かつ直感的に行えるようになりました。

特に、Slot Recipeを用いた複数部位(slot)を持つコンポーネント開発において、スタイルの一括管理と再利用が容易になります。また、 Ark UI やRadix UIなどのヘッドレスUIライブラリとの統合にも適しています。

JSX Style Context ドキュメント
https://panda-css.com/docs/concepts/jsx-style-context

対象読者

  • Panda CSSを利用しており、Slot Recipeの基本を理解している方
  • 複合コンポーネントやUIライブラリのスタイリング方法を知りたい方
広告

JSX Style Contextとは

Panda CSSは、 Chakra UI の開発チームが作ったゼロランタイム CSS-in-JSライブラリです。
v1で新たに導入されたcreateStyleContext関数により、親コンポーネントのpropsに応じて子コンポーネントのスタイルを自動的に切り替えることが容易に可能となりました。

これにより、以下のような直感的な記述が可能になります。

<Card.Root variant="subtle">
  <Card.Image src="hoge.png" alt="hoge" />
  <Card.Body>Hello, Panda</Card.Body>
</Card.Root>

使い方

1. Slot Recipeを定義してPanda Configに登録

まず、複数のslot(部位)を持つコンポーネントのスタイル定義を行います。その後、Panda ConfigにSlot Recipeを設定してください。

import { defineSlotRecipe } from "@pandacss/dev";

// ["root", "image", "body"]のslotからなるカード
export const card = defineSlotRecipe({
  className: "card",
  slots: ["root", "image", "body"],
  base: {
    root: {
      display: 'flex',
      borderWidth: '1px',
      borderRadius: 'lg',
      overflow: 'hidden',
      bg: 'white',
      shadow: 'md',
    },
  },
  variants: {
    layout: {
      vertical: {
        root: { flexDirection: 'column' },
        image: { w: '100%', h: 48, objectFit: 'cover' },
        body: { p: 4 },
      },
      horizontal: {
        root: { flexDirection: 'row' },
        image: { w: 48, h: 'auto', objectFit: 'cover' },
        body: { p: 6 },
      },
    },
  },
});

2. createStyleContextで複合コンポーネントを構築

Panda CSSのビルド後、styled-system/recipesからSlot Recipeを読み込み、createStyleContextでスタイル情報を共有できるコンポーネントを作成します。

// src/components/ui/card.tsx
import { card } from "styled-system/recipes";
import { createStyleContext } from "styled-system/jsx";

const { withProvider, withContext } = createStyleContext(card);

// 第1引数に要素名、第2引数にslot名、第3引数に任意でオプションを設定できる
const Root = withProvider("div", "root", { defaultProps: { className: "group" } });
const Image = withContext("img", "image");
const Body = withContext("div", "body");

export const Card = { Root, Image, Body };

3. コンポーネントを使用

import { Card } from "@/components/ui/card";

export function Example() {
  return (
    <Card.Root layout="horizontal">
      <Card.Image src="/panda.png" alt="Panda" />
      <Card.Body>Hello, Panda</Card.Body>
    </Card.Root>
  );
}

Panda CSS Playground
https://play.panda-css.com/DEI09SMXdb

利点

直感的なAPI

既存のUIライブラリに近い構文で記述でき、スタイル定義とコンポーネント構造をシンプルに保てます。

ヘッドレスUIライブラリとの相性

特にArk UIRadix UIのようなスタイル非依存のUIライブラリとの統合で威力を発揮します。 次項でArk UIをスタイリングするサンプルコードを掲載しています。

コンポーネントライブラリの構築が容易

createStyleContextは、Panda CSSを使ったUIキット「 Park UI 」や、Emotionベースの Chakra UI でも既に類似の仕組みが採用されています。
今回のv1での導入は、それらの実装アイデアを逆輸入した公式対応といえます。

Ark UI と Panda CSS の組み合わせでのスタイリングパターン

Ark UI はヘッドレス UI コンポーネントライブラリで、ロジックとアクセシビリティを提供します。 見た目のスタイルは自分で決める必要があり、CSS フレームワークやデザインシステムとの組み合わせが前提です。

ここでは、 Panda CSS と組み合わせた スタイリングのコードパターン を紹介します。 実際の UI 表示例ではなく、withRootProviderwithContext などを用いたスタイル適用の構造にフォーカスしています。

スタイリング例

// recipes/dialog.ts
import { defineSlotRecipe } from "@pandacss/dev";
import { anatomy as dialogAnatomy } from "@ark-ui/react/dialog";

// Ark UI の Dialog に対応する slot を定義
export const dialog = defineSlotRecipe({
  className: "dialog",
  slots: dialogAnatomy.keys(),
  base: {
    root: { display: "flex", alignItems: "center", justifyContent: "center" },
    backdrop: { bg: "blackAlpha.700" },
    content: { bg: "white", p: "4", rounded: "lg", shadow: "lg" },
    title: { fontSize: "lg", fontWeight: "bold" },
    description: { color: "gray.600" },
    closeTrigger: { position: "absolute", top: "2", right: "2" },
  },
});
// components/ui/dialog.tsx
import * as ArkDialog from "@ark-ui/react/dialog";
import { dialog } from "styled-system/recipes";
import { createStyleContext } from "styled-system/jsx";

const { withRootProvider, withContext } = createStyleContext(dialog);

export const DialogRoot = withProvider(ArkDialog.Root, "root");
export const DialogBackdrop = withContext(ArkDialog.Backdrop, "backdrop");
export const DialogPositioner = withContext(ArkDialog.Positioner, "positioner");
export const DialogContent = withContext(ArkDialog.Content, "content");
export const DialogTitle = withContext(ArkDialog.Title, "title");
export const DialogDescription = withContext(ArkDialog.Description, "description");
export const DialogCloseTrigger = withContext(ArkDialog.CloseTrigger, "closeTrigger");

export const Dialog = {
  Root: DialogRoot,
  Backdrop: DialogBackdrop,
  Positioner: DialogPositioner,
  Content: DialogContent,
  Title: DialogTitle,
  Description: DialogDescription,
  CloseTrigger: DialogCloseTrigger,
};

この構造のポイント

  1. withRootProvider

    • コンポーネントツリーのルートにスタイルコンテキストを提供します
    • 他の子要素が withContext で同じコンテキスト名を使うと、自動でスタイルが共有されます
  2. withContext

    • 同一コンテキストの Root から props や状態 (disabled, invalid など) を受け取れます
    • スタイルやクラスの一貫性が保たれます
  3. Panda CSS の recipe

    • スタイルのバリエーションを型安全に切り替えることができます

まとめ

  • JSX Style Contextは、Slot Recipeを用いた複合コンポーネント開発を劇的に改善します
  • Ark UIやRadix UIなどのヘッドレスUIライブラリとの統合に特に有効です
  • UIコンポーネントライブラリの設計パターンを、よりシンプルかつ再利用性の高い形で実現できます

いかにもChatGPTが生成した文章となってしまいましたが、createStyleContextとSlot Recipeの組み合わせは非常に便利という大意を汲み取ってくれると嬉しいです。

リンク

Panda CSS
panda-css.com
Park UI
park-ui.com

Take Your Aim / Rocket (2025)

広告

2025年08月11日

Panda CSS v1で導入されたJSX Style ContextでSlot Recipeがより便利に

技術記事

Top

水戸地図(β)