[CSS] Flex/Grid Layout Modules, part 3

Flex 容器設定

結果我真的兩篇就快講完了(驚恐)。

既然是說要講切版的話,還是多少交代一些比較基礎的東西好了。

Flex 的軸流向與留白

之前我們提及了多數 CSS 框架的設計,絕大部分都是 row 的主要軸流向。原因也相對單純,基本上你不太會擁有一個「無限寬度」的裝置,所以在目前主流裝置下,我們的寬度都會存在一個極大值,這個極大值基本上就會是整個 Flex 容器的主要軸尺寸。

所以,我們以雙欄結構來看,最單純的設定就會像是這樣,

.flex-container {
    display: flex;
    flex-flow: row wrap;
    align-items: flex-start;
    justify-content: flex-start;
}

接著,由於我們需要「雙欄」的設計,所以我們的元件定義就是,

.flex-item {
    // 通用設定放這邊
    margin: 0 10px;
}

.flex-aside {
    // 側邊區塊,我們預設把他放在左邊
    flex: 1 0 20%;
    max-width: calc(20% - 20px);
}

.flex-main {
    // 主要內容區塊,我們把他放在右邊
    flex: 1 0 80%;
    max-width: calc(80% - 10px);
    
    // 取消左邊留白,讓我們的間隔看起來相同
    margin-left: 0;
}

最終我們會獲得這樣的結果,

Flex 容器設定

可是你不是說不要用 margin 來定義間隔(gap)嗎?

是的,我們在這邊也可以使用 padding 來製作我們間隔,但是,當你的 Flex 容器或是元件有使用背景設定時(background),你還是得使用 margin 來當作間隔會比較適當。因為 background 會包含 padding 的留白部分,所以對於 Flex 元件來說,使用 margin 會比較適當。

當然,如果你使用背景的區塊是在 Flex 元件內的話,那麼使用 marginpadding 的差異就不大。另外,有些狀況使用 margin 是必須特別留意的,

  1. 當你有使用 stretch 的時候。
  2. 當你的 margin 有需要使用 auto 的時候。

對於 Flex 元件來說,margin 的關鍵字 auto 有一個特殊的效果,就是 自動補白 的特性。舉例來說,我們剛剛的主要欄位如果這樣設定,

.flex-main {
    margin-left: auto;
}

那麼他就會將 剩餘空間 往左邊補白。請留意,這裡的剩餘空間是指容器本身的剩餘空間。由於我們使用了計算過後的 max-width 會將整個 Flex 容器填滿。在沒有填滿,且沒有設定 flex-grow: 1 的情況下,這個自動補白才會生效。

Flex 元件在不填滿狀態下,margin 自動補白才會生效

自動補白看起來好像很好用,不過,margin 會與部分樣式有權重問題,

  • marginjustify-content, align-items 會產生疊加效應。
  • auto 關鍵字會覆蓋部分 justify-contentalign-items 的設定。
  • margin 使用數值在一定條件(大於剩餘空間)下,會覆蓋 justify-contentalign-items 的設定。

只要是跟 剩餘空間 有關的部分都會受到影響。

當 margin-bottom 大於 align-self: center 計算剩餘空間
stretch 與 margin 影響樣式的狀態

請留意,無論是疊加還是干涉樣式設定,其本身的樣式還是有效的,以上述的例子來說,

.flex-main {
    align-self: center;
    margin-top: auto;
}

雖然最終呈現的狀態並非 align-self: center 的結果,但其自身還是受到 align-self: center 的樣式所規範。而左邊的 align-self: stretch; 也是一樣的道理。

另外,也會影響這個結果的則是 flex-growflex-shrink 這兩個樣式設定。我們先前有提到 flex-grow 是做一個 填充 的動作,所以,當你在沒有指定 Flex 元件的 軸方向 尺寸時,他會將軸方向作填滿動作。

換句話說,這個填滿跟 margin 的樣式權重,由大到小排列會是這樣,

  • margin 數值尺寸,例如 margin-left: 200px
  • flex-grow
  • flex-basis || width || max-width || height || max-height
  • margin 的關鍵字 auto

而如果是 flex-shrink 的話,就比較特殊了,由於他是 壓縮,所以在我們使用 margin 的情況下,並不會 觸發 壓縮這件事情,只有當 Flex 容器空間不足夠時才會觸發,且他也同樣會干擾壓縮的情況。

  • margin 數值尺寸,例如 margin-left: 200px
  • flex-shrink
  • flex-basis || width || min-width || height || min-height
  • margin 的關鍵字 auto

其順序上跟 flex-grow 雷同,但他的先決條件是 Flex 容器空間不足 的情況下才會發生。


Flexbox 巢狀結構

基本上應用的方式與一般 Flex 容器沒有差異,就是單純的把 Flex 元件再次指定為 Flex 容器,在使用上沒有太大的區別。不過你得留意的是,原有區塊元件的父子元件同方向 margin 合併問題在巢狀結構中並不會發生。

也就是說,Flex 元件與容器會獨立擁有各自的 margin,若 Box 元件沒有設定 overflow 的情況下,

.box-module-parent {
    margin-top: 30px;
}
.box-module-child {
    margin-top: 10px;
}

上述兩者因為同方向 margin 的關係,會產生合併(collapse),

Box Module 父子元件同方向 margin 會產生合併

而這件事情在 Flex 容器中不會發生,

.flex-module-parent {
    margin-top: 30px;
}
.flex-module-child {
    margin-top: 10px;
}
Flex 容器並不會產生 margin 合併效果

當然,你在一般 Flex 容器遇到的 margin 狀況,在這種情況下也免不了。所以,基本上多層次的運用 Flexbox 也不會有太多的意外,該寫的意外我應該都寫的八九成了。

Flexbox 終究還是拿來畫出框架的。

其實總的來說,這跟你用許多 CSS 框架的方式是雷同的,若是以 Bootstrap 為例子,其實就類似於,

<div class="conatiner">
    <div class="row">
        <div class="col-4">
        </div>
        <div class="col-8">
            <div class="row">
                <div class="col-6">
                </div>
                <div class="col-6">
                </div>
            </div>
        </div>
    </div>
</div>

這就端看你要怎麼去規劃你的框架該長成什麼樣子,只是在不使用 CSS 框架的情況下,你的 HTML 可以稍微簡潔一點點。當然,那種寫到爛掉的也不是沒有。


關於對齊

其實比較讓人覺得有趣的,大概就是置中對齊這件事情。

當然講到置中對齊,就得呼叫一下 Amos CSS垂直置中技巧,我只會23個,你會幾個

我只會一種,就是 align-items: center; 這樣。

說到對齊,就得提到 CSS 關於對齊的模組 CSS Box Alignment Module Level 3,但這邊不會提到太多,我們就單純的拿 Flexbox 可以使用的部分出來聊聊。

樣式 應用範圍 適用狀態
justify-content Flex 容器 主要軸方向
align-content Flex 容器 交叉軸方向,多行狀態
align-items Flex 容器 主要軸方向
align-self Flex 元件 多行狀態

請注意有兩個樣式是必須要有 多行狀態 才會有效果。當然,說到對齊就必須得提到先前說過的 flex-growflex-shrink 這兩個樣式設定。在我們沒有特別指定主要軸、交叉軸尺寸,或是特別指定 Flex 元件尺寸的情況下,這兩個設定在某些情況下會讓你產生 客戶說沒對齊 的情況。

什麼叫做 客戶說沒對齊

當兩個容器想要基於底邊置中,卻不等高時

邏輯上來說,這是對齊的,但如果需要「根據底邊置中對齊」的時候,他就不符合預期結果。當然,請不要傳這一篇去給客戶看謝謝。 身為一個專業的前端工程師,這樣的問題也不是沒有解法。

你就把兩個元件鎖定相同高度就好(卍解)。

我在 Flexbox 最後面會解釋關於 Flex 的一些演算方式,這邊就暫不贅述。這樣的問題其實很常發生,當你的 Flex 容器允許斷行,在產生多行的情況下,由於整個容器的交叉軸尺寸是未知的(也就是隨內容動態增長),所以元件尺寸就會因為鄰近元件的關係而造成對齊點的問題。

以往我們使用區塊元件,利用 float: left; 大多會有這種狀況,

Float 元件靠左對齊的特性

雖然 Flex 容器沒有 靠左對齊 的特性,但,

Flex 元件斷行是以「整行」為基準

雖然沒有了奇怪的對齊點,但這就是所謂的有一好沒兩好。這一點在 Grid Layout Module 裡面會更明顯。所以,為了解決這種奇怪的補白問題,在一些必要情況下,請賦予你的 Flex 元件一個交叉軸尺寸,用以確保你在 flex-wrap: wrap; 的情況下會是比較好的呈現結果。

以下我直接用圖片來解釋各種 對齊

Flex 容器主要軸方向對齊設定

附帶一題,最後的 space-evenly 是屬於對齊模組草稿階段,但瀏覽器支援度也是不錯。在 Caniuse 裡面有 95% 以上的支持度。

CSS Box Alignment Module Level 3, Editor's Draft

space-evenly, caniuse.com

另外一個就是交叉軸的對齊(stretch 不算但我還是列出來),

Flex 容器主要軸方向對齊設定

小記

基本會遇到的狀況在這邊大概可以涵蓋個七八成,所以這個時候你再回頭去看看那些知名 CSS 框架,應該就可以理解人家為什麼要這樣做。

下一次會提及一些關於 Media Query 的事情來聊聊 Flexbox 的使用情境。


目錄與小節:
[CSS] Flex/Grid Layout Modules, part 0


鐵人賽同步放送:
https://ithelp.ithome.com.tw/articles/10260896

Hina Chen
偏執與強迫症的患者,算不上是無可救藥,只是我已經遇上我的良醫了。
Taipei