返回项目目录
开发工具精选项目

vanilla-extract

在 TypeScript 中编写类型安全样式,并在构建时输出静态 CSS。

主要语言TypeScript
开源许可MIT
项目类型开发工具
维护状态活跃维护
OVERVIEW

项目概述

vanilla-extract 提供接近原生 CSS 的 TypeScript API、局部作用域类名、类型安全变量与主题,并通过构建工具集成生成零样式运行时的 CSS 文件。

vanilla-extract 是采用 MIT 许可证的零样式运行时 CSS-in-TypeScript 工具。开发者在 .css.ts 文件中调用 style、createTheme、createVar 等 API,使用普通 TypeScript 数据结构描述 CSS;构建插件执行这些模块,生成局部作用域类名、CSS Variables、Keyframes、Font Face 和静态 CSS 文件,而样式定义代码不会进入浏览器 JavaScript Bundle。它保持对标准 CSS 的轻量抽象,同时通过 CSSType 提供属性和值的类型检查。核心包之外,Sprinkles 可以生成项目专属的原子 Utility,Recipes 提供类型安全的组件 Variant,Dynamic 则仅在确实需要运行时值时更新预先声明的 CSS Variables。

当前稳定版v1.21.2
样式文件.css.ts
输出Static CSS
FEATURES

主要特点

vanilla-extract 把 TypeScript 的类型、模块和组合能力用于样式作者体验,同时让浏览器最终加载普通静态 CSS。

01

构建时生成静态 CSS

.css.ts 模块在构建过程中执行并输出普通 CSS,样式声明本身不会进入最终浏览器 JavaScript。

02

类型安全的标准 CSS

样式对象通过 CSSType 检查 CSS 属性和值,并为伪类、媒体查询、Supports、Container Query 和 Cascade Layer 提供结构化语法。

03

局部作用域类名

style 返回构建时生成的类名,默认避免组件间命名冲突,并可像 CSS Modules 类名一样直接传给 className。

04

主题契约与 CSS Variables

createThemeContract 定义变量结构,createTheme 完整实现契约;缺少或写错 Token 会在 TypeScript 阶段暴露。

05

静态样式组合

style 可组合类名和样式对象,复用基础规则;生成结果仍是普通 Class List,并保留可用于选择器的唯一标识。

06

Recipes 组件变体

可选 Recipes 包支持 Base、Variant、Compound Variant 和默认值,生成静态样式并导出类型安全的轻量选择函数。

07

Sprinkles 原子 Utility

可选 Sprinkles 包从团队允许的属性、Token 与响应式条件生成原子类,形成项目专属、零样式运行时的 Utility API。

08

多构建工具集成

官方提供 Vite、esbuild、Webpack、Next.js、Parcel、Rollup 和 Gatsby 等集成,可让生成 CSS 参与原有拆包与缓存流程。

USE CASES

适用场景

适合需要类型安全主题、组件变体、框架无关样式包或零样式运行时输出的应用和设计系统。

TypeScript 设计系统

将颜色、间距、字体、圆角和组件 Variant 建模为可导入、可检查、可版本化的 TypeScript API。

React 与多框架应用

生成结果只是类名和 CSS,因此可用于 React、Vue、Svelte、Solid 或原生 DOM,前提是目标工具链配置了集成插件。

共享组件库

组件样式、主题契约和类名可以随包发布,让多个应用复用同一设计语言与类型定义。

多品牌与多主题产品

不同主题可完整实现同一契约,并按容器应用对应主题类,适合深浅模式、品牌皮肤和局部组件主题。

性能敏感界面

样式在构建时生成,不需要浏览器端 CSS-in-JS 引擎进行解析、哈希和动态 Style Tag 注入。

受控 Utility 与组件 Variant

Sprinkles 和 Recipes 适合把允许的响应式属性、Token 与组件状态封装成类型安全 API。

EVALUATION

优点与注意事项

技术选型不仅要看能力,也要理解它带来的团队成本。

主要优点

vanilla-extract 擅长的地方

  • 样式定义在构建时转为静态 CSS,不承担常规 CSS-in-JS 的样式生成运行时
  • CSS 属性、主题契约、Token 和 Recipe Variant 都能获得 TypeScript 检查与自动补全
  • 核心 API 接近标准 CSS,媒体查询、选择器、变量和 Layer 等能力仍可直接表达
  • 局部作用域类名减少命名冲突,样式又能通过普通模块导入和组合
  • 主题契约可确保多个品牌和颜色模式实现相同的 Token 结构
  • 核心、Sprinkles、Recipes 与 Dynamic 分层,可按项目需求选择抽象程度和运行时成本

需要注意

采用前应考虑的问题

必须配置构建工具

仅安装 @vanilla-extract/css 无法处理 .css.ts 文件;Vite、Webpack、Next.js 等环境都要安装并启用对应官方集成。

核心 API 只能在样式模块中执行

style、createTheme 等构建时 API 应放在 .css.ts 文件,不应在 React 渲染函数或普通运行时代码中动态调用。

任意动态值不会生成新规则

用户输入、数据库颜色或实时尺寸应使用预生成 Variant、CSS Variable 或 Dynamic 包,而不是运行时创建新的样式对象。

主题契约要求完整实现

严格契约有利于治理,但新增 Token 会迫使所有主题同步更新;大型多品牌项目需要规划升级顺序和包版本。

样式与组件分处不同文件

组件通常需要同时维护 .tsx 和 .css.ts 模块,喜欢单文件模板或内联样式的团队可能觉得文件切换较多。

高级抽象会增加学习成本

Sprinkles、Recipes、Theme Contract 和 Dynamic 解决不同问题,若没有清晰边界,团队可能同时维护多套重叠的样式 API。

库发布需要验证消费者集成

共享包应明确是否发布已生成 CSS、如何处理 External 和文件扩展,并在各消费框架中验证 SSR、拆包和样式顺序。

零运行时不等于零 JavaScript

核心静态样式不需要样式运行时,但 Recipes 的 Variant 选择、Dynamic 的变量赋值以及应用自身的类名逻辑仍会产生少量 JavaScript。

QUICK START

快速开始

使用官方 Vite 插件配置 React + TypeScript 项目,再创建主题、静态样式和类型安全 Recipe。

1创建 Vite React 项目并安装依赖
bash
npm create vite@latest vanilla-app -- --template react-ts
cd vanilla-app
npm install
npm install @vanilla-extract/css @vanilla-extract/recipes
npm install -D @vanilla-extract/vite-plugin
2启用 Vite 集成
typescript
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin";

export default defineConfig({
  plugins: [react(), vanillaExtractPlugin()],
});
3定义类型安全主题
typescript
import { createTheme } from "@vanilla-extract/css";

export const [lightTheme, vars] = createTheme({
  color: {
    brand: "#4b67ff",
    surface: "#ffffff",
    text: "#0f172a",
  },
  space: {
    small: "0.75rem",
    medium: "1.25rem",
  },
});

export const darkTheme = createTheme(vars, {
  color: {
    brand: "#8b7cff",
    surface: "#0f172a",
    text: "#f8fafc",
  },
  space: {
    small: "0.75rem",
    medium: "1.25rem",
  },
});
4创建静态样式与响应式规则
typescript
import { style } from "@vanilla-extract/css";
import { vars } from "./theme.css";

export const page = style({
  minHeight: "100vh",
  display: "grid",
  placeItems: "center",
  padding: vars.space.medium,
  background: vars.color.surface,
  color: vars.color.text,
});

export const card = style({
  width: "min(32rem, 100%)",
  padding: vars.space.medium,
  borderRadius: "1rem",
  boxShadow: "0 20px 60px rgb(15 23 42 / 0.12)",
  "@media": {
    "screen and (min-width: 768px)": {
      padding: "2rem",
    },
  },
});
5用 Recipe 定义按钮变体
typescript
import { recipe } from "@vanilla-extract/recipes";
import { vars } from "./theme.css";

export const button = recipe({
  base: {
    border: 0,
    borderRadius: "0.75rem",
    fontWeight: 700,
    cursor: "pointer",
  },
  variants: {
    tone: {
      brand: {
        background: vars.color.brand,
        color: "white",
      },
      neutral: {
        background: vars.color.surface,
        color: vars.color.text,
      },
    },
    size: {
      small: { padding: "0.5rem 0.75rem" },
      medium: { padding: "0.75rem 1rem" },
    },
  },
  defaultVariants: {
    tone: "brand",
    size: "medium",
  },
});
6在 React 组件中应用生成的类名
tsx
import { lightTheme } from "./theme.css";
import { page, card } from "./app.css";
import { button } from "./button.css";

export function App() {
  return (
    <main className={`${lightTheme} ${page}`}>
      <section className={card}>
        <h1>vanilla-extract</h1>
        <p>TypeScript 作者体验,静态 CSS 输出。</p>
        <button className={button({ tone: "brand" })}>
          开始使用
        </button>
      </section>
    </main>
  );
}
7启动开发并验证生产构建
bash
npm run dev

# 提交前验证
npm run build
npm run preview

下一步:所有静态样式必须放在 .css.ts 文件并由官方构建插件处理。不要在组件渲染期间调用核心样式 API;运行时数据应映射到预生成 Variant,或通过 @vanilla-extract/dynamic 写入已声明的 CSS Variable。

ALTERNATIVES

类似项目

这些方案同样把组件化样式转换为静态 CSS,但对原子化、主题、变体、静态分析和框架集成的取舍不同。

COMPARISON

vanilla-extract vs StyleX

vanilla-extract 和 StyleX 都使用类型安全对象描述样式,并在构建阶段输出静态 CSS。vanilla-extract 更接近 CSS Modules-in-TypeScript,默认保留每个 style 对应的局部类;StyleX 则系统性地将声明编译为可全局复用的原子 CSS,并强调确定性的跨文件组合。

比较维度vanilla-extractStyleX
核心语法.css.ts 中的 style、createTheme 与普通类名导入stylex.create 定义样式,stylex.props 负责组合和应用
CSS 输出局部作用域规则,可选 Sprinkles 生成原子 Utility默认把静态声明编译为原子 CSS
组合方式组合类名或样式数组,组件接收普通 classNamestylex.props 处理冲突,并保证后传入样式获胜
主题createThemeContract、createTheme 与 scoped variablesdefineVars、createTheme 与类型化 CSS Variable
组件变体可选 Recipes 提供 Variant 和 Compound Variant主要使用 JavaScript 条件组合预定义样式
Utility 能力Sprinkles 可生成项目专属、类型安全的原子 API核心输出本身原子化,但不提供 Tailwind 式 Utility DSL
框架范围生成类名可供多种框架和原生 DOM 使用理念可跨框架,目前 React 集成和用法最成熟
更适合框架无关组件库、类型安全主题和接近标准 CSS 的团队大型 React 产品、确定性组合和跨包原子规则复用
如何选择

如果团队希望用 TypeScript 作为 CSS 预处理器,保留普通 className、局部作用域规则和多框架适用性,同时按需引入 Recipes 或 Sprinkles,vanilla-extract 更自然;如果核心场景是大型 React 代码库,需要内建原子化和严格可预测的跨文件样式覆盖,StyleX 更聚焦。两者都依赖构建插件,选择前应使用真实 SSR、组件库发布和主题切换流程验证集成。

VERIFICATION

资料核验

版本、维护信息与本页采用的官方资料来源。

最后核验2026 年 7 月 27 日
核验版本vanilla-extract v1.21.2
内容维护Docs100 编辑整理
项目维护状态活跃维护

本页依据 vanilla-extract 官方入门、Vite 集成、Styling、Theming、Recipes、Sprinkles 与 Dynamic 文档,以及官方源码仓库整理。版本通过 npm 官方 Registry 核验;快速开始使用当前官方 @vanilla-extract/vite-plugin 和 .css.ts 工作流。

维护状态核验2026 年 7 月 26 日 · 最近可见代码活动:2026 年 7 月 27 日

官方仓库未归档,核验时最近可见的代码活动日期为 2026 年 7 月 27 日。该状态表示项目近期仍有公开维护活动,不代表固定发布频率或长期支持承诺。

查看官方仓库