2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TailwindからPanda CSSへの移行:生成CSS・型制約・recipeの確認点

2
Last updated at Posted at 2025-03-10

2026-10-12 訂正:欠落したJSX、recipeのimport、生成先への手書き、CSSの適用手順、JIT・型制約・性能の説明を訂正しました。個人の移行実績は確認できていないため撤回し、公式資料に基づく移行候補として説明します。

移行する理由を先に決める

Tailwindはユーティリティクラス、Pandaはスタイルオブジェクトやrecipeを中心に記述します。どちらもビルドでCSSを生成できます。Tailwind公式はソース内のクラスを検出する仕組みを説明しています。「JITですべてのutilityをビルドする」という旧比較は誤りでした。

Pandaの型補完・token・variantを共通設計へ組み込みたい場合は候補になります。Tailwindで既に十分な運用ができている場合は、変更費用やplugin依存、採用版、チームの習熟も比較します。単に新しいという理由だけで全面移行を推奨しません。

Pandaの「zero runtime」は主に実行時のCSS生成・挿入を不要にする説明です。通常のcss()等のクラス組立用JavaScriptまで必ずゼロになるわけではありません。Next.js統合公式は通常の小さなruntimeと、source transformによる置換も区別しています。Tailwindから変えるだけで必ず初期表示が速くなるとは保証できません。

Next.jsへの接続:codegenだけでは終わらない

以下は公式の現在のv2資料に沿う、src/app構成の候補です。既存の設定をバックアップし、採用版とlockfileを固定して、初期化が作る差分を確認します。以下のインストールやアプリ実行は今回行っていません。

npm install -D @pandacss/dev
npx panda init --postcss

現在の初期化が用意するpreset設定を維持します。src以外にもコンポーネントがあればincludeへ追加し、既存PostCSS設定へpluginを統合します。既存ファイルを丸ごと置換しません。

// panda.config.ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
  presets: ['@pandacss/preset-base', '@pandacss/preset-panda'],
  preflight: true,
  include: ['./src/**/*.{js,jsx,ts,tsx}'],
  exclude: [],
  outdir: 'styled-system',
  theme: { extend: { tokens: {
    colors: { brand: { value: '#3b82f6' } }
  } } }
})
// postcss.config.cjs:既存pluginがあれば保持して統合する
module.exports = { plugins: { '@pandacss/dev/postcss': {} } }

package.jsonのscriptsへ次を統合します。JSONへコメントは書けません。

{
  "scripts": {
    "prepare": "panda codegen",
    "dev": "next dev",
    "build": "panda codegen && next build",
    "start": "next start"
  }
}

src/app/globals.cssにはlayer順を置き、src/app/layout.tsxからimportします。この経路ではPostCSSがCSS抽出を担います。型/helper生成だけをするcodegenとCSS生成・配信を区別します。

@layer reset, base, tokens, recipes, utilities;
// src/app/layout.tsxの既存importへ追加
import './globals.css'

旧panda watchはpanda --watchへ訂正します。CLI watch方式を選ぶ場合は、生成stylesheetを適切な相対pathからimportし、PostCSS方式との二重出力を避けます。ここではPostCSS方式を使うためwatchを追加しません。Tailwindとの併用中はreset・layer・selectorの競合を比較し、移行が終わる前に旧CSSを削除しません。

JSXを含む比較例

次はsrc/components/Card.tsxを想定しています。rootのstyled-systemへの相対pathもファイル位置で変わります。Tailwind版とPanda版は色や間隔の見た目が等しいか別途確認してください。

import { css, cva } from '../../styled-system/css'
import type { ReactNode } from 'react'

export function TailwindCard() {
  return <article className="rounded-lg bg-white p-4 shadow-md">
    <h2 className="text-xl font-bold">カードタイトル</h2>
    <p className="mt-2 text-gray-700">説明文</p>
  </article>
}

export function PandaCard() {
  return <article className={css({ rounded: 'lg', bg: 'white', p: '4', shadow: 'md' })}>
    <h2 className={css({ fontSize: 'xl', fontWeight: 'bold' })}>カードタイトル</h2>
    <p className={css({ mt: '2', color: 'gray.700' })}>説明文</p>
  </article>
}

const buttonStyle = cva({
  base: { rounded: 'md', fontWeight: 'semibold', px: '4', py: '2' },
  variants: {
    visual: {
      solid: { bg: 'blue.500', color: 'white', _hover: { bg: 'blue.600' } },
      outline: { bg: 'transparent', color: 'blue.500', borderWidth: '1px', borderColor: 'blue.500' }
    }
  },
  defaultVariants: { visual: 'solid' }
})

export function Button({ children, visual = 'solid' }: {
  children: ReactNode; visual?: 'solid' | 'outline'
}) {
  return <button type="button" className={buttonStyle({ visual })}>{children}</button>
}

Recipe公式に沿い、この例はgenerated cssからcvaをimportして、手書きのコンポーネントファイルへ置きます。旧recipeを@pandacss/devからimportする例は撤回しました。共有するconfig recipeはdefineRecipeで設定のtheme.recipes等へ登録し、codegen後のhelperを使います。styled-systemは生成先であり、そこへ独自recipeを手書きして再生成で失う構成を避けます。

汎用patternという旧importも撤回しました。必要な場合に生成された具体的なpattern helperを選び、任意の名前が存在するとは扱いません。単純なlayoutならまずcssで十分か判断します。

型と静的抽出の限界

型制約の公式説明ではstrictTokensは追加設定です。既定でtoken以外をすべて拒否するという旧説明を訂正します。有効化してもtokenと結び付くpropertyを対象とし、raw値のescape hatchもあります。これはruntime入力の安全性や画面の正しさを保証する機能ではありません。

動的スタイル公式に沿い、利用者入力やAPI応答の値をそのままcss({ bg: runtimeColor })へ渡しても、必要なCSSが生成されるとは限りません。有限の候補はliteral/recipe variantで明示します。任意の値が必要ならCSS変数等を検討し、許可値・範囲・適用先を検証します。staticCssで広く生成すると出力量も増えるため測定します。

tokenの命名だけを合わせても、breakpoint、line-height、reset、色、forms/typography pluginの挙動が同じとは限りません。旧textColor utilityの不完全なalias例や、h1だけでTypography相当になる説明は撤回しました。既存の必要な表示・interactionを抽出し、個別に移植します。

自動変換は差分レビューの補助

tw2pandaのREADMEで確認できるCLI例です。採用するversionのhelpと対応範囲を確認します。

npx tw2panda rewrite src/components/Button.tsx
npx tw2panda convert -s "p-4 text-blue-500 hover:bg-gray-100"

rewriteは既定で標準出力へ結果を出し、ファイルへ書くには-wが必要です。-sはshorthandを指定します。旧説明の「実行するとファイルが変わる」「常に短縮propertyで出る」を訂正しました。変換後のimport、custom token/plugin、動的class、responsive・状態selectorを確認し、変換できたことを移行完了と扱いません。今回はツールをインストール・実行していません。

完了を判断するための検証

掲載TS/TSX/JavaScriptの構文とJSONのparseは確認しました。Pandaのcodegen、生成型との型検査、Next build、CSS生成、ブラウザ表示は未実行です。生成されるclass名は設定や版で変わるため、旧記事のCSS文字列を正確な出力として残していません。

本移行では一つの画面で、buildと型検査、CSSが配信されること、mobile/desktop、hover/focus/disabled、dark mode、keyboard、reset競合を確認します。bundle/CSS量と表示指標を同条件で比較し、戻せる段階ごとに進めます。今回の例だけで完全移行や高速化が実証されたとは扱いません。

この記事を書いた人✏️@YushiYamamoto
ITPRODX.com代表 / AIアーキテクト
Next.js / TypeScript / n8nを活用した自律型アーキテクチャ設計を専門としています。
日々の自動化の検証結果や、ビジネス側の視点(ROI等)に関するより深い考察は、以下の公式サイトおよびnoteで発信しています。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?