Next.js 集成 Panda CSS:四种类型安全样式方案的配置原理与实战实现
本文基于 Next.js 官方仓库中的 panda-css 示例,讲解如何在一个 App Router 项目中接入 Panda CSS 这一零运行时 CSS 解决方案。示例项目用同一组"链接"演示了 Panda CSS 支持的四种样式编写方式——Atomic Recipe(CVA)、Atomic Style、Config Recipe 与 Text Styles,并完整给出了 Panda 配置文件、PostCSS 入口与全局 CSS 的搭建细节。读完本文,你能够理解 Panda 各配置项的用途、四种样式方案的差异与取舍,并掌握从零初始化一个 Panda CSS 项目的完整步骤。
一、示例项目总览:一个项目演示四种样式方案
Panda CSS 是一个零运行时、构建时生成原子 CSS 的方案:它通过扫描源码中的样式声明,在构建阶段提取并生成最小化的 CSS,从而避免运行时计算样式带来的开销。Next.js 仓库中的这个示例(examples/panda-css)刻意用最简单的场景——一个链接——分别以 Panda 支持的四种方式实现同样的视觉效果,方便读者对照理解各方案的定位:
- Atomic Recipe(CVA):以类型安全的运行时 API 创建多变体原子样式。样式定义写在组件文件里,按需调用
cva生成 className。 - Atomic Style:可复用的原子样式,类型安全且 CSS 体积开销小。
- Config Recipe:在 Panda 配置中定义的"配置式配方",被提取出来并在构建时即时(just-in-time)生成。
- Text Styles:全局文本样式,用于统一、可读性一致的排版。
从源码结构看,项目页面 app/page.tsx 就是一个极简的展示页:一个 flex 列布局的 <main> 容器,依次渲染四个演示组件:
import { css } from "@/styled-system/css";
import LinkWithAtomicStyle from "@/app/components/link-with-atomic-style";
import LinkWithAtomicRecipe from "@/app/components/link-with-atomic-recipe";
import LinkWithConfigRecipe from "@/app/components/link-with-config-recipe";
import LinkWithTextStyles from "@/app/components/link-with-text-styles";
const styles = css({
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "2rem",
p: "6rem",
minH: "100vh",
});
export default function Home() {
return (
<main className={styles}>
<LinkWithAtomicStyle />
<LinkWithAtomicRecipe />
<LinkWithConfigRecipe />
<LinkWithTextStyles />
</main>
);
}
注意其中的两个细节:
css函数不是手写的,而是从@/styled-system/css导入——styled-system目录由 Panda 在构建时自动生成(见下文outdir配置),这也解释了为什么 tsconfig.json 的include中显式包含了"styled-system",以便 TypeScript 能识别生成的类型声明;- 每个组件内的链接都附带
<span className="icon">-></span>箭头装饰,其样式来自 Panda 配置里的全局 CSS(见下文)。
依赖与版本前提
从 package.json 可以看到示例的依赖组合:
next: "latest"、react与react-dom为^18.2.0;- Panda 相关依赖只有
@pandacss/dev(^0.15.1),作为 devDependency 安装——Panda 是纯构建时工具,运行时不需要它; - 脚本中有一条关键配置:
"prepare": "panda codegen",即每次安装依赖时自动执行panda codegen,生成styled-system类型与原子类名映射。
{
"private": true,
"scripts": {
"prepare": "panda codegen",
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint ."
}
}
二、核心配置解析:panda.config.ts
整个示例的"灵魂"是 panda.config.ts。下面按配置项逐一拆解。
2.1 文本样式定义(Text Styles)
import {
defineConfig,
defineGlobalStyles,
defineRecipe,
defineTextStyles,
} from "@pandacss/dev";
export const textStyles = defineTextStyles({
link: {
description: "The classic link text style - used in demo links",
value: {
fontSize: "lg",
fontFamily: "inter",
},
},
});
defineTextStyles 定义了名为 link 的文本样式,包含字号(lg)与字体族(inter)。它会被挂到 theme.extend.textStyles,之后任何地方都可以直接用 textStyle: "link" 引用,实现排版规则的集中管理。
2.2 配置式配方(Config Recipe)
export const linkRecipe = defineRecipe({
className: "link",
description: "The styles for the link component",
base: {
color: {
_default: "gray.800",
_osDark: "gray.100",
},
fontFamily: "inter",
},
variants: {
size: {
sm: { fontSize: "sm" },
lg: { fontSize: "lg" },
},
},
defaultVariants: {
size: "lg",
},
});
这里体现了 Panda 的几个典型特性:
className: "link":声明生成类名前缀为link,构建后组件端即可通过生成的link()函数(见 link-with-config-recipe.tsx)按需取用;_default/_osDark条件语法:这是 Panda 的"自响应式"(self-responding)语法——_osDark表示"当操作系统处于深色模式时",无需手写@media (prefers-color-scheme: dark);variants+defaultVariants:声明式变体系统,size取sm/lg两个字号,默认lg。
2.3 全局样式
const globalCss = defineGlobalStyles({
html: {
bg: {
_default: "white",
_osDark: "black",
},
"& .icon": {
ml: 2,
fontSize: "lg",
fontWeight: 700,
},
},
});
两个值得注意的点:html 背景色同样使用了深色模式自响应;& .icon 选择器为页面中所有带 icon 类的箭头装饰统一设置左边距、字号与字重——这正是四个演示组件里 <span className="icon">-></span> 的样式来源。全局样式集中定义在配置中,而不是散落在各处 CSS 文件里。
2.4 主配置:defineConfig
export default defineConfig({
// Whether to use css reset
preflight: true,
// Where to look for your css declarations
include: ["./app/**/*.{js,jsx,ts,tsx}"],
// Files to exclude
exclude: [],
// Useful for theme customization
theme: {
extend: {
tokens: {
fonts: {
inter: { value: "var(--font-inter)" },
},
},
recipes: {
link: linkRecipe,
},
textStyles,
},
},
// The output directory for your css system
outdir: "styled-system",
// Global styles
globalCss,
});
各配置项的作用:
| 配置项 | 取值 | 作用 |
|---|---|---|
preflight |
true |
启用 CSS reset,统一各浏览器默认样式 |
include |
./app/**/*.{js,jsx,ts,tsx} |
样式扫描范围,Panda 只解析匹配文件中的 css、cva 等调用 |
exclude |
[] |
排除文件列表 |
theme.extend.tokens.fonts.inter |
var(--font-inter) |
扩展设计令牌,把 inter 字体族指向 CSS 变量 --font-inter |
theme.extend.recipes.link |
linkRecipe |
注册配置式配方,生成 styled-system/recipes 中的 link 导出 |
theme.extend.textStyles |
textStyles |
注册 link 文本样式 |
outdir |
styled-system |
类型与原子类映射的输出目录,组件正是从 @/styled-system/... 导入 |
globalCss |
上文定义 | 注入全局样式 |
其中 tokens.fonts.inter: "var(--font-inter)" 与 Next.js 的字体方案衔接得很巧妙:Panda 并不自己下载 Inter 字体,而是引用 Next.js next/font 注入的 CSS 变量(见下节),让两套系统各取所长。
三、字体接入:next/font 与 Panda 令牌协作
app/layout.tsx 展示了 Panda 与 Next.js 字体系统的标准接法:
import "./global.css";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
const InterFont = Inter({
weight: ["400", "500", "700"],
display: "swap",
subsets: ["latin"],
variable: "--font-inter",
});
export const metadata: Metadata = {
title: "Create Next App with Panda CSS",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" className={`${InterFont.variable}`}>
<body>{children}</body>
</html>
);
}
调用链非常清晰:Inter() 的 variable: "--font-inter" 让 Next.js 在 <html> 上输出 --font-inter CSS 变量,<html className={InterFont.variable}> 负责挂上字体变量类;而 panda.config.ts 中的 tokens.fonts.inter 恰好声明了 var(--font-inter)。于是组件里写 fontFamily: "inter" 时,Panda 生成的 CSS 最终解析到 Next.js 自托管的 Inter 字体变量上——字体加载、字体令牌、原子类三层完全解耦又互相咬合。
同时注意 import "./global.css" 必须放在根 Layout 中,它是 Panda 输出的 CSS 入口。
四、入口 CSS 的 Layer 声明与 PostCSS 插件
Panda CSS 基于 CSS Cascade Layers 工作,入口 CSS 文件 app/global.css 的全部内容只有一行:
/* https://panda-css.com/docs/installation/nextjs#configure-the-entry-css-with-layers */
@layer reset, base, tokens, recipes, utilities;
这行声明确立了五层的优先级顺序:reset < base < tokens < recipes < utilities。Panda 构建时生成的 reset、令牌、配方与工具类会分别落入对应层,原子类(utilities 层)天然具有最高优先级,覆盖关系因此可预测。
构建链路的另一端是 postcss.config.js:
module.exports = {
plugins: {
"@pandacss/dev/postcss": {},
},
};
Next.js 在编译 global.css 时会经过 PostCSS,@pandacss/dev/postcss 插件负责扫描 include 范围内的源码、收集 css/cva 调用,并在构建时生成原子类 CSS 与 styled-system 类型输出。而 next.config.js 是空的(const nextConfig = {};)——接入 Panda 完全不需要改动 Next.js 配置,这也是"构建时方案、零运行时依赖"的直观体现。
五、四种样式方案的组件实现对照
5.1 Atomic Style:组件内直接声明
link-with-atomic-style.tsx 是最直接的用法:
import { css } from "@/styled-system/css";
const styles = css({
fontSize: "lg",
color: {
_default: "gray.800",
_osDark: "gray.100",
},
fontFamily: "inter",
});
export default function LinkWithAtomicStyle() {
return (
<a
className={styles}
href="https://panda-css.com/docs/concepts/writing-styles#atomic-styles"
target="_blank"
rel="noreferrer"
>
Link with <b>atomic style</b>
<span className="icon">-></span>
</a>
);
}
css() 返回一个类名字符串,Panda 从参数中提取出 font-size、color、font-family(含深色模式变体)等原子类。适合一次性、单组件使用的样式。
5.2 Atomic Recipe(CVA):组件内的变体系统
link-with-atomic-recipe.tsx 把变体逻辑提升到组件内:
import { cva } from "@/styled-system/css";
const styles = cva({
base: {
color: {
_default: "gray.800",
_osDark: "gray.100",
},
fontFamily: "inter",
},
variants: {
size: {
sm: { fontSize: "sm" },
lg: { fontSize: "lg" },
},
},
defaultVariants: {
size: "lg",
},
});
export default function LinkWithAtomicRecipe() {
return (
<a
className={styles({ size: "lg" })}
href="https://panda-css.com/docs/concepts/recipes#atomic-recipe-or-cva"
target="_blank"
rel="noreferrer"
>
Link with <b>atomic recipe</b>
<span className="icon">-></span>
</a>
);
}
cva(class variants authority)是"原子化配方":base 是共享基底,variants 声明可切换的属性组合,defaultVariants 给出默认取值,调用 styles({ size: "lg" }) 时按变体组合出最终类名。与 Config Recipe 相比,它的定义写在组件文件里、贴近使用点,适合"只有当前组件关心这套变体"的场景。
5.3 Config Recipe:集中定义、即时生成
link-with-config-recipe.tsx 只有一段核心代码:
import { link } from "@/styled-system/recipes";
export default function LinkWithConfigRecipe() {
return (
<a
className={link({ size: "lg" })}
href="https://panda-css.com/docs/concepts/recipes#config-recipe"
target="_blank"
rel="noreferrer"
>
Link with <b>config recipe</b>
<span className="icon">-></span>
</a>
);
}
样式本体在 panda.config.ts 中以 defineRecipe 声明并经 theme.extend.recipes.link 注册;Panda 据此在构建时"即时"生成 styled-system/recipes 模块导出的 link 函数。多组件复用同一套变体语义时,这是更收敛的做法——配方只有一份事实来源。
5.4 Text Styles:排版归一化
link-with-text-styles.tsx 展示了文本样式与原子样式的组合:
import { css } from "@/styled-system/css";
const styles = css({
textStyle: "link",
color: {
_default: "gray.800",
_osDark: "gray.100",
},
});
export default function LinkWithTextStyles() {
return (
<a
className={styles}
href="https://panda-css.com/docs/theming/text-styles#defining-text-styles"
target="_blank"
rel="noreferrer"
>
Link with <b>text styles</b>
<span className="icon">-></span>
</a>
);
}
textStyle: "link" 一行即引入配置中定义的 fontSize: "lg" 与 fontFamily: "inter",颜色等其余属性仍由局部 css 声明。四个方案对同一个"链接"实现了完全一致的视觉结果,但样式的事实来源与复用粒度各不相同:Text Styles 管排版、Atomic Style 管一次性原子声明、Atomic Recipe 管组件局部变体、Config Recipe 管跨组件共享配方。
六、本地运行与项目初始化
官方 README 提供了通过 create-next-app 一键引导示例的完整命令(支持 npm、Yarn、pnpm、Bun 四种包管理器):
npx create-next-app --example panda-css panda-css-app
yarn create next-app --example panda-css panda-css-app
pnpm create next-app --example panda-css panda-css-app
bunx create-next-app --example panda-css panda-css-app
执行后进入 panda-css-app 目录,pnpm install(安装时 prepare 脚本会自动执行 panda codegen),然后:
pnpm dev # 开发模式
pnpm build # 生产构建
pnpm start # 启动生产服务
若从本仓库直接使用示例目录,配置要求与上面一致:安装依赖后 panda codegen 会生成 styled-system 输出目录,next dev 的 PostCSS 编译会经 @pandacss/dev/postcss 插件产出原子 CSS。README 还提及可将项目部署到 Vercel 云端(deployment 文档见 Next.js 官方文档),部署时无需额外服务,产物即标准 Next.js 构建输出。
接入要点回顾
- 安装
@pandacss/dev(devDependency),PostCSS 中启用@pandacss/dev/postcss; - 编写
panda.config.ts:设置include扫描范围、outdir输出目录、preflight、主题令牌与配方; - 在根 Layout 引入入口 CSS,并用
@layer reset, base, tokens, recipes, utilities;声明层序; - 用
next/font的variable注入 CSS 变量,再通过 Panda 的tokens.fonts引用,打通字体链路; - 按"复用范围"选择样式方案:一次性用
css,组件变体用cva,跨组件共享用 Config Recipe,排版统一用 Text Styles。
按这条链路,你获得的是一套构建时零运行时开销、类型完备、且与 Next.js App Router 字体/布局体系无缝协作的样式基建。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00