你懂製程嗎?不不不…我懂CSS啦!!寫網頁的人,怎麼可以不懂CSS呢?
什麼?你不懂CSS?下面一位~(X
這次來跟大家分享的是,CSS從古至今的演化,儘管很多近代大家常用的技術,在早期其實很多人都已經提出來了。
但是有價值的技術需要在適當的時機才能發揮它最大的價值。
以下就來帶大家介紹分享一些CSS的知識吧!

石器時期的CSS

CSS reset

在早期的CSS2.0的時期,Eric Meyer釋出了一個CSS Reset解決了不同瀏覽器擁有各自預設CSS樣式的問題。
這是一個很簡短的CSS,但是初始化了大部分開發常用的tags。

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/* http://meyerweb.com/eric/tools/css/reset/ 
v2.0 | 20110126
License: none (public domain)
*/

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}

通常我們會先將CSS reset載入之後,才會開始開發自己的CSS。
是新手練習的最佳夥伴。

Normalize CSS

但是CSS reset載入之後,會衍生出一些問題。
例如:瀏覽器的Bug, 把需要用到的預設都清除了……
所以這個時候,Normalize CSS就是這些問題的solution。
其實有很多UI framework也是使用Normalize CSS在開發
例如:Antd, Bootstrap, ……

關於Bootstrap使用的CSS

我們可以從Bootstrap的主要SCSS中查看他們是使用CSS reset還是Normalize。
最後你會在reboot.scss裡面查看到他們是使用哪一個:

1
2
3
4
5
6
// Reboot
//
// Normalization of HTML elements, manually forked from Normalize.css to remove
// styles targeting irrelevant browsers while applying new styles.
//
// Normalize is licensed MIT. https://github.com/necolas/normalize.css

意思是,Boostrap是從Normalize fork出一份CSS去改寫的。
因此從原始碼可以得知,Bootstrap是使用Normalize CSS。

CSS specificity

在CSS中,CSS specificity是很重要的觀念,如果新手不熟CSS的話,很常會鬼打牆,覺得為什麼改了樣式卻沒有變化。
其實是因為CSS在使用上會有優先權的問題。
通常我們會記以下的順序:

1
element < class < inline-style

如果不好記的話,可以看圖像加深自己的記憶:

補充:

  1. 通常我們不會用ID來寫CSS,ID通常會被當作是anchor,因此在談CSS時,我們時常忽略它。
  2. 我們在寫CSS時,盡量會把CSS寫在CSS檔案裡面,盡量不使用inline-style。
    除非你要覆蓋framework的Normalize CSS或是開發EDM的CSS才比較會用到inline-style開發。
    關於EDM的CSS支援,請參考這邊
    由於EDM有很多支援的限制,所以有些場合需要用比較土炮的方式去開發,以支援每個信件,例如:outlook。
    (之後我會出一篇文章教大家如何開發支援各個信件的網頁)
  3. 這邊你會看到important對吧?這個是最強大的殺手鐧,只要用下去,只有important才可以對抗important。
    所以開發者也盡量不要用這個語法,不然當你要覆蓋相同class的時候,你會很痛苦,例如:手機版網頁。
    這邊也要補充一下,其實Bootstrap的CSS事實上也用了不少important,因此當你改不動樣式的時候,需要用到它。

封建時期的CSS


當大家開發CSS開發到最後一定會發現,CSS每次寫都會一大坨,可能大到自己都懷疑人生了。
沒錯,開發CSS不再只是單純的開發出東西來而已,大家開始漸漸地意識到維護性, 整潔性……
以下的範例是石器時期所開發出來的CSS:

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
.header {
background-size: cover;
background-position: center center;
background-image: url(../asset/img/header_desktop.png);
background-repeat: no-repeat;
}
.header .banner {
min-height: 420px;
}

.header .menu-bg {
min-height: 58px;
background: #ff0000;
position: relative;
}

.header .menu {
top: 0;
}

.header .menu ul a {
display: block;
}
.header .menu ul li{
color: #fff;
}

.header .menu ul li a:hover {
font-weight: bolder;
}

.header .banner-title {
max-width: 460px;
background: #ff0000;
color: #fff;
}

.header .main-menu {
background: #ff0000;
overflow: hidden;
}
.header .main-menu a:hover {
background: #ff0000;
}
.header .hamburger-menu {
display: block;
float: right;
}
.header .menu-show {
max-height: 165px;
}
.header .block-md {
display: block;
}

SASS/SCSS

這還只是一個header的CSS而已,如果是其他結構複雜的component,相信會更長的。
因此,SASS解決了CSS開發上的一些問題。
可以把相關的selector寫在一起,讓管理上更聚焦:

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
.header {
background-size: cover;
background-position: center center;
background-image: url(../asset/img/header_desktop.png);
background-repeat: no-repeat;

.banner {
min-height: 420px;
}

.menu-bg {
min-height: 58px;
background: #ff0000;
position: relative;
}

.menu {
top: 0;

ul a {
display: block;
}

ul li {
color: #fff;
}

ul li a:hover {
font-weight: bolder;
}
}

.banner-title {
max-width: 460px;
background: #ff0000;
color: #fff;
}

.main-menu {
background: #ff0000;
overflow: hidden;

a:hover {
background: #ff0000;
}
}

.hamburger-menu {
display: block;
float: right;
}

.menu-show {
max-height: 165px;
}

.block-md {
display: block;
}
}

但是這個時期的人對很多開發體驗還有很多不滿,程式設計都有越來越多的設計樣式出來了,那麼CSS呢?
我們在設計CSS的時候,可能會遇到很多結構或是元件有重複的屬性,但是這些屬性有其共同的特徵,並且可以重複使用。
如果我們把這樣的概念帶到CSS上,不是可以大大的增加日後的維護性嗎?
這邊舉經典的OOCSS為例:

Object Oriented CSS

當我們把OO的概念帶到CSS上,基本上可以達到:

  1. 結構分離
  2. 樣式分離

其實OO用在CSS上是更容易詮釋OO的概念。
以下來看常見的例子:

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
.button {
width: 200px;
height: 50px;
padding: 10px;
border: solid 1px #ccc;
background: linear-gradient(#ccc, #222);
box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px;
}

.box {
width: 400px;
overflow: hidden;
border: solid 1px #ccc;
background: linear-gradient(#ccc, #222);
box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px;
}

.widget {
width: 500px;
min-height: 200px;
overflow: auto;
border: solid 1px #ccc;
background: linear-gradient(#ccc, #222);
box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px;
}

我們在設計程式的時候,可能會有重複的邏輯,但是他們共通點就是具有相同的外觀。
因此我們可以把外觀給包裝起來共用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.button {
width: 200px;
height: 50px;
}

.box {
width: 400px;
overflow: hidden;
}

.widget {
width: 500px;
min-height: 200px;
overflow: auto;
}

.skin {
border: solid 1px #ccc;
background: linear-gradient(#ccc, #222);
box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px;
}

感覺是不是清爽很多?不僅可以提升reuse,還能達到clean code的效果。

此外,補充一些使用OOCSS要注意的事情:

  1. 盡量少用後代選擇器(descendent selector)
  2. 不要使用ID選擇器
  3. 非必要,不要使用!important
  4. 可嘗試使用CSS grids

以下補充一些實用的資源:

近代時期的CSS


封建時期的CSS,會開始有很多工具和規範來讓整體的效益更完善,理論上看起來是已經有系統性的可以讓開發體驗更好了。
但是事實並非如此簡單,人們總是追求進步,無法滿足自己的。
網頁設計隨後經歷了各種套件與框架的出現,在現在大家習慣使用框架把HTML寫在JS的時代中,CSS的管理方式也跟著開始有所變化。
CSS的寫法在近代也分成了兩個派別:

  1. HTML與CSS分離
    • HTML與CSS的code分離,比較好管理與分離檔案。
  2. CSS寫在HTML上
    • 比較好管理每個元件的CSS,不會受到global的影響。

其實這兩者都有自己的特點,只是要看專案適合哪一種,大家的共識是什麼。
不管是上面哪一個,基本上只要注意一點,一定要把CSS的邏輯清楚的表達與分離出來,以下介紹。

CSS Modules

有經驗的同學在開發CSS,尤其是跟別人共同開發專案時,一定會遇到你設計的網頁莫名的跑板了。
奇怪,明明就沒有動過呀?怎麼會突然就位移了呢?
這個時候,請你把位移的那個class name拿去search,你會發現你的同伴跟你用了同樣的class selector
而且…還跟你用不一樣的樣式了呢……
這個時候,千萬不要去怪你的夥伴。
因為相信我,如果換作是你,你一定也會犯這種錯。
錯就錯在,CSS在import的時候,是global的!!
以下是範例:

1
2
3
4
5
6
7
8
9
10
11
import React from 'react';
import './MyApp.scss';

const MyApp = () => {
return (
<h1 className="title">
Hello World
</h1>
);
};
export default MyApp;

那該怎麼解決呢?其實很簡單!你只需要使用CSS Modules,而且方法很簡單!
只要包裝成一個模組,然後再分別到每個class去使用就行了:

1
2
3
4
5
6
7
8
9
10
11
import React from 'react';
import styles from './MyApp.scss';

const MyApp = () => {
return (
<h1 className={styles.title}>
Hello World
</h1>
);
};
export default MyApp;

你一定會問,那這樣跟寫class有什麼兩樣?
厲害的地方就是在這邊啦!
請打開你的瀏覽器開發工具查看,你會發現class的名字不太一樣:

1
2
3
<h1 class="title_yATCOkg">
Hello World
</h1>

CSS也會是長這樣:

1
2
3
.title_yATCOkg {
...
}

後面會自動夾帶一串hash,如此一來,只有這個頁面的hash會是一樣的,就不會影響到其他頁面的相同class了!
然後你一定會問一件事情,可是如果想要覆蓋掉global的selector不就沒辦法了嗎?
例如使用Bootstrap, Antd… 想要把樣式覆蓋掉該怎麼做?
答案是可以的!CSS Modules提供global的語法給大家使用:

1
2
3
4
5
6
7
.title {
color: white;
}

:global(.title) {
color: red;
}

或是你要覆蓋掉framework的Normalize:

1
2
3
4
5
:global {
.ant-form {
color: black;
}
}

CSS in JS

不過有另一派的人有不同的觀點,覺得每次都要管理範疇這麼大的CSS覺得心很累,為什麼不把每個元件的CSS都獨立呢?
因此CSS in JS使用的人數也不斷的增加了,流行框架可查看這裡
這邊介紹最具有代表性的 Styled Components。
你不需要再針對elements去設計,撰寫方式很像在做component一樣,把CSS都視為一組一組的概念。

  1. 使用方式需要安裝

    1
    npm install styled-components
  2. 使用方式

    1
    2
    3
    4
    5
    6
    7
    import styled from 'styled-components';
    const MyContaner = styled.div`
    width: 100%;
    height: 100%;
    background: grey;
    `
    return <MyContaner>Hello World</MyContaner>;

這樣的好處當然是,讓你的code看起來更清爽,僅管CSS是寫在component裡面也不會影響到整潔與維護性。
而且還有提供props傳遞Attribute進去,以下是官方的範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const Button = styled.button`
/* Adapt the colours based on primary prop */
background: ${props => props.primary ? 'palevioletred' :
'white'};
color: ${props => props.primary ? 'white' :
'palevioletred'};

font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
render(
<div>
<Button>Normal</Button>
<Button primary>Primary</Button>
</div>
);

結論


統整以上的幾個時期,大概是:

  1. 石器時期
    • 大家開始已知用火,尋找與提出solution。
  2. 封建時期
    • 開始學習制定規範,優化結構,不再只是單純的使用他們。
  3. 近代時期
    • 大家知道使用工具與方法是沒有絕對的好壞與適合,只有適不適合把這幾套用在專案上。

你喜歡用什麼開發模式或是套件呢?歡迎跟我一起分享哦!

meme分享

因為最近看了這部影片,所以才想到要下這個標題:

你問我壓力是不是很大?沒錯,最近壓力真的蠻大的,而且寫這篇文章寫到快脫窗啦~