微信小程序 + Taro + TDesign 去掉 [&&&],让普通 Tailwind 类接管局部样式
在微信小程序里把 Taro Text 替换成 TDesign 的 t-text 后,页面间距突然变大了。继续检查,还能发现字号、行高和颜色也不完全一致。
原因并不复杂:原来的 Text 没有额外的排版边距,字体样式主要继承父级;TDesign 文本组件则自带默认样式:
css
.t-typography {
color: var(--td-text-color-primary, ...);
font: var(--td-font-body-medium, ...);
margin: var(--td-spacer-2, 32rpx) 0;
}
默认上下各 32rpx 的边距,放进原本紧凑的列表后就很明显。
我最开始用全局规则恢复原来的排版:
css
.t-typography.t-typography {
margin: 0;
font: inherit;
color: inherit;
}
它能压过组件默认值,却又带来了新的问题:普通 Tailwind 类只有一个类选择器,覆盖不了这个双类选择器。于是页面里开始出现这样的写法:
tsx
<t-text t-class="[&&&]:mt-[12rpx]" ellipsis>
{content}
</t-text>
[&&&] 会让生成的选择器重复三次,以提高优先级。这个办法能用,但全局默认值本来应该容易覆盖,现在却要求每个局部样式都主动提高优先级,方向反了。
解决方案
这次调整的目标是让三个文本组件统一使用应用默认排版,页面里的差异直接交给普通 Tailwind 类:
tsx
<t-text t-class="mt-[12rpx]" ellipsis>
{content}
</t-text>
这里需要同时处理两处样式来源:
- 应用自己的全局排版规则。
- TDesign 文本组件 WXSS 中的基础排版规则。
只把全局规则改成单类选择器还不够。组件自身仍然声明了 font、margin 和 color,这些规则也会参与覆盖。
项目最终采用的做法是:在现有资源复制环节移除三个文本组件的基础排版规则,再由 Tailwind base 层统一提供默认值。
项目环境
这次调整发生在 Taro React 微信小程序项目中,使用的版本是:
| 依赖 | 版本 |
|---|---|
| Taro | 4.3.2 |
| TDesign Miniprogram | 1.17.0 |
| Tailwind CSS | 4.3.4 |
| weapp-tailwindcss | 5.6.3 |
项目已经通过 Taro 的 copy.patterns 复制 TDesign 资源,并安装了 PostCSS。下面的配置沿用这套构建链路,没有修改 node_modules 中的依赖源码。
在复制资源时处理组件默认样式
修改 apps/miniprogram/config/index.ts,增加 PostCSS 导入:
ts
import postcss from "postcss"
项目已有的 tdesignRuntimeDirectories 包含 text、title 和 paragraph。在对应的主包资源复制配置中增加 transform:
ts
...tdesignRuntimeDirectories.map((directory) => ({
from: `node_modules/tdesign-miniprogram/miniprogram_dist/${directory}`,
to: `dist/npm/tdesign-miniprogram/${directory}`,
ignore: [
"**/*.d.ts",
"**/*.map",
"**/shared/calendar/**",
"**/shared/color-picker/**",
"**/shared/qrcode/**",
],
transform(content: Buffer, absoluteFrom: string) {
if (
!["text", "title", "paragraph"].includes(directory) ||
path.basename(absoluteFrom) !== `${directory}.wxss`
) {
return content
}
const stylesheet = postcss.parse(content.toString(), {
from: absoluteFrom,
})
stylesheet.walkRules((rule) => {
if (/^\.t-typography(?:--h[1-6])?$/.test(rule.selector)) {
rule.remove()
}
})
return stylesheet.toString()
},
})),
这段配置只处理三个组件各自的入口 WXSS,移除:
css
.t-typography
.t-typography--h1
.t-typography--h2
.t-typography--h3
.t-typography--h4
.t-typography--h5
.t-typography--h6
主题、装饰、复制和省略相关规则继续保留,其他组件资源原样复制。
这里也有一个明确的取舍:项目不再直接使用 TDesign 标题等级自带的字号和边距,而是由页面用 Tailwind 表达视觉差异。例如,登录标题仍使用 t-title,字号和字重显式配置。
这段适配依赖当前版本的选择器结构。升级 TDesign 时,需要重新核对这些规则,不能默认它永远不变。
将应用默认排版放入 base 层
修改 apps/miniprogram/src/tailwind.css,删除原来的双类选择器,以及为不同文本位置编写的自定义样式类。
默认排版放进已有的 @layer base:
css
@layer base {
.t-typography {
@apply m-0 [font:inherit] text-inherit;
}
}
这条规则只负责三件事:
- 清除默认外边距。
- 继承父级字体,包括字号、字重和行高。
- 继承父级颜色。
具体页面需要什么样式,就直接写普通 Tailwind 类:
tsx
<t-title
level="h3"
t-class="min-w-0 flex-1 text-2xl font-semibold"
>
{title}
</t-title>
<t-text t-class="mt-[12rpx]" ellipsis>
{createdAt}
</t-text>
<t-paragraph
t-class="mb-[16rpx] text-[length:26rpx] text-[color:var(--td-text-color-secondary)]"
>
{notice}
</t-paragraph>
不再需要 original-text,也不再需要 [&&&]。
小程序里要写 t-class
这次排查还遇到了一个比 CSS 优先级更靠前的问题:外部样式属性没有正确传入组件。
在当前项目的 Taro 编译链路中:
tsx
<t-text tClass="mt-[12rpx]">
{content}
</t-text>
会生成:
xml
<t-text tclass="{{i.tClass}}">
而 TDesign 声明的外部样式属性是 t-class。属性名不匹配时,即使 CSS 已经生成,样式也没有通过这个入口传进组件。
因此 JSX 使用连字符写法:
tsx
<t-text t-class="mt-[12rpx]">
{content}
</t-text>
同时,在 apps/miniprogram/config/index.ts 的 weapp-tailwindcss 配置中保留对应的属性识别:
ts
customAttributes: {
"*": [
"t-class",
"t-class-content",
"t-class-title",
"t-class-description",
"t-class-center",
"t-class-note",
"t-class-label",
"t-class-right",
"t-class-left",
],
},
这样,Tailwind 类名转换和组件外部样式属性才能接上。
重新构建后检查产物
保存以上文件后,需要重新构建。只在微信开发者工具中重新编译旧的 dist,不会执行资源复制时新增的转换逻辑。
bash
cd apps/miniprogram
pnpm build
这次构建成功后,我检查了两部分产物。
三个文本组件的 WXSS 中,基础 .t-typography 和标题等级规则已经移除,其余样式仍然保留。dist/app.wxss 中,全局默认规则排在普通工具类之前:
css
.t-typography {
color: inherit;
font: inherit;
margin: 0rpx;
}
/* 后面生成普通工具类 */
.mt-_b12rpx_B {
margin-top: 12rpx;
}
两者都是单类选择器。在这份产物中,后面的 margin-top 可以覆盖前面的 margin 简写,不需要额外提高优先级。
页面 JS 中的类名也已经完成转换:
js
"t-class": "mt-_b12rpx_B"
WXML 使用的则是正确的外部样式属性:
xml
<t-text t-class="{{i.tClass}}">
本次编译、TypeScript 和差异检查通过,Lint 无错误,但仍有项目已有警告。这些结果验证了构建链路和样式生成结构;实际微信界面尚未验证,需要重新加载当前 dist,检查登录标题、列表辅助行间距和地图文本的显示效果。