跳转到内容

样式

VPT 可同时使用 Tailwind CSS v4、CSS Modules 和普通 CSS。样式也支持热更新,修改后立刻在开发者工具中能看到效果,不会丢失页面状态。

写法适合场景作用范围
Tailwind CSS直接在 JSX 中组合样式全局工具类
CSS Modules组件内部的复杂样式、动画和伪元素类名自动隔离
普通 CSS页面基础样式、重置样式和共享规则全局

create-vite-taro 默认启用 Tailwind CSS v4。该功能由 weapp-tailwindcss 支持,感谢作者开源。

默认的 src/app.css 包含:

@import "tailwindcss/theme.css";
@import "tailwindcss/preflight.css";
@import "tailwindcss/utilities.css";
@source "./";
  • 三个 @import 分别引入主题、基础规则和工具类。
  • @source "./" 扫描 src 中使用的 Tailwind 类名。
  • vpt 为微信、支付宝、抖音和 Web 分别生成可用的目标样式。

在 JSX 中直接使用 className:

import { Text, View } from 'virtual:taro/components'
export function AccountSummary() {
return (
<View className="rounded-[24px] bg-emerald-950 p-5 shadow-xl">
<Text className="text-sm font-medium text-emerald-100">本月余额</Text>
<Text className="mt-2 block text-3xl font-bold text-white">¥ 8,260</Text>
</View>
)
}

任意值、颜色透明度和常见状态变体会经过同一套目标转换。

Tailwind 根据源码中出现的完整类名生成样式。不要用字符串片段拼出类名:

// 不要这样写:源码中不存在完整的 bg-*-500 类名。
const className = `bg-${tone}-500`

将所有可能的类名写成完整字符串:

const toneClass = active ? 'bg-emerald-600' : 'bg-slate-300'
return <View className={`rounded-xl p-4 ${toneClass}`} />

从 @source "./" 以外的位置读取类名时,使用 Tailwind v4 的 @source 指令显式添加扫描目录。动态尺寸不能写成 `w-[${width}px]`:Tailwind 扫描不到完整类名;请使用动态行内样式。

推荐通过 @theme 设定项目的设计令牌(tokens):

@theme {
--color-brand-500: #2a9d5b;
--color-brand-950: #123a25;
--font-display: Georgia, serif;
}

之后可以使用 bg-brand-500、text-brand-950 和 font-display。随着项目扩大,相同工具类会在不同组件中复用,样式体积主要随实际使用的规则增长;统一的颜色、间距、字号和圆角令牌也能减少任意值与重复 CSS。

若遇到复杂样式,推荐使用 CSS Modules。CSS Modules 会生成唯一的 class name,杜绝样式冲突和覆盖。

文件名使用 Vite 的 *.module.css 约定:

profile-card.module.css
.card {
padding: 24px;
border: 1px solid rgba(23, 103, 61, 0.16);
border-radius: 20px;
background: white;
}
.title {
color: #17673d;
font-size: 28px;
font-weight: 700;
}

小程序构建都会把普通 CSS 中的 px 和 rem 转换为 rpx。

在组件中导入生成的类名映射:

import { Text, View } from 'virtual:taro/components'
import styles from './profile-card.module.css'
export function ProfileCard() {
return (
<View className={styles.card}>
<Text className={styles.title}>个人资料</Text>
</View>
)
}

Tailwind 工具类可以与 CSS Modules 生成的类名组合:

<View className={`${styles.card} flex items-center gap-3 p-4`}>
<Text className={styles.title}>个人资料</Text>
</View>

Sass、Less 和 Stylus 也支持 Modules 文件约定,例如 profile-card.module.scss。

普通 CSS 不会隔离选择器,适合放置全局重置、字体和根元素规则。建议将这些规则集中在 app.css 中。

*,
*::before,
*::after {
box-sizing: border-box;
}
page,
body {
margin: 0;
background: #f8faf7;
color: #173e29;
}

page 是小程序页面根元素,body 是 Web 文档根元素。

目标专用规则可以使用条件指令:

/* #ifdef wx */
.wx-only-surface {
padding-bottom: env(safe-area-inset-bottom);
}
/* #endif */
/* #ifdef h5 */
.web-only-surface {
min-height: 100dvh;
}
/* #endif */

VPT 仍支持 .scss、.sass、.less 和 .styl,但不推荐新项目使用。新项目请优先使用 Tailwind,复杂组件样式使用 CSS Modules。迁移现有预处理器样式时,需要安装对应的依赖,例如:

终端窗口
npm install --save-dev sass

然后像普通 CSS 一样导入:

import './profile-card.scss'

PostCSS 配置使用 Vite 的 css.postcss 或项目根目录中的 PostCSS 配置文件。

运行 npm run dev:... 时,以下修改会触发样式热更新:

  • 修改已导入的 CSS、CSS Modules 或预处理器文件。
  • 在 JSX 中新增、替换或删除 Tailwind 类名。
  • 添加或删除组件对样式文件的导入。

样式文件必须由应用模块图导入。全局样式通常从 src/app.tsx 导入:

import type { PropsWithChildren } from 'react'
import './app.css'
function App({ children }: PropsWithChildren) {
return children
}
export default App

页面或组件也可以导入自己的样式;未被任何应用、页面或组件导入的样式不会进入构建结果。

Web 目标沿用 Vite 的 CSS 行为。小程序通过 build.cssCodeSplit 控制样式范围,开发和生产构建使用同一设置:

设置效果
true(默认)App 导入的样式全局生效;页面及其组件导入的其他样式仅在使用它们的页面生效。
false应用导入的所有样式全局生效,普通选择器可能影响其他页面。

需要全局共享的样式,建议从 src/app.tsx 导入。也可以在 vite.config.ts 中关闭拆分:

import { defineConfig } from 'vite'
export default defineConfig({
build: {
cssCodeSplit: false
}
})

两种设置都支持普通 CSS、CSS Modules、预处理器和 Tailwind;CSS Modules 的类名隔离不变。从 App 导入的 Tailwind 工具类始终全局可用。

完整示例见 page-styles-demo。

小程序(微信、支付宝、抖音)默认按 750px 设计稿转换应用 CSS:100px → 100rpx、1rem → 32rpx。已有的 rpx 保持不变;原生组件的样式不参与转换。

行内样式不经过 CSS 转换。需要固定的 CSS 像素时,在 style 中使用带 px 单位的字符串:

<View style={{ width: '100px', borderBottom: '1px solid #ddd' }} />

需要按设计稿换算尺寸时,小程序中可以使用 Taro.pxTransform(),默认 100 → '100rpx':

import Taro from 'virtual:taro/api'
import { View } from 'virtual:taro/components'
export function SizedView({ width }: { width: number }) {
return <View style={{ width: Taro.pxTransform(width) }} />
}

Web 默认保留 CSS 中的 px,不会自动将其转换为 rem。若要在 Web 中使用 Taro.pxTransform(),须先初始化,并让 CSS 与动态样式使用相同单位。rem 的实际尺寸取决于根字号;这里推荐使用直接按视口宽度计算的 vw。

以 375px 设计稿为例,为小程序和 Web 配置同一套自适应尺寸:

  1. 安装转换插件:

    终端窗口
    pnpm add -D postcss-rule-unit-converter
  2. 在 vite.config.ts 现有的 defineConfig 回调中,取得 target 后加入 css 字段,保留其他配置:

    import unitConverter, { presets } from 'postcss-rule-unit-converter'
    const css = {
    postcss: {
    plugins: [
    unitConverter({
    rules: target === 'h5'
    ? [
    presets.pxToVw({ viewportWidth: 375 }),
    presets.rpxToVw({ viewportWidth: 375, ratio: 1 })
    ]
    : [presets.pxToRpx({ ratio: 2 })]
    })
    ]
    }
    }
    // 将 css 加入原有的 return 对象。
  3. 在首次调用 Taro.pxTransform() 前,用相同的设计宽度初始化运行时:

    import Taro from 'virtual:taro/api'
    Taro.initPxTransform({
    designWidth: 375,
    deviceRatio: { 375: 2 },
    targetUnit: import.meta.env.VITE_VPT_TARGET === 'h5' ? 'vw' : 'rpx'
    })

H5 中已有的 rpx 按相同设计稿比例换算为 vw。100px 在小程序中成为 200rpx,在 Web 中成为 26.66667vw;Taro.pxTransform(100) 也会返回对应单位。两者在 375px 宽的屏幕上均相当于 100px。

确认引入 Tailwind 的 CSS 文件仍由 app.tsx 导入,并保留 @source "./"。同时检查类名是否以完整字符串出现在扫描范围内。

确认应用的 TypeScript 配置包含:

{
"compilerOptions": {
"types": ["vite/client", "vite-plugin-taro/client"]
}
}

检查所用属性和选择器是否受当前 WXSS、ACSS 或 TTSS 方言支持,并确认尺寸换算、默认元素样式和 Tailwind Preflight。不要依赖开发者工具的 PostCSS;样式转换由 vpt 完成。