Essentail Rails Design Pattern

Write Good Rails Code

Sass/SCSS 與 Compass

自 Rails 3.1 起提供的 Asset Pipeline,是一套讓開發者很方便能夠削減 (minify) 以及 壓縮 (compress) Javascript / CSS 檔案的框架。它同時也提供你直接利用其他語言如 CoffeeScript / SASS / ERB ,直接撰寫這些 assets (stylesheets / javascripts / images ) 的可能性。

它的面世,推動了新的 Asset 整理概念,也向世界的開發者,介紹了 SCSS / Compass,引爆在 CSS 開發上無限的可能。

使用 Sass / SCSS 撰寫 CSS

Asset Pipeline 提供了內建直接使用 Sass 撰寫 CSS 的功能。

Sass 是 Haml 這個 project 中的一部分 feature。 Haml 能讓開發者能夠使用縮排的方式撰寫 HTML,輕鬆做到永不忘記關 Tag 的效果。

Haml

1
%h1= "Hello World"

產生出來的 HTML 就會長這樣

1
 <h1>Hello World</h1>

Haml 是需要使用縮排撰寫,再行 compile 的 markup language。它可以讓你:

  • 阻絕撰寫出錯誤結構的 HTML TAG 的機會

只要 syntax 一錯誤,編譯就無法成功。利用這樣的特性,很容易阻絕寫出會在瀏覽器因為 TAG 結構錯誤而難以 debug 出的 HTML。

在網頁設計開發階段,若要大幅調整 HTML 結構,對網頁設計師也是很傷腦筋的一件事。因為大幅的搬動 HTML,通常有時候會造成:「少剪到一個 TAG」或 「改了開頭 TAG ,卻忘了改關閉 TAG 」的憾事。

在 Haml 中,這些狀況都不會發生。因為 Haml 本身就屬於「塊狀結構」、「自我 close」。因此不論怎樣搬動和調整,只要符合縮排,幾乎都不會爆炸。

  • 輕鬆調整排版

然而 Haml 並沒有如預期中風行?反倒是原先屬於副功能的 Sass 大紅特紅。原因就在於 Haml 的特性:不只需要被機器 compile,它也需要被人腦 compile。HTML 本身就是一門相當直觀的 markup language。在撰寫 Haml 時,排版雖然相當輕鬆。但接手維護 Haml 版面的人,卻通常痛苦不堪。因為「非常不直觀」。

Sass / SCSS

回頭談 Sass,Sass 原先是附屬在 Haml 裡面的一個副功能。這也是 sass-convert 這個指令必須要安裝 haml 這個 gem 才能使用的原因。Sass 的原理,是讓開發者可以透過「縮排」的方式去撰寫維護 CSS,同樣可以避免忘記關 TAG 而大爆炸的糗事。而因為 CSS 的結構特性,造成了 Sass 與 Haml 截然不同的命運。多數人反對 Haml,是因為 Haml 反而造成了 HTML 在閱讀上的不直觀。

而 Sass 的語法

1
2
3
4
  .content
    margin: 2em 0
    h1
      font-size: 2em

1
2
3
4
5
6
   .content{
    margin: 2em 0;
   }
   .content h1{
      font-size: 2em;
   }

反倒讓 CSS 的維護變得直觀了。

接觸 Sass / SCSS 後的不少開發者甚至認為:縮排 block 的撰寫方式才是 CSS 在被發明時應該被設計出來的樣子。現在撰寫 CSS 的方式,有一個絕大缺點:只要在結構上涉及巢狀或多個 class,維護者就必須複製貼上 style。不少人認為這真是愚蠢至極且煩人透頂的設計。

內建 Killer features 讓維護 CSS 變得更簡單

Sass 也提供了其他便利功能,如變數、函數、數學、繼承、mixin …等等功能。

在進行網頁 protyping 時,更改全站配色或者是直接提供兩個以上的設計,對設計師來說是家常便飯的事。

但更改全站配色卻是相當麻煩的一件事,因為「尋找 + 全數取代」,並不能保證最後會有正確的結果。很有可能:你更改了所有 CSS 中涉及連結的顏色,卻發現在全數取代的過程中,不小心也改到邊框的顏色。

若能使用變數去指定特定 style 的顏色,該有多好。

  • 變數 ( Variables )
Sass
1
2
3
4
5
6
   $border-color: #3bbfce
   $link-color: #3bbfcf
   .content
      border-color: $border-color
      a
        color: $link-color
CSS
1
2
   .content{ border-color: #3bbfce; }
   .content a{color: #3bbfcf; }
  • 數學

在調整區塊寬度時,你也希望:每次調整寬度時,可不可以不要每次都按計算機,再全數手動修改…

Sass
1
2
   .content
      width: (500px/2);
CSS
1
   .content{ width: 250px; }
  • 內建函式

在調整顏色亮度時,你希望可否無需再開調色盤,直接改 CSS 就讓 style 暗一點?

1
2
3
   $color = darken(#800, 20%)
   .content
      background-color: $color
1
   .content{ background-color: #200; }

這都還只是 Sass 所提供的當中一小部分基礎功能而已,卻足以讓網頁設計師驚艷十足了。加上撰寫維護時十分直觀,這也難怪為何 Sass 只是 Haml 中的副功能,後繼的聲勢浪頭卻遠高於 Haml 本身。

SCSS

那 SCSS 又與 Sass 有什麼差別,他們看起來好像是類似的東西?

是這樣的,Sass 原先使用的縮排,對於網頁設計師來說,還是相當不直觀。而且實務上也不能直接將舊有的 CSS 直接貼進 Sass 中。其實還是存在一定的不方便度。也因此 Sass 進行了進化,改良了 syntax,而 Sass 3 後來就被稱為 SCSS ( Sassy CSS)。

它的 syntax 與 CSS 原有的 syntax 完全 compatible,使用了 { } 去取代原先的縮排方式。

比如說原有的

Sass
1
2
3
4
  .content
    margin: 2em 0
    h1
      font-size: 2em

在 SCSS 中變成了

SCSS
1
2
3
4
  .content{
    margin: 2em 0;
    h1 {font-size: 2em }
  }

在撰寫上,更加無比的直觀,同時也能將舊有的 CSS 直接貼進去,完全沒問題!SCSS 更新增了不少關於 CSS3 的 feature 函式。

就拿我最愛的背景漸變色來說好了,原先要做漸變色,CSS 必須要這樣寫:

CSS
1
2
3
4
5
6
7
8
 #linear-gradient {
   background-image: -webkit-gradient(linear, 0% 0%, 100% 100%, color-stop(0%, #ffffff), color-stop(100%, #dddddd));
   background-image: -webkit-linear-gradient(left top, #ffffff, #dddddd);
   background-image: -moz-linear-gradient(left top, #ffffff, #dddddd);
   background-image: -o-linear-gradient(left top, #ffffff, #dddddd);
   background-image: -ms-linear-gradient(left top, #ffffff, #dddddd);
   background-image: linear-gradient(left top, #ffffff, #dddddd);
 }

因為你必須支援所有的 Browser。

但在 SCSS 中,一行就搞定了!

SCSS
1
 #linear-gradient { @include background-image(linear-gradient(left top, white, #dddddd)); }

什麼是 Compass

Sass : extend / mixin / import

在 Sass 一章,我們只提到了一些基礎的部分。更強大的其實是這些功能

  • extend
  • mixin
  • include

extend

extend 可以讓你將某些已經寫好的樣式,直接套入另外一組樣式中重複使用。

CSS 的原始內容:

CSS
1
2
3
4
5
6
7
8
9
   .warning{
      border: 1px;
      color: red;
   }
   .serious-warning{
       border: 1px;
       color: red;
       font-weight: bold;
   }

而使用 extend,只需這樣寫,就可生成上述 CSS 了:

SCSS
1
2
3
4
5
6
7
8
   .warning{
      border: 1px;
      color: red;
   }
   .serious-warning{
       @extend .warning;
       font-weight: bold;
   }

mixin

可以預先寫好自定要被 mixin 的 function ,需要用時 include 進來

SCSS
1
2
3
4
5
6
7
 @mixin rounded-top {
   $side: top;
   $radius: 10px;
   border-#{$side}-radius: $radius;
   -moz-border-radius-#{$side}: $radius;
   -webkit-border-#{$side}-radius: $radius;
 }

要用時 include 進來

SCSS
1
2
   #navbar li { @include rounded-top; }
   #footer { @include rounded-top; }

import

可以把太長的 SCSS 拆開成一個一個的 partial,要用時 import 進來。

style.scss
1
2
   //需要被 import 的檔案必須以 _開頭命名,如 _rounded.scss
   @import "rounded";

有了 extend / mixin / import,加上 variable,加上支援部分 Ruby syntax。

聰明的你,應該想到這些元素集合起來可以作什麼?

沒錯,就是造 CSS Framework!

Compass = Sass framework powered by community

Sass 提供了一堆基本武器可以讓許多開發者造出自己的最佳實踐,再讓其他人可以重複利用。而 Compass 就是這些實踐產物的集合體。

例如 Compass 實際就提供以下這些熱門 CSS framework 的 mixin:

開發者也能夠利用 Sass + Compass 自己造出很多超棒的小工具,如: Fancy Buttons

而 Compass 自己的 core 更是充滿 killer features,比如說:

撰寫漸層色與圓角框不再是令人頭大的難事。

漸層色 Background Gradients : http://compass-style.org/examples/compass/css3/gradient/

SCSS
1
2
#linear-gradient {
  @include background-image(linear-gradient(left top, white, #dddddd)); }
CSS
1
2
3
4
5
6
7
8
#linear-gradient {
  background-image: -webkit-gradient(linear, 0% 0%, 100% 100%, color-stop(0%, #ffffff), color-stop(100%, #dddddd));
  background-image: -webkit-linear-gradient(left top, #ffffff, #dddddd);
  background-image: -moz-linear-gradient(left top, #ffffff, #dddddd);
  background-image: -o-linear-gradient(left top, #ffffff, #dddddd);
  background-image: -ms-linear-gradient(left top, #ffffff, #dddddd);
  background-image: linear-gradient(left top, #ffffff, #dddddd);
}

圓角框 Border radius: http://compass-style.org/examples/compass/css3/border_radius/

SCSS
1
2
#border-radius {
  @include border-radius(25px); }
CSS
1
2
3
4
5
6
7
8
#border-radius {
  -moz-border-radius: 25px;
  -webkit-border-radius: 25px;
  -o-border-radius: 25px;
  -ms-border-radius: 25px;
  -khtml-border-radius: 25px;
  border-radius: 25px;
}

擔心使用 CSS3 ,IE 無法支援?Compass 內建支援 CSS3 Pie

討厭在 HTML 上寫 CSS grid system require 的 class name 嗎?配合 Blueprint 做 mixin,輕鬆造出多欄版面!

SCSS
1
2
3
4
5
6
7
8
9
10
11
12
$blueprint_grid_columns: 8;
$blueprint_grid_width: 40px;
@import "blueprint";
.two-col {
  @include container;
  background-color: #cccccc;
  #header, #footer {
    @include column(8); }
  #sidebar {
    @include column(3); }
  #content {
    @include column(5, true); } }
HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<div class='two-col'>
  <div id='header'>
    <h1>This is the Header</h1>
  </div>
  <div id='sidebar'>
    <ul>
      <li>
        <a href='#'>Nav #1</a>
      </li>
      <li>
        <a href='#'>Nav #2</a>
      </li>
      <li>
        <a href='#'>Nav #3</a>
      </li>
    </ul>
  </div>
  <div id='content'>
    <p>
      Lorem ipsum dolor sit amet,
      consectetur adipisicing elit, sed do
      eiusmod tempor incididunt ut labore et
      dolore magna aliqua.
    </p>
  </div>
  <div id='footer'>
    This is the footer.
  </div>
</div>
  • 不費吹灰之力實作 CSS Sprites

知道什麼是 CSS Sprites 技巧嗎?

CSS Sprites 是 Minimize HTTP Requests 中的一招,實作方式是將 CSS 中的小圖合併成較大的一張,再透過 CSS 定位的方式呼叫。比如說這個 CSS 中原先需要用到十張小圖,合併到只剩一張,就變得非常節省 HTTP requests( 因為光是發出 requests 也相當耗時)。

問題是,負責 Scaling 的 server guy 很喜歡這招,但網頁設計師卻對這件事相當感冒。因為太麻煩了!

試想,url(‘xxx.gif’) 到處貼多方便呀,誰喜歡合併成一張,然後仔細精算定位?後續維護又怎麼辦呢?

Well,Compass 幫你「自動解決了問題」!http://compass-style.org/help/tutorials/spriting/

你可以把原先所有要用的小圖,通通丟進同一個資料夾。讓 Compass 幫你自動合併並算出位置!

SCSS
1
2
@import "icon/*.png";
@include all-icon-sprites;

Compass 會幫你自動合併並算出位置:

CSS
1
2
3
4
5
6
7
8
9
.icon-sprite,
.icon-delete,
.icon-edit,
.icon-new,
.icon-save   { background: url('/images/icon-s34fe0604ab.png') no-repeat; }
.icon-delete { background-position: 0 0; }
.icon-edit   { background-position: 0 -32px; }
.icon-new    { background-position: 0 -64px; }
.icon-save   { background-position: 0 -96px; }

毛骨悚然的強大呀!

如何整理 SCSS

在實務演變中,Sass 的通用規則大致上是朝往這幾個方向拆:

  • by feature
  • by special block
  • by default block ( header / footer / sidebar )
  • by (Rails) controller
  • by page specific

Examples

#TODO

Comments