Panda CSS 設計書
最終更新: 2026-09-25(新規作成、#1655)
1. 概要
フォールダブル端末・広画面対応(MorningStatusApp#1648)を契機に導入した Panda CSS のセットアップ内容と、既存スタック(Tailwind CSS v4・CSS Modules・app/globals.css)との共存方式を扱う設計書(MorningStatusApp#1655)。
Panda CSS は Zero-Runtime(ビルド時にCSSを静的生成する)かつ TypeScript の型補完が効くスタイリングライブラリ。フォールダブル端末の展開判定(~580px付近)のようなカスタムブレークポイントを型安全に扱える点、および Next.js App Router との親和性を理由に採用した。
実際にコンポーネント・画面へ Panda CSS を適用していく段階移行の範囲は本書の対象外で、MorningStatusApp#1656 側で決定する。本書は、その前提となる「導入設定」と「既存スタックとの共存方式」の確定内容に限定する。長期的にはTailwind CSSを撤去しPanda CSS単独の構成へ移行する方針であり、その位置づけは§5を参照。
2. セットアップ内容
- 依存関係:
@pandacss/devを devDependency として追加 - 設定ファイル: リポジトリルートの
panda.config.tsinclude:./app/**/*.{js,jsx,ts,tsx}と./components/**/*.{js,jsx,ts,tsx}(Next.js App Router のディレクトリ構成に合わせる。panda initの初期値であるsrc/・pages/はこのプロジェクトでは使わない)preflight: false(§3-1参照)outdir: "styled-system"(生成物。.gitignoreに追加し、コミット対象外とする。bunx panda codegenまたは後述のPostCSSプラグインがビルド時に自動生成する)
- PostCSS統合:
postcss.config.mjsのpluginsに@tailwindcss/postcssと並べて@pandacss/dev/postcssを追加。panda init --postcssは初期状態でpostcss.config.cjsを別ファイルとして生成するため、既存のpostcss.config.mjsに統合し、重複するPostCSS設定ファイルを残さないこと app/globals.cssの@import "tailwindcss";の直後に@layer reset, base, tokens, recipes, utilities;を追加。@pandacss/dev/postcssはビルド時にこの宣言の位置へ生成CSSを注入する
3. Tailwind CSS v4 との共存方式
3-1. カスケードレイヤーの優先順位
Tailwind CSS v4 は @import "tailwindcss" の展開により @layer theme, base, components, utilities; を、Panda CSS は @layer reset, base, tokens, recipes, utilities; をそれぞれ宣言する。CSSの @layer は同名レイヤーであれば宣言位置に関わらず1つに統合され、初出のレイヤー名だけがその位置に新規追加される仕様のため、実際のレイヤー優先順位(低→高)は次のようになる。
theme < base(Tailwind+Panda共有) < components < utilities(Tailwind+Panda共有) < reset < tokens < recipes
base と utilities は Tailwind・Panda 両方が同名レイヤーを使うため統合され、レイヤー内では後から追加されたCSSほど優先される(globals.css 内で Tailwind の @import より Panda の @layer 宣言が後にあるため、同一プロパティが競合した場合は Panda 側の指定が勝つ)。実機検証(Playwright、class="bg-red-500" と css({ bg: "blue.500" }) を同一要素に適用)でも Panda側が優先されることを確認済み。
備考: Panda の
preflight(ブラウザデフォルトスタイルのリセット)はfalseに設定している。Tailwind v4 が@import "tailwindcss"により自前の preflight をbaseレイヤーへ既に注入しており、二重適用を避けるため。
3-2. レイヤー外(unlayered)CSSは常に最優先
app/globals.css の :root 変数定義や *, html, body 等、@layer で囲まれていないCSSは、CSS仕様上どの @layer 内のCSSよりも常に優先される。既存のグラスモーフィズム等のカスタムスタイルはこの位置にあり、Panda・Tailwindいずれのユーティリティクラスとも衝突しない。
3-3. .container → .app-container への改名と、Panda化を見送った経緯(重要な注意点)
備考(バグとして顕在化した問題と対策): 共通レイアウトの最大幅(1200px)を実現していた独自クラス
.container(app/globals.css、app/layout.tsx等)を Panda 化する過程で、2段階の技術的な非互換が技術検証中に判明した。問題1(クラス名の衝突):
.containerという名前のまま Panda のglobalCssに移行したところ、幅1920pxで通常画面のmax-widthが意図しない1536pxになった。原因は、Tailwind CSS v4 が@import "tailwindcss"により、ソース内でcontainerというクラス名トークンが使用されていると、組み込みのレスポンシブ.containerユーティリティ(@layer utilities、ブレークポイントごとにmax-widthが変わる)を自動生成するため。移行前は独自の.containerが@layerの外(unlayered)にあり §3-2 の理由で常に最優先されていたため問題にならなかったが、Panda のglobalCss(baseレイヤー)に移した時点で、より優先度が高いutilitiesレイヤーにある Tailwind組み込み.containerに負けるようになった。→ 対策として、クラス名を
.app-containerに改名し、Tailwindの組み込み.containerと名前の衝突を避けた(ソース上のトークンcontainerが無くなることで、TailwindのJITスキャンが組み込みユーティリティを生成しなくなる効果もある)。問題2(プロジェクト自身のレイヤー外リセットとの衝突。改名だけでは解決しない):
.app-containerに改名した後も、padding/marginが0になる不具合が残った。原因は、app/globals.cssの* { box-sizing: border-box; padding: 0; margin: 0; }という、このプロジェクト自身が持つレイヤー外のリセットCSS。§3-2の通りレイヤー外CSSは常に最優先されるため、Panda のglobalCss(レイヤー内)に置いた.app-containerのpadding/margin指定は、詳細度に関わらずこのリセットに負ける。Tailwindとは無関係に、「レイヤー外CSSを前提にした既存のglobals.cssの構造」と「Panda の globalCss(レイヤー内)」がそもそも噛み合わないという、より根本的な非互換であることが分かった。最終対応:
.app-containerのクラス名改名は維持しつつ、CSS定義自体はPandaのglobalCssに移さず、従来通りapp/globals.cssへレイヤー外の生CSSとして書く方針に変更した(§5「将来方針」も参照)。globals.css側のレイヤー外リセット構造ごと見直す(全廃してPanda/Tailwindのレイヤー体系に揃える)選択肢も検討したが、影響が全画面に及ぶ大改修になるため本Issueの範囲外とした。
.app-container の実体は次の通り(app/globals.css。レイヤー外の生CSSとして維持)。
/* Main Layout Grid */
.app-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 2rem;
}
/* 画面幅いっぱいに使う画面(最大幅の制限を外す。世代マトリックス画面) */
main.app-container:has(> [data-wide-canvas]) {
max-width: none;
}
@media (max-width: 768px) {
.app-container {
padding: 0 1rem;
}
}
ワイドキャンバス例外(世代マトリックス画面のみ最大幅を外す仕組み)の考え方自体は変更していない。世代マトリックス 画面設計書 §4-8を参照。data-wide-canvas 属性をページ直下に付与する実装(app/generational-matrix/page.tsx)も変更なし。
影響を受けた参照元(className="container" → className="app-container" に改名): app/layout.tsx(header・main・footer)、app/ameba/page.tsx、app/instagram/page.tsx、app/instagram/[accountId]/page.tsx、app/tiktok/page.tsx、app/tiktok/official/[accountId]/page.tsx、app/tiktok/og/[memberId]/page.tsx、app/youtube/page.tsx、app/youtube/official/page.tsx、app/youtube/og/[memberId]/page.tsx。
4. カスタムブレークポイント(fold)
フォールダブル端末の展開時(~580px付近)を判定するため、panda.config.ts の theme.extend.breakpoints に fold: "584px" を追加した。Panda のブレークポイントは min-width ベース(モバイルファースト)で解釈されるため、CLAUDE.mdのモバイルファースト規約と自然に合致する。
theme: {
extend: {
breakpoints: {
fold: "584px",
},
},
},
デフォルトのブレークポイント(sm・md・lg・xl・2xl)は extend により維持される。584pxという値は、Samsung Galaxy Fold系の展開時インナーディスプレイ幅を目安とした一般的な業界慣行値。
実際にこのブレークポイントを使う一覧画面(ライブ一覧・SNSアカウント一覧等)への適用は、MorningStatusApp#1656 の対象。
5. 段階移行の方針と将来方針
本Issueでは、上記のセットアップと .app-container(ワイドキャンバス機構。§3-3の理由によりCSS定義自体は globals.css に残す)の対応のみを対象とする。それ以外のコンポーネント・画面をPanda化する範囲・優先順位は、本書で確立した共存方式をもとに MorningStatusApp#1656 側で決定する。
将来方針: Tailwind CSS の撤去とPanda CSS単独化
§3-3で判明した2つの非互換(Tailwind組み込みユーティリティとの命名衝突、globals.css のレイヤー外リセットとの構造的な非互換)は、いずれも「TailwindとPandaが同居し、かつ globals.css にレイヤー外CSSが残っている」という過渡的な状態に起因する。共存を維持し続けるコストを避けるため、Tailwind CSS v4 を撤去し、Panda CSS単独の構成へ移行する方針とする。Tailwindが無くなれば組み込みユーティリティとの命名衝突は原理的に発生しなくなり、globals.css のレイヤー外リセット(:root 変数・* {...} 等)もPandaの globalCss(またはPandaのpreflight)に置き換えれば、.app-container を含む共通レイアウトのCSSも安全にPanda化できるようになる。
この撤去(既存画面・コンポーネントで使われている全てのTailwindユーティリティクラスをPandaの書き方に置き換える作業。#1655時点の調査では対象はapp/・components/配下223ファイル中25ファイル)は、MorningStatusApp#1656 のフォールダブル対応と一体で実施する方針に変更した(当初は段階移行が十分に進んだ後の別Issueとする想定だったが、Tailwindとの共存コスト(§3-3)を踏まえ前倒しした)。
改訂履歴(バージョン降順に記載)
| 版 | 更新日 | 変更内容 |
|---|---|---|
| 1.0 | 2026-09-25 | 新規作成(#1655) |