第一次嘗試 Responsive Design 和 Grid System

公司的活動頁,明明趕得要死,我還是忍不住把 Susy 用上了,為什麼?

因為前陣子在規劃新版面,我認為像樣的 grid system 能幫助我們,減少設計師、上稿人員和我,煩惱或溝通一些不必要的問題,例如某元素 margin 多少、某版位我做了多寬、……,不如先定個標準出來,要改再說,不改就我處理。

RWD 也是早就想做的事情,目前網站拆為桌面版和手機版(m.網址),結果因為 m 的功能不完整,造成 tablet 使用者處在不上不下的局面,想想 RWD 還是最輕鬆的解法。

找了不少解決方案,感覺都很難搞,最後只有 Susy 給我嘗試的勇氣。

Susy 可愛之處

  • 不用自己算寬度。 比如 960px 分成 16 欄,每欄是多寬? 我不可能算對啊!
  • 不用在 HTML 加入像 .row4 .span3 的噁心 class,那根本無法維護嘛。

Susy 實際做的事情

剛開始什麼 gutter、columns 的名詞都看不懂,但實際做一次,認識一下產生的 CSS,會發現 Susy 做的事其實不多。

※目前版本是 1.0.8,但以下會混用 2.0 alpha 未定的介面說明,我覺得更清楚。

  1. 建立 grid(set-grid(960px 12 1/4)

    主要是維護 grid 設定的工作,也就是定義「容器寬、欄數、欄間隔、留邊」等等變數; 一般會寫下幾組設定,用在不同的 media query 條件下。

    具體例子,v1.0.8 的語法

    $container-width: 970px;
    @include container;

    產生的 CSS 會包含 max-width: 970px; 以及 :after :before 的 clearfix。

  2. 安排元素的佔有欄數與定位(span(last 3 of 5 isolate)

    Susy 做掉兩個麻煩的算數問題,第一是元素本身的 width; 只要指定橫跨(span)寬度是 N 欄,Susy 就會根據當時的 grid 設定,把元素 width 設為應有的百分比

    第二是為了將元素放在正確的水平位置上,所需要設定的左右 margin (理由:Susy 採用 float 加上負 margin 實作 grid,避免 % 除不盡時,瀏覽器處理最後一個 pixel 的問題,參考 Responsive Design’s Dirty Little Secret)。

    v1.0.8 使用 @include span-columns(3 omega, 5); 指定元素佔 5 欄中的 3 欄; omega 表示是該列的最後一個元素,預設會產生反方向的 float,倒過來算,否則那幾 % 有時還是會被擠到第二列。

    另有 isolate(3) 用來將元素定位在第 3 欄; 針對列表也能以 isolate-grid(4) 讓各 item 寬 4 欄、自動對齊,且 nth-child(4n+1) 會自動 clear,產生 CSS 如:

    #AllWorks ul.gallery li {
      width: 23.07692%;
      float: left;
      margin-right: 2.5641%;
      margin-right: -100%;
    }
    #AllWorks ul.gallery li:nth-child(4n + 1) {
      margin-left: 0%;
      clear: left;
    }
    #AllWorks ul.gallery li:nth-child(4n + 2) {
      margin-left: 25.64103%;
    } ... (後略)

  3. Media query

    提供方便設定 media query 的 Mixin。 v1.0.8 用法

    $media-tablet:  640px  8 969px;
    @include at-breakpoint($media-tablet) {  // ... (block 後略)

    會產生 @media (min-width: 640px) and (max-width: 969px) { ... 的 CSS,且其中寬度會以 8 欄為標準計算。


遇到的問題

  1. 容器已經有 max-width,但某個 header 想要背景佔滿畫面寬

    要讓 content 置中,header、footer 又要橫向佔滿整個畫面,自然的作法就是將 header、content、footer 放在同一層,各自設定寬度(分別為 100%、container width、100%)。

    如果想讓 header 的「內容」也遵守 grid 設定,讓它跟 content 對齊,就要在寬 100% 的 header 下,再加一層 wrapper 且設為 grid container 才行。

    若 container 寬未佔滿全畫面,如何實現全畫面寬的 header?

    結論:這個問題跟 Susy 沒有關係。終究無法只用一個 container 就達成需求。

    註:有個用 pseudo element 的做法,參考 css - How to handle full-width element outside of a 960 compass susy grid? - Stack Overflow

  2. 預設的垂直對齊,只能靠頂(top)對齊

    因為 CSS vertical-align 只對 layout 為 inline 或 table-cell 的元素有用,而 float 會把 layout 變成 block,正好 Susy 是使用 float 排版,結果就是「垂直對齊」變得不易做到。

    預設的 grid 定位,總是垂直置頂對齊。

    可能的解法是改用 display: inline-block;,就能使用 vertical-align 了,也是我們平常實作 gallery 的方式。 (Susy 有一個相關 feature request inline-block version · Issue #69,不過被拒絕掉了)

    這次我也沒用 vertical-align,設計師可能也比較中意 vertical rhythm…… 但總之想換 inline-block 的話,要注意的應該只有:

    1. Haml tag 要用 > 和 < 把多餘的空白去掉,因為空白會佔寬度:
      %li><
        %div.image
    2. 不使用 Susy isolate,改為自己指定 width 和 margin:
      li {
        width: columns(4, 16);
        margin-right: gutter(16);
        &:nth-child(4n) {
          margin-right: 0;
        }
      }

    項目改用 inline-block 就能 vertical-align 了。