作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Juan Varela's profile image

Juan Varela

Juan是一名前端和后端开发人员,对用户体验、可用性和设计充满热情. 他有十多年的专业经验.

Previously At

Latam airlines
Share

Recently, 我面临着创建自己的网格系统的挑战, 因为重新发明轮子总是有用的学习经验, I went for it. 我知道这将是一个有趣的挑战,但我惊讶地发现它竟然如此简单!

Sass and Flexbox Grid Tutorial

In this experiment, 我们将研究Flexbox布局,以及它们如何在不做任何疯狂的hack的情况下实现优雅的布局. 此外,如果您不熟悉Sass,我们将了解它是如何工作的,并使用一些方便的Sass实用程序. 你甚至可以学到一些关于CSS网格的新知识,比如Bootstrap的一部分.

Sass和Flexbox的简短介绍

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.

The Grid

继续讨论网格本身,让我们从它的基本元素开始. 它们将受到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:

  • Blocks,它“封装了一个独立的实体,它本身是有意义的”: .block.
  • Elements, 哪些是“块的一部分,没有独立的意义”,由块名称表示, two underscores and the element: .block__elem
  • Modifiers,比如“块或元素上的标志”,用两个破折号表示: .block .block--mod.

Containers, Rows, and Columns

Containers

这是网格的最外层元素,它将包含我们的行元素. 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%;
  }

Play with it here.

That was easy! We now have both containers implemented. Let’s move on to the next element.

Rows

行将是内容的水平组织者.

我们将使用flexx来定位一行的子元素, 让它们自动换行,这样它们就不会溢出,并在行内设置100%的宽度(以便稍后我们可以嵌套它们).

&__row {
  display: flex;
  flex-wrap: wrap;
  width: 100%;
}

这将并排定位子元素,如果它们的宽度之和大于其本身,则将它们换行成新行. 我们现在只需要添加一些div,它看起来像这样:

Row elements

通过展开和收缩“输出”窗口来进行操作.

事情开始成形,但这还不是CSS网格. It’s missing…

Columns

列是站点内容所在的位置. 它们定义了一行被分成多少部分以及它们占据了多少部分. 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%——或者我们想要的任何百分比.

Play with it here.

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

这已经开始看起来像一个网格了!

Looks like a grid!

Play with it here.

Screen Width-dependent Columns

我们现在希望一个元素在移动设备上有一定的宽度,但在平板电脑上有不同的宽度. 我们将根据窗口的宽度使用特定的断点. 我们的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, 因为在移动设备上,当宽度有限时,你可能需要将元素堆叠在顶部而不是彼此相邻.

Stacking columns after hitting a breakpoint

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%;
    }
  }
}

Play with it here.

已经很接近有用的东西了. 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.

Offsets

偏移量将添加关于前一列的左边距. 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} {
        浮动基数:(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.

Displayability

有时,我们希望在某一点以下或上方显示/隐藏元素. 为此,我们可以提供像这样的类 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 类要更抽象一些:

在$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 规则,但我们仍然希望将其隐藏在断点的下方或上方. 如果你不同意这种方法,请在评论中告诉我!

就是这样:我们现在有了一个可显示性系统.

Play with it here.

Conclusion

虽然这个“框架”还不能用于生产, 它展示了Flexbox布局是多么强大,Sass是多么方便. 仅用几行代码,我们就实现了CSS框架/网格的核心功能.

它是否也可以作为一个教训,即几乎任何软件的基本版本都可以非常容易地实现. 现实世界中的具体问题开始累积起来,使之变得困难.

I created a GitHub repo 你可以在哪里提交问题或拉取请求.

您希望看到实现哪些功能? 实现是否可以更简化或更优雅?

欢迎在下面的评论中告诉我你的意见.

Understanding the basics

  • What is Sass?

    Sass是一种脚本语言,可以转换为CSS. 它提供了一组工具和功能,增强了编写CSS的体验,使代码更加优雅和简洁. Sass碰巧也是CSS3的超集,所以任何CSS3样式表都是有效的Sass样式表.

  • What is CSS3 Flexbox?

    一种新的布局元素的方式,允许更灵活的定位. It helps you position, 更优雅地对齐和间距HTML,它与现代浏览器兼容. Check out caniuse.com/flexbox for a list.

  • What is a grid system?

    网格系统是一种基于响应式布局来分发站点内容的方式, well, a grid. In this case, 我们分析了一个基于列的网格,其中指定给定屏幕尺寸的元素应该占用多少列.

Hire a Toptal expert on this topic.
Hire Now
Juan Varela's profile image
Juan Varela

Located in Graz, Austria

Member since April 19, 2017

About the author

Juan是一名前端和后端开发人员,对用户体验、可用性和设计充满热情. 他有十多年的专业经验.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

Previously At

Latam airlines

World-class articles, delivered weekly.

Subscription implies consent to our privacy policy

World-class articles, delivered weekly.

Subscription implies consent to our privacy policy

Toptal Developers

Join the Toptal® community.