⚡️ Flash Grid:通过构建网格系统学习 CSS Grid
最近,我一直在尝试构建基于 CSS Grid 的轻量级网格系统。
CodyFrame中确实有一个基于 Flexbox 的网格系统。然而,CSS Grid 有很多 Flexbox 所不具备的强大功能,所以我最终创建了 Flash Grid。
我将分享创建 Flash Grid 的整个过程。如果你想了解更多关于 CSS Grid 的知识,这里是一个很好的起点,因为我们将介绍 CSS Grid 的主要属性,并分享一些实用技巧,让你能够充分利用这个强大的布局系统。
如果您想跳过教程并直接获取代码:
开始吧!🙌
第一步是创建.grid
类:
$grid-columns: 12 !default;
.grid {
--grid-cols: #{$grid-columns};
display: grid;
grid-gap: var(--grid-gap, 0); // default grid-gap = 0
grid-template-columns: repeat(var(--grid-cols), 1fr); // grid of 12 flexible columns
> * {
grid-column-end: span var(--grid-cols); // each grid item takes full-width by default
}
}
在定义网格列数时,如果网格系统作为模块导入,我们使用!default
SCSS 标志,并且我们希望该值是可定制的。
这grid-template-columns
是我们定义网格布局的属性:我们需要 12 个响应式列。每列的宽度为 1fr。Fr(分数单位)是一个智能单位,等于可用空间的 1/10。因为我们的网格由 12 个 1fr 列组成,所以每个灵活列占用可用宽度的 1/12。
repeat() 函数允许我们传递单个宽度值 (1fr)。定义相同网格的另一种方法是:
.grid {
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; // 🙈
}
...但是,你知道...不那么优雅!
在上面的截图中,请注意列之间的数字(目前只需关注顶部的正数)。这些行号可用于放置网格项。
在.grid
代码片段中,我们还针对所有网格子项,并将它们的grid-column-end
值设置为span 12
。
默认情况下,我们希望每个子元素占据整个可用宽度。grid-column-end
用于指定网格项的结束位置。您可以使用此属性设置结束线(例如grid-column-end: 3;
)。但是,如果您使用“span”魔法词,则可以定义网格项应占用多少列。例如,grid-column-end: span 12;
表示“将此元素跨越 12 列”。
为什么要为网格项设置默认的 12 列跨度?因为我们的工作以移动设备为先。我们可以假设,在大多数情况下,我们的网格项最初会占据全宽(12 列),然后在更大的屏幕上占据较少的列数。我们的默认值使我们无需.col-12
手动指定每个网格项(跨度 12)。
列数设置为 CSS 自定义属性,以便你想在组件级别(或通过创建其他实用程序类)更改它。例如:
.grid--2 {
--grid-cols: 2;
}
接下来,我们可以为该属性定义实用程序类grid-gap
:
.grid-gap-xxxxs { --grid-gap: var(--space-xxxxs, 0.125rem); }
.grid-gap-xxxs { --grid-gap: var(--space-xxxs, 0.25rem); }
.grid-gap-xxs { --grid-gap: var(--space-xxs, 0.375rem); }
.grid-gap-xs { --grid-gap: var(--space-xs, 0.5rem); }
.grid-gap-sm { --grid-gap: var(--space-sm, 0.75rem); }
.grid-gap-md { --grid-gap: var(--space-md, 1.25rem); }
.grid-gap-lg { --grid-gap: var(--space-lg, 2rem); }
.grid-gap-xl { --grid-gap: var(--space-xl, 3.25rem); }
.grid-gap-xxl { --grid-gap: var(--space-xxl, 5.25rem); }
.grid-gap-xxxl { --grid-gap: var(--space-xxxl, 8.5rem); }
.grid-gap-xxxxl { --grid-gap: var(--space-xxxxl, 13.75rem); }
间距变量是CodyFrame的一部分。您可以用自己的间距比例替换它们,或者使用每个变量中指定的后备值(如果变量未定义,则应用逗号后的值)。
该grid-gap
属性用于定义网格项之间的空间。
为了完成基本的网格系统,我们应该定义以下.col
类:
@for $i from 1 through $grid-columns {
.col-#{$i} { grid-column-end: span #{$i}; }
}
我们使用 SASS @for.col
循环根据变量中指定的列数生成类$grid-columns
。
编译后的 CSS 为:
.col-1 { grid-column-end: span 1; }
.col-2 { grid-column-end: span 2; }
.col-3 { grid-column-end: span 3; }
.col-4 { grid-column-end: span 4; }
.col-5 { grid-column-end: span 5; }
.col-6 { grid-column-end: span 6; }
.col-7 { grid-column-end: span 7; }
.col-8 { grid-column-end: span 8; }
.col-9 { grid-column-end: span 9; }
.col-10 { grid-column-end: span 10; }
.col-11 { grid-column-end: span 11; }
.col-12 { grid-column-end: span 12; }
col 类指定网格项占用的列数。记住,“span”表示“元素跨越 x 列”,其中 x 是 span 后面指定的数字。
添加一些 CSS Grid 元素
回顾一下,Flash Grid 的准系统版本包括网格、网格间隙和 col 实用程序类的定义:
现在是时候加点料了!💃
这是.grid-auto-cols
实用程序类:
.grid-auto-cols { // cols = same size
display: grid;
grid-gap: var(--grid-gap, 0);
grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
}
这个类与 类类似.grid
,只是我们没有设置列数。auto-fit
替换了 类中的 12。.grid
这意味着让 CSS Grid 根据列的宽度值(repeat() 函数的第二个值)来决定列数。
可是等等!宽度值(.grid
类中的 1fr)现在被 minmax() 函数替换了。字面意思是,列的最小宽度为 0,最大值为 1fr。我们正在为列宽设置一个值范围。
结果是:您得到一个网格,其中所有列都具有相同的宽度,无论其内容或网格项的数量如何。
使用类似的方法,我们创建.grid-auto-{size}
实用程序类:
.grid-auto-xs, .grid-auto-sm, .grid-auto-md, .grid-auto-lg, .grid-auto-xl { // auto-sized grid
display: grid;
grid-gap: var(--grid-gap, 0);
grid-template-columns: repeat(auto-fit, minmax(var(--col-min-width), 1fr));
}
.grid-auto-xs { --col-min-width: 8rem; }
.grid-auto-sm { --col-min-width: 10rem; }
.grid-auto-md { --col-min-width: 15rem; }
.grid-auto-lg { --col-min-width: 20rem; }
.grid-auto-xl { --col-min-width: 25rem; }
与 不同.grid-auto-cols
,这些新类的最小宽度值等于--col-min-width
。结果是一个响应式网格,当有足够空间时,会添加一个新列(minmax() 函数中指定的最小宽度)。
🔥 使用 .grid-auto 实用程序类(.grid-auto-cols
和.grid-auto-{size}
),您可以创建响应式布局,而无需.col
在网格项上使用类。实际上,.col
如果您希望 .grid-auto 类正常工作,则根本不应该使用类。
最后,为了利用网格线号,我们可以创建一组新的实用程序类:col-start-{line-number}
和.col-end-{line-number}
。
@for $i from 1 through $grid-columns {
.col-start-#{$i} { grid-column-start: #{$i}; }
.col-end-#{$i+1} { grid-column-end: #{$i+1}; }
}
.col-start 类从.col-start-1
到col-start-12
,而 .col-end 类从.col-end-2
到.col-end-13
。
如果您希望网格项跨越特定的起点和终点线,请使用它们。
底部的负数是定位相同行的另一种方法。它们为何有用:如果您想定位最后一行,而不管列数是多少,您可以执行以下操作:
.col-end {
grid-column-end: -1;
}
.col-start/end 类允许您创建高级网格:
类断点修饰符
为了使网格在不同的断点处可编辑,我们可以添加类修饰符。在CodyFrame中,我们的惯例是为类添加 @{breakpoint} 后缀(例如col-4@sm
)来定位断点。
以下是 x-small 断点处的类修饰符的示例:
$breakpoints: (
xs: 32rem,
sm: 48rem,
md: 64rem,
lg: 80rem,
xl: 90rem
) !default;
@mixin breakpoint($breakpoint) {
@media (min-width: map-get($map: $breakpoints, $key: $breakpoint)) { @content; }
}
@include breakpoint(xs) {
.grid-auto-xs\@xs { --col-min-width: 8rem; }
.grid-auto-sm\@xs { --col-min-width: 10rem; }
.grid-auto-md\@xs { --col-min-width: 15rem; }
.grid-auto-lg\@xs { --col-min-width: 20rem; }
.grid-auto-xl\@xs { --col-min-width: 25rem; }
.grid-auto-cols\@xs { grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); }
@for $i from 1 through $grid-columns {
.col-#{$i}\@xs { grid-column-end: span #{$i}; }
.col-start-#{$i}\@xs { grid-column-start: #{$i}; }
.col-end-#{$i+1}\@xs { grid-column-end: #{$i+1}; }
}
.col-start-auto\@xs { grid-column-start: auto; }
.col-end-auto\@xs { grid-column-end: auto; }
}
⚡️ Flash Grid 的 Beta 版本
Flash Grid 测试版已准备就绪!
在 Codepen 上尝试一下:
最终的 SCSS 代码:
// ⚡️ Flash Grid
$grid-columns: 12 !default;
.grid, [class*="grid-auto-"] {
display: grid;
grid-gap: var(--grid-gap, 0);
}
.grid {
--grid-cols: #{$grid-columns};
grid-template-columns: repeat(var(--grid-cols), 1fr);
> * {
grid-column-end: span var(--grid-cols);
}
}
.grid-auto-xs, .grid-auto-sm, .grid-auto-md, .grid-auto-lg, .grid-auto-xl { // auto-sized grid
grid-template-columns: repeat(auto-fit, minmax(var(--col-min-width), 1fr));
}
.grid-auto-xs { --col-min-width: 8rem; }
.grid-auto-sm { --col-min-width: 10rem; }
.grid-auto-md { --col-min-width: 15rem; }
.grid-auto-lg { --col-min-width: 20rem; }
.grid-auto-xl { --col-min-width: 25rem; }
.grid-auto-cols { // cols = same size
grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
}
.grid-gap-xxxxs { --grid-gap: var(--space-xxxxs, 0.125rem); }
.grid-gap-xxxs { --grid-gap: var(--space-xxxs, 0.25rem); }
.grid-gap-xxs { --grid-gap: var(--space-xxs, 0.375rem); }
.grid-gap-xs { --grid-gap: var(--space-xs, 0.5rem); }
.grid-gap-sm { --grid-gap: var(--space-sm, 0.75rem); }
.grid-gap-md { --grid-gap: var(--space-md, 1.25rem); }
.grid-gap-lg { --grid-gap: var(--space-lg, 2rem); }
.grid-gap-xl { --grid-gap: var(--space-xl, 3.25rem); }
.grid-gap-xxl { --grid-gap: var(--space-xxl, 5.25rem); }
.grid-gap-xxxl { --grid-gap: var(--space-xxxl, 8.5rem); }
.grid-gap-xxxxl { --grid-gap: var(--space-xxxxl, 13.75rem); }
@for $i from 1 through $grid-columns {
.col-#{$i} { grid-column-end: span #{$i}; }
.col-start-#{$i} { grid-column-start: #{$i}; }
.col-end-#{$i+1} { grid-column-end: #{$i+1}; }
}
.col-start { grid-column-start: 1; }
.col-end { grid-column-end: -1; }
// breakpoints
$breakpoints: (
xs: 32rem,
sm: 48rem,
md: 64rem,
lg: 80rem,
xl: 90rem
) !default;
@mixin breakpoint($breakpoint) {
@media (min-width: map-get($map: $breakpoints, $key: $breakpoint)) { @content; }
}
@include breakpoint(xs) {
.grid-auto-xs\@xs { --col-min-width: 8rem; }
.grid-auto-sm\@xs { --col-min-width: 10rem; }
.grid-auto-md\@xs { --col-min-width: 15rem; }
.grid-auto-lg\@xs { --col-min-width: 20rem; }
.grid-auto-xl\@xs { --col-min-width: 25rem; }
.grid-auto-cols\@xs { grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); }
@for $i from 1 through $grid-columns {
.col-#{$i}\@xs { grid-column-end: span #{$i}; }
.col-start-#{$i}\@xs { grid-column-start: #{$i}; }
.col-end-#{$i+1}\@xs { grid-column-end: #{$i+1}; }
}
.col-start-auto\@xs { grid-column-start: auto; }
.col-end-auto\@xs { grid-column-end: auto; }
}
@include breakpoint(sm) {
.grid-auto-xs\@sm { --col-min-width: 8rem; }
.grid-auto-sm\@sm { --col-min-width: 10rem; }
.grid-auto-md\@sm { --col-min-width: 15rem; }
.grid-auto-lg\@sm { --col-min-width: 20rem; }
.grid-auto-xl\@sm { --col-min-width: 25rem; }
.grid-auto-cols\@sm { grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); }
@for $i from 1 through $grid-columns {
.col-#{$i}\@sm { grid-column-end: span #{$i}; }
.col-start-#{$i}\@sm { grid-column-start: #{$i}; }
.col-end-#{$i+1}\@sm { grid-column-end: #{$i+1}; }
}
.col-start-auto\@sm { grid-column-start: auto; }
.col-end-auto\@sm { grid-column-end: auto; }
}
@include breakpoint(md) {
.grid-auto-xs\@md { --col-min-width: 8rem; }
.grid-auto-sm\@md { --col-min-width: 10rem; }
.grid-auto-md\@md { --col-min-width: 15rem; }
.grid-auto-lg\@md { --col-min-width: 20rem; }
.grid-auto-xl\@md { --col-min-width: 25rem; }
.grid-auto-cols\@md { grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); }
@for $i from 1 through $grid-columns {
.col-#{$i}\@md { grid-column-end: span #{$i}; }
.col-start-#{$i}\@md { grid-column-start: #{$i}; }
.col-end-#{$i+1}\@md { grid-column-end: #{$i+1}; }
}
.col-start-auto\@md { grid-column-start: auto; }
.col-end-auto\@md { grid-column-end: auto; }
}
@include breakpoint(lg) {
.grid-auto-xs\@lg { --col-min-width: 8rem; }
.grid-auto-sm\@lg { --col-min-width: 10rem; }
.grid-auto-md\@lg { --col-min-width: 15rem; }
.grid-auto-lg\@lg { --col-min-width: 20rem; }
.grid-auto-xl\@lg { --col-min-width: 25rem; }
.grid-auto-cols\@lg { grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); }
@for $i from 1 through $grid-columns {
.col-#{$i}\@lg { grid-column-end: span #{$i}; }
.col-start-#{$i}\@lg { grid-column-start: #{$i}; }
.col-end-#{$i+1}\@lg { grid-column-end: #{$i+1}; }
}
.col-start-auto\@lg { grid-column-start: auto; }
.col-end-auto\@lg { grid-column-end: auto; }
}
@include breakpoint(xl) {
.grid-auto-xs\@xl { --col-min-width: 8rem; }
.grid-auto-sm\@xl { --col-min-width: 10rem; }
.grid-auto-md\@xl { --col-min-width: 15rem; }
.grid-auto-lg\@xl { --col-min-width: 20rem; }
.grid-auto-xl\@xl { --col-min-width: 25rem; }
.grid-auto-cols\@xl { grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); }
@for $i from 1 through $grid-columns {
.col-#{$i}\@xl { grid-column-end: span #{$i}; }
.col-start-#{$i}\@xl { grid-column-start: #{$i}; }
.col-end-#{$i+1}\@xl { grid-column-end: #{$i+1}; }
}
.col-start-auto\@xl { grid-column-start: auto; }
.col-end-auto\@xl { grid-column-end: auto; }
}