コンテンツにスキップ
Documents for MorningStatusApp
Esc
↑↓移動↵開く⌘Jプレビュー
このページの内容

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.ts
    • include: ./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)

このページは役に立ちましたか?