Blog Cover Image

Inspire you to have New thinking, Walk out your unique Road.

有的時候,你無意間遇到的一些故事,會激發你的靈感,改變你的想法,接下來你會用與之前全然不同的觀念去創造屬於你獨特的故事。

Sign @MinaYu.

[前端小菜鳥] 前端工程師成長計畫 - 讀書篇(3) | CSS(2) css樣式優先度計算

Posted on

第三篇讀書計畫的主題是CSS。

正式進入 CSS 後,我決定花費大約一個半月的時間去刷這個網站的內容,所以接下來的文章都會是以這篇網站的內容學習為主。

如果按照我的讀書計畫排列來讀的話,估計CSS的篇幅可能會出個至少7-8篇吧,除了網站內容外,後面還有關於面試題目跟一些影片要看。

若對CSS沒有太大興趣的話,可以直上替代方案: CSS Cheatsheet + 標準語法糖 - w3schools

但本人的讀書計畫可能近期兩個月都會是更新跟CSS相關,希望年底能真的把整套讀書計畫run一次XD。


CSS 讀書項目

  • HTML/CSS

詳請可參考完整讀書計畫目錄


Table of Content 內容


CSS


The Cascade

The cascade 是用來解決當許多css規則造成矛盾跟衝突時,哪些css樣式跟規則會優先被史用。

  • Position and order of appearance: the order of which your CSS rules appear (你擺放css的位置)
  • Specificity: an algorithm which determines which CSS selector has the strongest match (不同的selector優先度不同,這個在下個章節有更詳細的分數計算)
  • Origin: the order of when CSS appears and where it comes from, whether that is a browser style, CSS from a browser extension, or your authored CSS (是瀏覽器本身、使用者套用、CSS Framework的優先度)
  • Importance: some CSS rules are weighted more heavily than others, especially with the !important rule type (!important就是非常重要,通常優先度很高、但是transition的優先度會比!important高)

Position and order of appearance

後者宣告的永遠贏,就算是 link sheet (<link rel="stylesheet" href="./test.css" />) 和 <style> tag,混在一起時,也是自然而然最後宣告的css樣式優先使用。

但然而,如果最後宣告的樣式該瀏覽器不支援,那就會渲染倒數第二個宣告的樣式,以此類推。


Specificity

<h1 class="my-element">Heading</h1>
.my-element {
  color: red;
}

h1 {
  color: blue;
}

以上的舉例來說, class 優先度高於 html tag

然後,id的優先度會高於所有其他元素,所以建議不要直接用id撰寫css樣式,如果有其他需要override的需求的話,會比較難維護。

a.my-class.another-class[href]:hover,雖然可以用這種方式對css命名指定(selector),但會造成維運困難或者若需要樣式可重複利用的話,建議讓selector越簡單越好。

這個部分的分數計算在下個章節會有更詳細的解說。


Origin

The order of specificity of these origins, from least specific, to most specific is as follows:

因為會有來自不同地方import的css樣式,包括瀏覽器原生有的,其優先度如下:

低 -> 高

  • User agent base styles. These are the styles that your browser applies to HTML elements by default.
  • Local user styles. These can come from the operating system level, such as a base font size, or a preference of reduced motion. They can also come from browser extensions, such as a browser extension that allows a user to write their own custom CSS for a webpage.
  • Authored CSS. The CSS that you author.
  • Authored !important. Any !important that you add to your authored declarations.
  • Local user styles !important. Any !important that come from the operating system level, or browser extension level CSS.
  • User agent !important. Any !important that are defined in the default CSS, provided by the browser.

!important -> 不是不重要,反而是最重要,優先度最高。


Importance

Not all CSS rules are calculated the same as each other, or given the same specificity as each other.

The order of importance, from least important, to most important is as follows:

  • normal rule type, such as font-size, background or color
  • animation rule type
  • !important rule type (following the same order as origin)
  • transition rule type

animation 跟 transition的優先度會比較高,主要是因為當有這兩個樣式時,他們主要是改變視覺狀態的因子。


Note: 最後就是善用開發者工具去觀察css樣式的優先層級。

腦子是不用這麼可憐塞這些計算,開發者工具是你的好朋友。


Specificity

這個章節主要是針對上一個章節 The Cascade 的 Specificity去做補充。

Specificity對於每個selector都有一個分數制,哪一個selector擁有高分,他的優先度最高被採用作為渲染css樣式,打個比方:

<button class="branding">Hello, Specificity!</button>
button {
  color: red;
}

.branding {
  color: blue;
}

按鈕會是藍色的,因為就 HTML tagclass selector,class的分數會高一些。


Specificity scoring

以下列舉不同的selector以及他們的分數

  • Universal selector (*): no specificity and gets 0 points
  • Element or pseudo-element selector (div, ::selection): 1 point of specificity.
  • Class, pseudo-class, or attribute selector (.my-class, :hover, [href=’#’]): 10 points of specificity.
  • ID selector (#id): 100分
  • Inline style attribute (<div style="color: red"></div>): 1000分
  • !important rule: 10000分

Note: pseudo-class 例外,當使用 :not() 等同使用pseudo-class,所以下面這個分數是11分,div 1分 + pseudo-class 10分

div:not(.my-class) {
  color: red;
}

Note: !important

.my-class {
  color: red !important; /* 10,000 points */
  background: white; /* 10 points */
}

簡單來說可以累加

<a class="my-class another-class" href="#">A link</a>
/* 1分 */
a {
  color: red;
}

/* 11分 */
a.my-class {
  color: green;
}

/* 21分 */
a.my-class.another-class {
  color: rebeccapurple;
}

/* 31分 */
a.my-class.another-class[href] {
  color: goldenrod;
}

/* 41分 */
a.my-class.another-class[href]:hover {
  color: lightgrey;
}

Visualizing specificity

這個部分參考了chatGPT的回答才會計算,簡單來說上半部已經介紹了如何計算分數,這個章節比較像是當所有的selector都拼在一起時,你可以怎麼用各種進位制計算。

chatGPT的回答是 specificity分數由四個部分組成數字(文章是3個)

  1. Inline styles (1000分)
  2. ID (100分)
  3. Class, pseudo-class, or attribute selector (.my-class, :hover, [href=’#’]) (10分)
  4. Element or pseudo-element selector (div, ::selection) (0分)

然後以上的數字用進位制去計算

Inline styles - ID - Class - Element


/* Example 1 */
div {
  color: blue;
}
  1. Inline styles: 沒有
  2. ID: 沒有
  3. Class, pseudo-class, or attribute selector (.my-class, :hover, [href=’#’]): 沒有
  4. Element or pseudo-element selector (div, ::selection): 1個

所以分數是 0-0-0-1,就是1分。

/* Example 2 */
ul li a:hover {
  color: purple;
}
  1. Inline styles: 沒有
  2. ID: 沒有
  3. Class, pseudo-class, or attribute selector (.my-class, :hover, [href=’#’]): 1個 (hover)
  4. Element or pseudo-element selector (div, ::selection): 3個 (ul, li, a)

分數是 0-0-1-3,就是13分


來看看網站內的範例

a.my-class.another-class[href]:hover {
  color: lightgrey;
}
  1. Inline styles: 沒有
  2. ID: 沒有
  3. Class, pseudo-class, or attribute selector (.my-class, :hover, [href=’#’]): 4個 (.my-class, .another-class[href], :hover)
  4. Element or pseudo-element selector (div, ::selection): 1個 (a)

所以分數是 0-0-4-1 (網站寫 0-4-1,因為他沒有把Inline styles算進去), 41分


另一個網站範例

Which of the following selectors is 1-2-1?

#specialty:hover li.dark
  1. Inline styles: 沒有
  2. ID: 1個 (#)
  3. Class, pseudo-class, or attribute selector (.my-class, :hover, [href=’#’]): 2個 (:hover, .dark)
  4. Element or pseudo-element selector (div, ::selection): 1個 (li)

所以答案是 0-1-2-1 (1-2-1), 121分


Pragmatically increasing specificity

.my-button {
  background: blue;
}

button[onclick] {
  background: grey;
}
<button class="my-button" onclick="alert('hello')">Click me</button>

接著以上的答案來計算的話

  • .my-button: 10分
  • button[onclick]: 11分,因為 button是1分、[onclick]算attribute selector所以10分

.my-button {
  background: blue;
}

[onclick] {
  background: grey;
}

這樣的話兩週都是10分,可是 grey贏,因為他是在最後被宣告。

所以如果想要按鈕是 blue的話,倒過來寫就可以了,分數相同的,以最後宣告的為主。

[onclick] {
  background: grey;
}

.my-button {
  background: blue;
}

這樣按鈕就是 blue。


Inheritance

後來發現這個章節跟前面相關,所以就將 Inheritance 更新在此篇文章。

先來引用這個章節的第一個例子,但我做了些改變

.my-button {
  display: inline-block;
  padding: 1rem 2rem;
  text-decoration: none;
  background: pink;
  font: inherit;
  text-align: center;
  color: blue
}

article a {
  color: maroon;
}
<a href="http://example.com" class="my-button">I am a button link</a>

你認為 a link 的字的顏色是什麼顏色?

如果結合上面兩個章節來看,會是 blue。因為在前兩個章節提到, class的selector分數比html attribute (article, a)高,所以在優先度來看,會優先選擇 class=".my-button"的樣式,再選擇 article a,如果他內部有其他樣式是.my-button沒有定義的。


How to explicitly inherit and control inheritance

接著本章重點介紹這三個相關的關鍵字怎麼使用

  • The inherit keyword
  • The initial keyword
  • The unset keyword

Inherit

inherit 關鍵字就是字面上的意思「繼承」,使用後他會繼承父元素所設定的值。

.parent {
  color: red;
}
.child {
  color: inherit; /* child 會繼承 parent 設定的值,也就是 color: red */
}

但這例子很抽象,再舉一個

strong {
  font-weight: 900;
}

.my-component {
  font-weight: 500;
}

.my-component strong {
  font-weight: inherit;
}
<div class="my-component">
  <strong>test
  </strong>
</div>

今天想要特別在class="my-component"底下的strong他的font-weight繼承class my-component (即500),我就用上面列舉的寫法

.my-component strong {
  font-weight: inherit;
}

這樣,所有在 div class="my-component"底下的strong,他們的font-weight就都會是500,繼承class my-component所設定。


Initial

initial這個關鍵字,則是賦予你有重置(reset)的功能,然後再將值繼承最default的值(通常是瀏覽器原生的值)。

aside strong {
  font-weight: initial;
}

比方說,他把原先 strong的設定透過 initial 重設, 那原先strong會是粗體字或者設為 font-weight: 900;,重置後,字會變成瀏覽器原先設計的粗細 (也許是細字font-weight: 200;)。


Unset

最後則是 unset 關鍵字,這個關鍵字有兩種用法,「他保留 擁有繼承屬性的值、但重設 沒有繼承屬性的值」。

舉下面這個例:

.foo {
  color: blue;
}
.bar {
  color: green;
}
p {
  color: red;
}
.bar p {
  color: unset;
}

在這樣的情況下 .bar p 的字會是綠色,因為 <div class=".bar"> 是父元素,底下的 <p> 繼承父元素的顏色,所以是綠色。


div {
  border: 1px solid green;
}
p {
  border: 1px solid red;
}
.bar p {
  border-color: unset;
}

在這個例子中,<p>設置的邊框樣式是 1px solid red;,邊框顏色是紅色。但是在 .bar p 樣式時,unsetborder-color 這個值,我們可以看到上面的selector都沒有賦予 border-color這個屬性,所以在這 .bar p 邊框顏色其實會被重設為跟瀏覽器一樣 (通常是黑色)。


本節小課程 - CSS 大小單位

最後,我自己塞一個小小的課程,就是有關CSS的單位大小。

參考網站

  • px:絕對單位,代表螢幕中每個「點」( pixel )。
  • em:相對單位,每個子元素透過「倍數」乘以父元素的 px 值。
  • rem:相對單位,每個元素透過「倍數」乘以根元素的 px 值。
  • %:相對單位,每個子元素透過「百分比」乘以父元素的 px 值。

簡單來說,網站CSS單位有這些,特別是早期的網站都是運用 px 值去撰寫。

而最近比較熱門的類似 TailwindCSS 這種CSS 框架比較常用 rem 作為計算單位,應該是由於現在的網站都很要求響應式(RWD),再加上rem是基於跟元素的px值去以倍數乘除,所以不需要像px單位,每個元素都要特別定義,rem的單位可以做到一致性的比例控制,縮放整個頁面的大小,而不用調整每個元素。