作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Juan是一名前端和后端开发人员,对用户体验、可用性和设计充满热情. 他有十多年的专业经验.
PREVIOUSLY AT
Recently, 我面临着创建自己的网格系统的挑战, 因为重新发明轮子总是有用的学习经验, I went for it. 我知道这将是一个有趣的挑战,但我惊讶地发现它竟然如此简单!
In this experiment, 我们将研究Flexbox布局,以及它们如何在不做任何疯狂的hack的情况下实现优雅的布局. 此外,如果您不熟悉Sass,我们将了解它是如何工作的,并使用一些方便的Sass实用程序. 你甚至可以学到一些关于CSS网格的新知识,比如Bootstrap的一部分.
Sass 基本上是一个工具,可以让您避免CSS的一些缺点, 它是一种被解释为CSS的脚本语言. 如果您已经在编写CSS样式,那么语法看起来非常熟悉,但它的工具箱包括 variables, mixins for re-usability and if, for, each and while directives among others. 关于Sass最方便的事情之一是任何有效的CSS代码都是有效的Sass, 因此,您可以逐步转换代码库.
A simple example of a for loop:
@for $i from 1 through 3 {
.a-numbered-class-#{$i} {
width: (20 * $i) * 1px;
}
}
这个简单的循环从1迭代到3并创建类. 迭代的索引将被方便地存储在 $i
. We can also do math and print the .a-numbered-class-X
三次,每次宽度不同. This code outputs:
.a-numbered-class-1 {
width: 20px;
}
.a-numbered-class-2 {
width: 40px;
}
.a-numbered-class-3 {
width: 60px;
}
正如我们所看到的,我们可以将CSS中需要做的很多工作抽象出来. In CSS, 你必须手动复制、粘贴和修改, 哪一种显然更容易出错,而且不那么优雅, If you haven’t tried it yet, don’t waste any more time!
Flexbox stands for Flexible Box, 动态定位和分配元素的CSS3布局系统. 它是一个非常强大的工具,可以用最少的努力实现灵活的布局. 有关如何学习Flexbox的更多细节,请查看 Chris Coyier’s Complete Guide to Flexbox.
继续讨论网格本身,让我们从它的基本元素开始. 它们将受到Bootstrap的网格元素:容器的启发, Rows, and Columns, each contained within the former.
We’ll be using the BEM naming conventions for the classes’ names. BEM约定使用起来非常简单,并添加了大量关于元素及其上下文的信息. Briefly put, you have:
.block
..block__elem
.block .block--mod
.这是网格的最外层元素,它将包含我们的行元素. There are two types of containers: .container
and .container--fluid
.
The behavior of .container
定义为某一点以下宽度的100%, 宽边的:在其上方有最大固定宽度且左右边距相等的:
$grid__bp-md: 768;
.container {
max-width: $grid__bp-md * 1px;
margin: 0 auto;
}
For the fluid container, which always has 100% width, 我们只需要用一个修饰符覆盖这些属性:
&--fluid {
margin: 0;
max-width: 100%;
}
That was easy! We now have both containers implemented. Let’s move on to the next element.
行将是内容的水平组织者.
我们将使用flexx来定位一行的子元素, 让它们自动换行,这样它们就不会溢出,并在行内设置100%的宽度(以便稍后我们可以嵌套它们).
&__row {
display: flex;
flex-wrap: wrap;
width: 100%;
}
这将并排定位子元素,如果它们的宽度之和大于其本身,则将它们换行成新行. 我们现在只需要添加一些div,它看起来像这样:
事情开始成形,但这还不是CSS网格. It’s missing…
列是站点内容所在的位置. 它们定义了一行被分成多少部分以及它们占据了多少部分. We’re going to do a twelve column layout. 这意味着我们可以把这一行分成一个或最多十二个部分.
To start with, some basic math. 当我们想要一个列时,它的宽度应该是100%. If we want twelve columns. Then each should occupy 8.333…% or 100/12 of the width.
使用Flexbox,以这种方式分发内容,我们可以使用 flex-basis
.
为了将其分成四列,我们现在需要添加如下内容:
flex-basis: (100 / 4 ) * 1%;
这样,我们可以让每个元素占据宽度的25%——或者我们想要的任何百分比.
Let’s make that more dynamic. 因为我们想让它反映可能的类,所以我们调用 .col-1
, a class for a column div that will have 8.宽度的333%,因为其中12行在换行之前应该适合. 百分比将一直增加,直到 .col-12
, which will occupy 100%.
$grid__cols: 12;
@for $i from 1 through $grid__cols {
.col-#{$i} {
Flex-basis: (100 / ($grid__cols / $i)) * 1%;
}
}
为了弄清楚是怎么回事,假设我们想把宽度分成四个相等的部分. We would need .col-3
因为它在12次中适合4次,这意味着 .col-3
should have 25% flex-basis:
100 / ($grid__cols / $i)
100 / (12 / 3) = 25
这已经开始看起来像一个网格了!
我们现在希望一个元素在移动设备上有一定的宽度,但在平板电脑上有不同的宽度. 我们将根据窗口的宽度使用特定的断点. 我们的UI将对这些断点做出反应,并适应适合不同设备屏幕尺寸的理想布局. 我们将按大小命名断点:小(sm),中(md)等等, .col-sm-12
将是至少占用12列的元素,直到 sm
breakpoint.
Let’s rename the .col-*
class .col-sm-*
. 因为我们的网格首先是移动的,所以我们将把它的属性应用到所有屏幕尺寸上. 对于那些我们需要在更大屏幕上表现不同的内容,我们将添加这样的类: .col-md-*
.
Imagine an element with .col-sm-12
and .col-md-4
. 预期的行为将是,在断点“md”(中)以下,它将具有100%的宽度,在它之上,它将具有33.333%—a very common occurrence, 因为在移动设备上,当宽度有限时,你可能需要将元素堆叠在顶部而不是彼此相邻.
For this, 我们需要在断点处添加一个媒体查询(一个包含代码的表达式,该代码只会在特定宽度以上或以下或在特定设备上执行)并创建我们的 md
columns the like we did before for sm
:
@media screen and (min-width: $grid__bp-md * 1px) {
@for $i from 1 through $grid__cols {
&__col-md-#{$i} {
Flex-basis: (100 / ($grid__cols / $i)) * 1%;
}
}
}
已经很接近有用的东西了. That’s quite a bit WET (Get it? It isn’t DRY…), so let’s make it more abstract.
As we saw, 对于每个断点,我们需要一个媒体查询, 因此,让我们创建一个mixin,它接收一个动态创建媒体查询的断点. It could look something like this:
@mixin create-mq($breakpoint) {
@if($breakpoint == 0) {
@content;
} @else {
@media screen and (min-width: $breakpoint *1px) {
@content;
}
}
}
现在,让我们把创建 __col
classes in a mixin called create-col-classes
and use the create-mq
mixin.
@mixin create- color -classes($modifier, $grid__cols, $breakpoint) {
@include create-mq($breakpoint) {
@for $i from 1 through $grid__cols {
&__col#{$modifier}-#{$i} {
Flex-basis: (100 / ($grid__cols / $i)) * 1%;
}
}
}
}
And that’s it. 要使用它,我们现在在Sass映射中定义断点,并迭代它们.
map-grid-props:美元(“sm”:0,“md”:grid__bp-md美元,“lg”:grid__bp-lg美元);
在$map-grid-props{中设置$断点
@include create- color -classes($modifier, $grid__cols, $breakpoint);
}
Our grid system is basically done! We have defined an .container__col-sm-*
类,它将是默认的,我们可以在更大的屏幕上使用 container__col-md-*
and container__col-lg-*
.
We can even nest rows! Play with it here.
这样做的好处是,如果我们现在想让它有相同的断点 Bootstrap v4 we would just need to do:
$grid__bp-sm: 576;
$grid__bp-md: 768;
$grid__bp-lg: 992;
$grid__bp-xl: 1200;
$map-grid-props: (
'': 0,
'-sm': $grid__bp-sm,
'-md': $grid__bp-md,
'-lg': $grid__bp-lg,
'-xl': $grid__bp-xl
);
And that’s it! Play with it here.
请注意Bootstrap采用了比我们最初讨论的更完整的移动优先方法. 最小的窗口大小没有后缀 sm
or md
,理由是类等价于 .container__col-X
will not only be applied from a window width of 0 to 576px; if we don’t overwrite it explicitly, 它将是每个窗口大小的列数. Otherwise, we could add the class .container__col-sm-Y
to make it a width of Y columns between the sm
breakpoints.
偏移量将添加关于前一列的左边距. A .container__col-offset-4
will add a margin-left: 33.333%
on all screen sizes. .container__col-md-offset-4
will do the same but above the md
breakpoint.
The implementation is now trivial; we add an -offset
属性创建类,而不是 flex-bases
, we write the property margin-left
. We have to do an extra one for -offset-0
此外,由于我们可能希望在更大的屏幕上清除边距:
@mixin create- color -classes($modifier, $grid-cols, $breakpoint) {
@include create-mq($breakpoint) {
&__col#{$modifier}-offset-0 {
margin-left: 0;
}
@for $i from 1 through $grid-cols {
&__col#{$modifier}-#{$i} {
flex-basis: (100 / ($grid-cols / $i) ) * 1%;
}
&__col#{$modifier}-offset-#{$i} {
Margin-left: (100 / ($grid-cols / $i)) * 1%;
}
}
}
}
We now have fully functional offsets! Play with it here.
有时,我们希望在某一点以下或上方显示/隐藏元素. 为此,我们可以提供像这样的类 the ones of Bootstrap v4.
For example, the class .hidden-md-up
将隐藏带有此类的任何元素 md
breakpoint upwards; conversely, .hidden-md-down
will hide it from the breakpoint down.
此操作的代码也很简单:只需迭代断点并创建一个 .hidden-*
class with a for each
breakpoint. We modified the create-mq
class to be a little more abstract, though:
在$map-grid-props{中设置$断点
@if($modifier == '') {
$modifier: '-xs';
}
@include create-mq($断点- 1,'max') {
.hidden#{$modifier}-down {
display: none !important;
}
}
@include create-mq($breakpoint, 'min') {
.hidden#{$modifier}-up {
display: none !important;
}
}
}
作为旁注,这不是少数几个很好的用法之一吗 !important
? 注意,元素可以有任意大的 specificity with a display: block
规则,但我们仍然希望将其隐藏在断点的下方或上方. 如果你不同意这种方法,请在评论中告诉我!
就是这样:我们现在有了一个可显示性系统.
虽然这个“框架”还不能用于生产, 它展示了Flexbox布局是多么强大,Sass是多么方便. 仅用几行代码,我们就实现了CSS框架/网格的核心功能.
它是否也可以作为一个教训,即几乎任何软件的基本版本都可以非常容易地实现. 现实世界中的具体问题开始累积起来,使之变得困难.
I created a GitHub repo where you can submit issues or pull requests.
您希望看到实现哪些功能? 实现是否可以更简化或更优雅?
欢迎在下面的评论中告诉我你的意见.
Sass是一种脚本语言,可以转换为CSS. 它提供了一组工具和功能,增强了编写CSS的体验,使代码更加优雅和简洁. Sass碰巧也是CSS3的超集,所以任何CSS3样式表都是有效的Sass样式表.
一种新的布局元素的方式,允许更灵活的定位. It helps you position, 更优雅地对齐和间距HTML,它与现代浏览器兼容. Check out caniuse.com/flexbox for a list.
网格系统是一种基于响应式布局来分发站点内容的方式, well, a grid. In this case, 我们分析了一个基于列的网格,其中指定给定屏幕尺寸的元素应该占用多少列.
Graz, Austria
Member since April 19, 2017
World-class articles, delivered weekly.
World-class articles, delivered weekly.
Join the Toptal® community.