把设计页面这事儿比作做饭,那组合就是准备一堆现成的菜码和调料,需要啥就往上搁啥。很多人觉得Tailwind那套在标签里堆一堆p-4、border-2的玩法就是组合的终极形态,其实这玩意儿就跟直接把盐、糖、酱油的配方写进菜谱里没啥两样,都是把样式往元素上怼。
<div class="p-4 border-2 border-blue-500"> ... </div>同样是在拼凑样式,写成下面这种形式,本质上也是一种组合:
/* 这不也是把零件拼一块儿嘛 */
.card {
padding: 1rem;
border: 2px solid var(--color-blue-500)
}说到底,CSS这玩意儿打娘胎里就是为组合而生的,只是大伙儿平时不怎么把这事儿挂在嘴边,觉得它理所应当罢了。
组合从哪来
天生自带的属性
层叠样式表嘛,层叠这俩字本身就带着组合的基因。比如给按钮定了一套基础样式,这算是打了个底:
.button {
display: inline-flex;
padding: 0.75em 1.5em;
/* 其他打底样式 */
}想换个皮肤?直接往上叠别的类就完事儿了,跟打工人套娃似的:
<button class="button primary">点我</button>
<button class="button secondary">点我</button>.primary { background: orange; }
.secondary { background: pink; }更有意思的是,这.button类还能直接甩给链接,让链接摇身一变成了按钮范儿:
<a href="#" class="button">戳这里</a>这么一搞,其实干了俩事儿:一是把.button的样式组合到了<a>标签上,二是把.primary的颜色组合到了.button这个基础样式上。这招从CSS诞生那天就能玩,不是什么新鲜玩意儿。
文件里的混搭
一提到CSS组合,很多人脑子里只有HTML里那一长串类名。实际上,在样式文件里头用Sass的混入(mixin)来拼凑样式,也是实打实的组合,只是阵地转移了:
@mixin button () {
display: inline-flex;
padding: 0.75em 1.5em;
/* 其他样式 */
}
.button {
@include button;
}这么干的好处是,样式文件里也能像搭积木一样,把常用的样式块(混入)拼到不同的选择器里去。
拆分四大块
如果把所有样式分门别类,基本上跑不出这四个筐:
| 类别 | 负责 |
|---|---|
| 布局 | 页面元素怎么搁 |
| 排版 | 字体字号行距 |
| 主题 | 颜色背景边框 |
| 特效 | 阴影渐变动画 |
这四个筐里的东西基本井水不犯河水。比如font-weight只归排版管,color只属于主题范畴,不会互相掐架。
把这四类拆开,各自做一套可组合的类,到时候想用哪个就用哪个,跟玩乐高似的,想拼个房子就拼房子,想拼个车就拼车。甚至说,连小朋友玩的得宝大颗粒积木都比这复杂不到哪儿去:
<!-- 纯属虚构的类名,但意思就是那么个意思 -->
<div class="布局-1 布局-2 特效-1">
<h2 class="排版-1 主题-1">标题来啦</h2>
<div class="排版-2">正文走起</div>
</div>拿实际点的例子来说,要是用了某些现成的样式库,最后HTML可能长这样:
<div class="card vertical elevation-3">
<h2 class="inter-title">标题在这儿</h2>
<div class="prose">正文内容</div>
</div>card管框架,vertical管方向,elevation-3管阴影特效,inter-title和prose管排版样式。各管各的,互不干扰。
体积那点事儿
有人纠结用工具类(utility)堆HTML会不会导致HTML体积爆炸,用选择器组合(selector)会不会让CSS体积膨胀。说实话,这俩问题在大多时候都不是核心矛盾。
HTML本身就能扛一大堆东西,对性能影响微乎其微。CSS那边,500行撑死了也就12到15kb(大模型给的数据),随便一张图片就150kb往上走了。与其纠结这十几kb的样式是堆在HTML还是堆在CSS里,不如去优化几张图片来得实在。
费劲巴拉重构代码去缩减CSS体积,可能换来的只是加载时间快了那么两毫秒,体感基本为零。但如果重构是为了让代码更清晰、更好维护、让团队接手的人一眼就能看懂,那这笔买卖可就太值了。
类名叠穿的玩法
想把组合玩溜,关键是把样式拆得足够细,但又不至于细碎到没法管理。实操上,可以这么干:
第一步:打地基
先定义好最底层的样式,比如页面整体框架、字体家族:
/* 布局地基 */
.layout-stack {
display: flex;
flex-direction: column;
gap: 1rem;
}
/* 排版地基 */
.typo-base {
font-family: system-ui, -apple-system, 'Segoe UI', Roboto;
line-height: 1.5;
}第二步:做模块
针对不同场景,做专用的组合类。比如卡片模块,就可以把布局和排版的基础样式组合进来:
.card {
@apply layout-stack; /* 假设项目里用Tailwind的@apply,否则就直接写flex那一套 */
background: white;
border-radius: 0.5rem;
padding: 1rem;
}
.card-title {
@apply typo-base;
font-weight: 600;
font-size: 1.25rem;
}第三步:上变体
给模块做变体,比如不同主题色的卡片:
.card-primary {
border-top: 3px solid #3b82f6;
}
.card-primary .card-title {
color: #1e3a8a;
}
.card-secondary {
border-top: 3px solid #10b981;
}
.card-secondary .card-title {
color: #065f46;
}第四步:HTML里组合
到具体页面时,直接把这些零件组合起来用:
<div class="card card-primary">
<h3 class="card-title">新功能上线</h3>
<p class="typo-base">这功能绝了,用了都说好</p>
</div>
<div class="card card-secondary">
<h3 class="card-title">限时优惠</h3>
<p class="typo-base">错过等一年,赶紧上车</p>
</div>这么一套流程下来,.card负责基础框架,.card-primary只负责改变颜色主题,.card-title管标题样式。改一个地方,所有用了这个组合的地方都会跟着变,比在HTML里写十来个类名再挨个找要痛快得多。
混入的进阶玩法
Sass的混入(mixin)是文件内组合的利器,可以做出带参数的组合块:
@mixin flex-center($direction: row) {
display: flex;
justify-content: center;
align-items: center;
flex-direction: $direction;
}
.header {
@include flex-center; // 默认横向居中
}
.sidebar {
@include flex-center(column); // 竖向居中
}这样flex-center这个组合块就能按需取用,不用重复写那几行flex代码。文件里维护一处,所有引用的地方都跟着同步更新。
