Flexbox 和 Sass Grid 教程:如何簡化響應式設計
已發表: 2022-03-11最近,我面臨創建自己的網格系統的挑戰,由於重新發明輪子作為一種學習體驗總是有用的,我就去做了。 我知道這將是一個有趣的挑戰,但結果竟然如此簡單,讓我感到驚訝!
在這個實驗中,我們將研究 Flexbox 佈局以及它們如何允許優雅地實現佈局而不做任何瘋狂的 hack。 此外,如果您不熟悉 Sass,我們將了解它的工作原理並使用一些方便的 Sass 實用程序。 你甚至可以學到一些關於 CSS 網格的新知識,比如 Bootstrap 中的網格。
Sass 和 Flexbox 的簡短介紹
Sass 基本上是一個可以讓你避免 CSS 的一些缺點的工具,它是一種可以解釋為 CSS 的腳本語言。 如果您已經在編寫 CSS 樣式,那麼語法看起來非常熟悉,但它的工具箱包括變量、可重用的 mixin 以及 if、for、each 和 while 指令等。 Sass 最方便的事情之一是任何有效的 CSS 代碼都是有效的 Sass,因此您可以逐步轉換您的代碼庫。
for循環的一個簡單示例:
@for $i from 1 through 3 { .a-numbered-class-#{$i} { width: (20 * $i) * 1px; } } 這個簡單的循環從 1 迭代到 3 並創建類。 迭代的索引將被方便地存儲在$i中。 我們還可以進行數學運算並打印.a-numbered-class-X三次,每次具有不同的寬度。 此代碼輸出:
.a-numbered-class-1 { width: 20px; } .a-numbered-class-2 { width: 40px; } .a-numbered-class-3 { width: 60px; }正如我們所看到的,我們可以抽像出許多您必須在 CSS 中完成的工作。 在 CSS 中,您必須手動複製粘貼和修改,這顯然更容易出錯且不太優雅,如果您還沒有嘗試過,請不要再浪費時間了!
Flexbox 代表 Flexible Box,一種動態定位和分佈元素的 CSS3 佈局系統。 它是一個非常強大的工具,可以以最小的努力實現靈活的佈局。 有關如何學習 Flexbox 的更多詳細信息,請查看 Chris Coyier 的 Flexbox 完整指南。
網格
繼續討論網格本身,讓我們從它的基本元素開始。 它們將受到 Bootstrap 的網格元素的啟發:容器、行和列,每一個都包含在前者中。
我們將對類的名稱使用 BEM 命名約定。 BEM 約定使用起來非常簡單,並且添加了很多關於元素及其上下文的信息。 簡而言之,你有:
- Blocks ,它“封裝了一個獨立的實體,它本身就有意義”:
.block。 - 元素,它們是“塊的一部分,沒有獨立的意義”,由塊名稱、兩個下劃線和元素表示:
.block__elem - 修飾符,例如“塊或元素上的標誌”,用兩個破折號表示:
.block .block--mod。
容器
這是網格的最外層元素,它將包含我們的行元素。 有兩種類型的容器: .container和.container--fluid 。
.container的行為定義為某個點下方寬度的 100%,其上方具有最大固定寬度,並且左右邊距相等:
$grid__bp-md: 768; .container { max-width: $grid__bp-md * 1px; margin: 0 auto; }通過擴展和收縮“輸出”窗口來玩它
對於始終具有 100% 寬度的流體容器,我們只需使用修改器覆蓋這些屬性:
&--fluid { margin: 0; max-width: 100%; }在這裡玩。
那很簡單! 我們現在已經實現了兩個容器。 讓我們繼續下一個元素。
行
行將是我們內容的橫向組織者。
我們將使用 Flexbox 來定位行的子元素,使它們環繞以防止溢出,並在行內為它們提供 100% 的寬度(以便我們以後可以嵌套它們)。
&__row { display: flex; flex-wrap: wrap; width: 100%; }如果子元素的寬度總和大於自身,這會將子元素並排放置並換行。 我們現在只需要添加一些 div,它看起來像這樣:
通過擴展和收縮“輸出”窗口來玩它。
事情開始成形,但這還不是 CSS 網格。 它不見了……
列
列是網站內容所在的地方。 他們定義了行被劃分為多少部分以及它們佔據了多少。 我們將進行十二列佈局。 這意味著我們可以將行分成一個或最多十二個部分。
首先,一些基本的數學。 當我們想要一列時,它的寬度應該是 100%。 如果我們想要十二列。 那麼每個應該佔據寬度的 8.333…% 或 100/12。
使用 Flexbox,要以這種方式分發內容,我們可以使用flex-basis 。
為了分成四列,我們現在添加如下內容:
flex-basis: (100 / 4 ) * 1%;這樣,我們可以讓每個元素佔據 25% 的寬度——或者我們想要的任何百分比。
在這裡玩。
讓我們讓它更有活力。 因為我們希望這反映我們可能的類,所以我們調用.col-1 ,這是一個列 div 的類,它將具有 8.333% 的寬度,因為它們中的 12 個在它們必須換行之前應該適合。 該百分比將自始至終遞增,直到.col-12佔據 100%。
$grid__cols: 12; @for $i from 1 through $grid__cols { .col-#{$i} { flex-basis: (100 / ($grid__cols / $i) ) * 1%; } } 只是為了澄清發生了什麼,假設我們想將寬度分成四個相等的部分。 我們需要.col-3 ,因為它在 12 中適合 4 次,這意味著.col-3應該有 25% 的 flex-basis:
100 / ($grid__cols / $i) 100 / (12 / 3) = 25這已經開始看起來像一個網格了!
在這裡玩。
屏幕寬度相關的列
我們現在希望能夠擁有一個在移動設備上具有一定寬度但在平板電腦上具有不同寬度的元素等等。 我們將根據窗口的寬度使用某些斷點。 我們的 UI 將對這些斷點做出反應,並適應適合不同設備屏幕尺寸的理想佈局。 我們將按大小命名斷點:小 (sm)、中 (md) 等等, .col-sm-12將是一個在sm斷點之前至少佔據 12 列的元素。
讓我們重命名.col-*類.col-sm-* 。 由於我們的網格將首先是移動的,因此我們將其屬性應用於所有屏幕尺寸。 對於我們需要在更大的屏幕上表現不同的那些,我們將添加類: .col-md-* 。
想像一個帶有.col-sm-12和.col-md-4的元素。 預期的行為將是,在斷點“md”(中)之下,它將有 100% 的寬度,而在它之上,它將有 33.333%——這是很常見的情況,因為在移動設備上,您可能需要將元素堆疊在頂部而不是旁邊當您的寬度受到更多限制時,彼此。

為此,我們需要在斷點處添加一個媒體查詢(一個包含僅在特定寬度之上或之下或在特定設備上執行的代碼的表達式),並像我們之前為sm所做的那樣創建我們的md列:
@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%; } } }在這裡玩。
這已經接近有用的東西了。 這有點濕(明白嗎?它不干......),所以讓我們讓它更抽象。
正如我們所看到的,我們需要為每個斷點創建一個媒體查詢,所以讓我們創建一個 mixin 來接收一個動態創建媒體查詢的斷點。 它可能看起來像這樣:
@mixin create-mq($breakpoint) { @if($breakpoint == 0) { @content; } @else { @media screen and (min-width: $breakpoint *1px) { @content; } } } 現在,讓我們將用於創建__col類的內容包裝在一個名為create-col-classes的 mixin 中,並使用create-mq mixin。
@mixin create-col-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%; } } } }就是這樣。 為了使用它,我們現在在 Sass 映射中定義斷點,並對其進行迭代。
$map-grid-props: ('-sm': 0, '-md': $grid__bp-md, '-lg': $grid__bp-lg); @each $modifier , $breakpoint in $map-grid-props { @include create-col-classes($modifier, $grid__cols, $breakpoint); } 我們的網格系統基本完成了! 我們已經定義了一個默認的.container__col-sm-*類,我們可以使用container__col-md-*和container__col-lg-*在更大的屏幕上修改它的行為。
我們甚至可以嵌套行! 在這裡玩。
這樣做的好處是,如果我們現在希望它具有與 Bootstrap v4 相同的斷點,我們只需要這樣做:
$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 );就是這樣! 在這裡玩。
請注意 Bootstrap 如何採用比我們最初討論的更完整的移動優先方法。 最小的窗口大小沒有sm或md之類的後綴,原因是等效於.container__col-X的類不僅適用於 0 到 576px 的窗口寬度; 如果我們不明確地覆蓋它,它將是每個窗口大小的列數。 否則,我們可以添加類.container__col-sm-Y使其在sm斷點之間的寬度為 Y 列。
偏移量
偏移量將相對於前一列增加一個邊距。 .container__col-offset-4將在所有屏幕尺寸上添加margin-left: 33.333% 。 .container__col-md-offset-4將執行相同的操作,但高於md斷點。
現在的實現很簡單; 我們在創建類的同一個循環中添加了一個-offset屬性,但是我們編寫了屬性margin-left而不是flex-bases 。 我們還必須為-offset-0做一個額外的操作,因為我們可能想在更大的屏幕上清除邊距:
@mixin create-col-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%; } } } }我們現在有了功能齊全的偏移量! 在這裡玩。
可顯示性
我們有時想在某個點下方或上方顯示/隱藏元素。 為此,我們可以像 Bootstrap v4 那樣提供類。
例如,類.hidden-md-up將從md斷點向上隱藏任何具有此類的元素; 相反, .hidden-md-down會將其從斷點向下隱藏。
代碼很簡單:我們只是迭代斷點並for each斷點創建一個.hidden-*類。 不過,我們將create-mq類修改為更抽像一點:
@each $modifier , $breakpoint in $map-grid-props { @if($modifier == '') { $modifier: '-xs'; } @include create-mq($breakpoint - 1, 'max') { .hidden#{$modifier}-down { display: none !important; } } @include create-mq($breakpoint, 'min') { .hidden#{$modifier}-up { display: none !important; } } } 作為旁注,這不是!important的少數幾個好的用途之一嗎? 請注意,該元素可能具有任意更大的特定性display: block規則,但我們仍然希望將其隱藏在斷點下方或上方。 如果您不同意這種方法,請在評論中告訴我!
就是這樣:我們現在有一個可展示性系統。
在這裡玩。
結論
雖然這個“框架”還沒有為生產做好準備,但它展示了 Flexbox 佈局的強大功能以及 Sass 的便捷性。 只需幾行代碼,我們就實現了 CSS 框架/網格的核心功能。
它也可以作為一個教訓,即幾乎任何軟件的基本版本都可以非常容易地實現。 現實世界的具體問題開始加起來並變得困難。
我創建了一個 GitHub 存儲庫,您可以在其中提交問題或拉取請求。
您希望實現哪些功能? 實現可以簡化還是更優雅?
隨時讓我知道您對以下評論的看法。
