間隔、間隔、間隔

先不囉唆,上圖。

中間看起來比較密集的部分,就是所謂的 間隔,在樣式表當中就是這樣設定,

.grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: repeat(4, 1fr);
    grid-gap: 1rem;
}

這邊能夠接受的數值除了 fr 不能用以外,其他數字你要怎麼寫都可以。你也可以把間隔設定的比元件尺寸還要大,但是應該不會有人做這種事情吧。那麼,這個間隔有什麼毛病呢?請你回去看看剛剛那張圖。

看到那個精美的內容衝到間隔裡面了嗎?

是的,這跟昨天在說明 Grid 規則跟尺寸的部分相同。他無法硬性規定你的元件不能超過格線軌道的尺寸限制,所以,你所設計的間隔如果遇到這種狀況,並不能真實反應兩個元件之間是否一定存在有這樣的間距。在這個時候,你是不是開始懷念 float: left; 然後 margin: 0 15px 這種設計了。

所以,從 grid-templategrid-gap 整組的容器大結構來看,基本上就是設計一個有格線軌道的容器,然後,然後就沒有然後了。不然你覺得 Grid Layout Module 還能做什麼事情呢?

好啦,我後面可以告訴你一些八奇的用法。


合併、重複與空區塊

在格線系統當中,有一個很特別的屬性,可以針對格線區塊來命名,

.grid {
    display: grid;
    grid-template-areas: 
        "head head head"
        "lefthand body lover"
        ". foot ."
}

我們沒有特別指定 rowscolumns,直接使用 grid-template-areas 來指定需要的區塊,並且賦予他們名字,我們現在就來看看上面那樣的設定代表哪些事情。

  • "head head head" 代表我有一個 1 row x 3 columns 的區塊,他們的 名字都一樣,表示他會變成 合併儲存格
  • "lefthand body righthand" 格線區塊同上,但是他們不會合併,是獨立的區塊。
  • ". foot ." 格線區塊同上,但由於使用了 .,所以這個區塊不會被使用(變成空白區塊)。

所以,上面的設定就會變成一個 3 x 3 的格線容器。這個時候,倘若你設定了 rowscolumns 的話,如果設定的容器比較大,就會從 3 x 3 長大,如果比 areas 設定的小,那麼最小就會使用 3 x 3 的格線容器。

Grid Template Areas

接著,你要把容器元件放到 指定名稱的區塊,所以就會變成這樣的設定,

.grid .head {
    grid-area: head;
}
.grid .foot {
    grid-area: foot;
}
.grid .lefthand {
    /* 左手只是輔助 */
    grid-area: lefthand;
}
.grid .lover {
    grid-area: lover;
}
.grid .body {
    grid-area: body;
}

好的,如果說 指定位置 的動作在樣式表當中就做好了,那麼我 HTML 要做什麼?

就像你問男(女)朋友晚餐要吃什麼?

隨便。

所以 HTML 可以這樣寫也沒關係,

<section class="grid">
    <div class="item foot"></div>
    <div class="item lover"></div>
    <div class="item body"></div>
    <div class="item lefthand"></div>
    <div class="item head"></div>
</section>

千萬不要,好嗎。

雖然格線系統的目的是讓你排版,但是不代表你可以惡搞 DOM 結構。另外,如果你用了一個沒有被命名的區塊,他還是會畫出來給你。你覺得他會放在哪裡?

<section class="grid">
    <div class="item foot"></div>
    <div class="item lover"></div>
    <div class="item body"></div>
    <div class="item lefthand"></div>
    <div class="item head"></div>
    <div class="item r20"></div>
</section>
.grid .r20 {
    grid-area: r20;
}

我先把結果放上來,然後你自己想想他放在什麼地方。

好的,這個放置位置就跟格線系統的填滿規則有關。當你的元件放在一個 不存在 的區塊,他會有兩種反應,

  • 如果是命名區塊不存在或沒指定名稱,會先去放置到第一個空區塊,並依序把空區塊放滿。
  • 如果空區塊都被放滿了,則依照格線稀疏排列方式繼續往下排列。
  • 如果是元件指定位置,則會把格線軌道長大到那個尺寸,然後把元件放在那個位置上。

來,我們看看指定位置超出容器的狀況,

.grid .r20 {
    grid-column: 5 / 6;
    grid-row: 2 / 3;
}

最終你會得到這樣的結果,

指定位置超出格線系統

你會得到一個 變大 的格線容器。好的,這樣可以理解空區塊,命名區塊跟超出容器設定的差異了吧。另外,在格線系統中,剛剛提到的間隔,每個間隔其實也可以命名,舉例來說,

.grid {
    grid-template-columns: [first] 1fr [two] 1fr [lala] 1fr [lol] 1fr [column-end];
    grid-template-rows: [hello] 1fr [r20] 1fr [world] 1fr [last] 1fr [row-end];
}

命名方式有很多種變形,例如可以多組名字,

.grid {
    grid-template-columns: [first] 1fr [first-end two] 1fr [two-end lala] 1fr [lol] 1fr [column-end];
    grid-template-rows: [hello] 1fr [r20] 1fr [world] 1fr [last] 1fr [row-end];
}

當中的 [first-end two] 就代表這個間隔有兩個名字。然後,

grid-template-columnsgrid-template-rows 會覆蓋 grid-template-areas 的尺寸。

舉例來說,把剛剛上面的 grid-template-areas 合併起來看,

.grid {
    display: grid;
    grid-template-columns: [first] 1fr [two] 1fr [lala] 1fr [lol] 1fr [column-end];
    grid-template-rows: [hello] 1fr [r20] 1fr [world] 1fr [last] 1fr [row-end];
    grid-template-areas: 
        "head head head"
        "lefthand body lover"
        ". foot ."
}

他就會變成 4 x 4 然後中間有包含一個有命名的 grid-template-areas。然後,關於 grid-template-columnsgrid-template-rows 這邊有三種特殊設定可以使用,

  • span <命名間隔> 跨過 <命名間隔> 的格線,直到碰觸到下一個格線為止。
  • span <數字 N> 跨過 指定數字 N 個間隔
  • <命名間隔> <負數 N> 指到 <命名間隔> 後,往回算 N 個間隔

請注意 span 的使用,如果指定到的 <命名間隔> 不存在,就會直接接到容器的最後面。如果 <數字 N> 加上開始格線,超過容器的尺寸,容器會直接被擴大。舉例來說,

.grid .r20 {
    grid-column-start: 1;
    grid-column-end: span 30;
    grid-row-start: 2;
    grid-row-end: span 29;
}

這樣你就會獲得 30 x 30 的容器。是的,容器本身並不會硬性規定可容納的區塊,倘若內部區塊設定有不符合的地方,那麼他就會自己擴充到剛好可以放得下的那個尺寸。這一點在規劃、設計格線系統元件時,需要特別留意。

或者你會說,那我就不要設定容器的尺寸就好啦,反正元件要長由他長,要跳位置就跳位置啊。

你一定是甲方派來臥底的對不對!


所以說那個手機版

所以當我們的行動裝置需要複雜規劃時,利用 Grid 來做一些相對複雜的卡片內容排版是合理的。只是說,通常也不會複雜到 Flex 無法應付的狀況。如果有的話,先看一下甲方的誠意再說。