用$button-variants Map统一管理按钮样式变体,通过@each遍历生成.btn-primary等类,并在循环内定义:hover、:disabled状态样式,支持用独立变量(如$btn-primary-bg)局部覆盖,确保可维护性。

用 $button-variants Map 统一管理按钮样式变体
直接定义一个 $button-variants Map,比写一堆重复的 .btn-primary、.btn-danger 类更可控,也避免手误漏改某一处。Map 本质是键值对集合,适合表达「类型 → 颜色/边框/文字」这种映射关系。
常见错误是把 Map 写成普通变量或嵌套在选择器里——Map 必须在顶层作用域定义,且键名建议用字符串(如 "primary"),方便后续遍历和拼接类名:
$button-variants: ("primary": (#007bff, #fff), "danger": (#dc3545, #fff), "outline": (#6c757d, transparent, 1px));- 每个值是列表:前两个通常是背景色、文字色;第三个可选边框宽/样式,用于区分实色按钮和描边按钮
- 别用数字或布尔当键名(如
1或true),编译时会报Invalid CSS after "...": expected "{", was ";"
用 @each 遍历 Map 生成完整按钮类
有了 Map,就用 @each 批量产出所有变体类,不用手动复制粘贴。关键点在于解构值列表 + 动态插值生成类名:
- 语法必须是
@each $type, $props in $button-variants,$type是键(如"primary"),$props是对应值列表 - 用
nth($props, 1)取背景色,nth($props, 2)取文字色,nth($props, 3)判断是否存在边框(可用if(length($props) > 2, ...)做容错) - 类名必须用插值:
.btn-#{$type},否则编译后就是字面量.btn-$type
示例片段:
立即学习“前端免费学习笔记(深入)”;
@each $type, $props in $button-variants {
.btn-#{$type} {
background-color: nth($props, 1);
color: nth($props, 2);
@if length($props) > 2 {
border: nth($props, 3) solid nth($props, 1);
background-color: nth($props, 2);
}
}
}
给按钮加状态样式时,&:hover 必须写在循环体内
很多人把 &:hover 提到循环外单独写,结果所有按钮 hover 效果都一样——因为没绑定到具体 $type 上。hover 样式必须和基础类共处一个作用域,才能继承当前循环上下文。
- 正确做法:在
@each块内紧贴基础样式写&:hover,用lighten()或darken()基于当前背景色微调 - 例如:
&:hover { background-color: darken(nth($props, 1), 10%); } - 如果按钮有禁用态,也得一起处理:
&:disabled { opacity: 0.6; cursor: not-allowed; },否则.btn-primary:disabled不会生效
!default 不能用在 Map 遍历过程中覆盖单个变体
!default 只在变量首次赋值时生效,对 Map 内部字段无效。想让项目能安全覆盖某个按钮颜色(比如把 "primary" 改成品牌蓝),不能靠 $button-variants: ("primary": (#0056b3, #fff)) !default; —— 这只会覆盖整个 Map,破坏其他变体。
- 真正可行的方式:拆出独立变量,再汇入 Map
- 例如先定义
$btn-primary-bg: #007bff !default;,再写$button-variants: ("primary": ($btn-primary-bg, #fff), ...); - 这样下游项目只要重定义
$btn-primary-bg,就能局部覆盖,不影响"danger"等其他项
Map 本身是静态结构,它的灵活性不在运行时,而在组织方式——把“哪些变体存在”和“每个变体长什么样”彻底解耦,才是可维护的关键。


















