Sass 工具扩展 bootstrap5 支持任意自定义列数
- 作者: 刘杰
- 来源: 技术那些事
- 阅读:123
- 发布: 2025-09-11 11:01
- 最后更新: 2025-09-11 11:01
最近做新网站的响应式页面,我用的 bootstrap5.3.8 ,整体页面两列布局,分为左侧主要内容,和右侧侧边栏。
因为是响应式的网站页面,经常会用到在不同的屏幕设置指定的元素个数。
用过bootstrap5 的都知道,bootstrap 默认是 12 列布局。使用列式布局的时候,需要有 row 元素包裹。而且默认生成的代码中,当我们要设置每行的元素个数的时候,只能设置为 12 的公约数,也就是列数为:1、2、3、4、6、12 的时候才能生效。
主要是因为,bootstrap 响应式,是通过 @media 媒体查询来实现的。而媒体查询程序的机制,本身跟普通的程序编写是有区别的。
bootstrap 的媒体查询机制
比如说:定义两个全局变量,然后通过媒体查询的方式,让 --#{$prefix}-columns的值,能够在不同的屏幕级别下,递增或者递减。如下代码:
css
:root {
--#{$prefix}-columns: 12;
--#{$prefix}-step: 1;
}
@include media-breakpoint-up(sm) {
--#{$prefix}-columns: calc(var(--#{$prefix}-columns) - var(--#{$prefix}-step));
}
@include media-breakpoint-up(md) {
--#{$prefix}-columns: calc(var(--#{$prefix}-columns) - var(--#{$prefix}-step));
}
@include media-breakpoint-up(xxl) {
--#{$prefix}-columns: 12
}
以上代码,虽然语法什么的都没问题,但是实际编译后,使用起来就会发现 --#{$prefix}-columns变量,在xxl屏幕级别下,可以正常获得值 12。但是,一旦屏幕到了 md 级别,就会提示未定义的问题(通过浏览器的 dev 工具能可以确认)。这是为什么呢?
问题在于:
sm、md 级别的屏幕上,calc 都是基于上一个值继续减,但 CSS 变量在不同媒体查询中是独立覆盖的,不是“链式递减”。
- 每个媒体查询内的
:root是独立作用域 -
var(--#{$prefix}-columns)取的是当前作用域的值。 - 有在每个断点“重新定义”基础值,所以
calc()拿到的var(--#{$prefix}-columns)可能是上一个断点的值,也可能被覆盖,行为不可靠。 - CSS 不是编程语言,没有“变量状态变迁”的概念。
所以在应用媒体查询的时候,需要用户去指定每个屏幕级别下的变量的变化的值。其实就是我们用到的bootstrap 提供的常见的一些类,如:
-
row-cols-#{$bp_name}-#{$col_count}用来指定不同屏幕下划分的列数 -
col-#{$bp_name}-#{$col_count}用来指定,不同屏幕下每个元素宽度占用12列中的几列
$bp_name 取值为屏幕的五个级别 sm/md/lg/xl/xxl,$col_count为自定义的列数量,取值范围为 1 ~ 12。
bootstrap 中的工具类
基于媒体查询的以上特性,我们无法实现像编写代码一样去控制每列的元素个数。而如果想达到此效果,可以通过 Sass 循环生成响应式类。也就是说,提前将可能用到的自定义的列数全部生成出对应的样式。
但是这样一来就有会有新的问题,自定义的列数,那可是无数种可能,根本没法完全实现。于是,就又有了个折中方案,将常用的自定义的列数样式类,提前生成出来即可,为了保证样式文件尽量小,只尽可能少的生成了一些样式,也就是 12 (默认一行分成的列数)的公约数 1/2/3/4/6/12。只有这些类才有生成的样式。
这也导致,当我们使用 5/7/8/9/10/11 之类的列数进行设置时候,样式并没有生效,不是有问题,而是没有对应的样式生成。
有的同学也许会想到,bootstrap 有一个自定义变量 $grid-columns,可以控制 bootstrap 默认一行划分的列数,如果设置为 24(24 的公约数有 8),这时候如果使用 8 列就可以成功。但是如果使用一些非常用的列数。5/7/9 之类的还是无法生效。
根本解决方案,其实还是扩展一下bootstrap 的 sass 工具,生成我们需要的自定义列的样式就行了。这样一来,也不用修改默认列数,我们想要定义多少列都可以成功实现。
自定义工具实现任意列的响应式设置
我们这里创建一个 _utilities.scss文件,用来补充自定义列的样式类的生成。我这里将 20 列之内的样式全都补全了,其实根据需要就好,一般 12 列就够用了。除非你页面的元素很小,一行 12 个间隙显得特别大,那这时候就可以考虑更大的列数。具体代码如下示例:
css
@import "bootstrap/scss/variables";
// 自定义列数
$column-counts: (5, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20) !default;
@each $bp-name, $bp-value in $grid-breakpoints {
// xs 断点(0)不需要 media query
@if $bp-value == 0 {
@each $col-count, $unused in $column-counts {
.row-cols-#{$col-count} {
> .col {
flex: 1 0 calc(#{percentage(calc(1 / $col-count))});
max-width: calc(#{percentage(calc(1 / $col-count))});
}
}
}
} @else {
// 其他断点用 media query
@media (min-width: $bp-value) {
@each $col-count, $unused in $column-counts {
.row-cols-#{$bp-name}-#{$col-count} {
> .col {
flex: 1 0 calc(#{percentage(calc(1 / $col-count))});
max-width: calc(#{percentage(calc(1 / $col-count))});
}
}
}
}
}
}
示例代码
html
<div class="nav-list">
<div class="row row-cols-8 row-cols-md-5 row-cols-lg-8 row-cols-xl-13">
<div class="col">
<a href="#">A</a>
</div>
<div class="col">
<a href="#">B</a>
</div>
<div class="col">
<a href="#">C</a>
</div>
<!-- 此处省略其他字母的样式,共26个 -->
</div>
</div>
这里分别设定了,当屏幕为 sm 及其以下级别的列数为 8。md 屏幕级别下,一行分为 5 列,lg 屏幕级别下一行分为 8 列,xxl 屏幕下,一行分为 13 列。




CSS 媒体查询是“状态驱动”的,而非“事件驱动”的
是否我的响应式变量设置文件(_responsive_vars.scss)中,需要把 media-breakpoint-up 和 media-breakpoint-down 每个级别的媒体查询都写上呢,来控制屏幕从大到小和从小到大的不同变化呢?
- 媒体查询不关心屏幕是“从大变小”还是“从小变大”
- 媒体查询只关心:“当前屏幕宽度是多少?”
- 只要条件满足,样式就生效;不满足,就失效
所以只要你的断点覆盖了所有尺寸范围,无论用户怎么缩放窗口,浏览器都会自动应用正确的样式。
media-breakpoint-up 对应的媒体查询和覆盖范围
| Mixin | 对应 CSS 媒体查询 | 覆盖范围 |
|---|---|---|
media-breakpoint-up(sm) |
@media (min-width: 576px) |
sm, md, lg, xl, xxl |
media-breakpoint-up(md) |
@media (min-width: 768px) |
md, lg, xl, xxl |
media-breakpoint-up(lg) |
@media (min-width: 992px) |
lg, xl, xxl |
media-breakpoint-up(xl) |
@media (min-width: 1200px) |
xl, xxl |
media-breakpoint-up(xxl) |
@media (min-width: 1400px) |
xxl |
media-breakpoint-down 对应的媒体查询和覆盖范围
| Mixin | 对应 CSS 媒体查询 | 覆盖范围 |
|---|---|---|
media-breakpoint-down(sm) |
@media (max-width: 575.98px) |
xs |
media-breakpoint-down(md) |
@media (max-width: 767.98px) |
xs, sm |
media-breakpoint-down(lg) |
@media (max-width: 991.98px) |
xs, sm, md |
media-breakpoint-down(xl) |
@media (max-width: 1199.98px) |
xs, sm, md, lg |
media-breakpoint-down(xxl) |
@media (max-width: 1399.98px) |
xs, sm, md, lg, xl |
bootstrap 断点使用说明
- 使用
media-breakpoint-up()定义“从某个尺寸开始”的样式(移动优先) - 使用
media-breakpoint-down()定义“最大到某个尺寸”的样式(桌面降级) - 避免在
media-breakpoint-up()和media-breakpoint-down()中设置冲突样式,以免覆盖混乱 - 如果你希望某个样式只在特定断点生效,或者跨域多个断点宽度,可以用
media-breakpoint-between(...)