CSS 沒有像 HTML 和 JavaScript 那樣有一份統一的標準文檔,而是由近百份不同的標準組成。本章透過 CSS 語法結構這條線索,系統性地整理 CSS 的核心知識。

CSS 語法結構#

CSS 的頂層樣式表由兩種規則組成:

  1. at-rule(@ 規則):以 @ 關鍵字開頭的特殊規則
  2. qualified rule(普通規則):選擇器 + 聲明區塊

常用 @ 規則#

/* 字符編碼,必須在最前面 */
@charset "utf-8";

/* 引入其他 CSS 文件 */
@import "mystyle.css";

/* 媒體查詢 */
@media print {
  body {
    font-size: 10pt;
  }
}

/* 關鍵幀動畫 */
@keyframes slidein {
  from {
    transform: translateX(0%);
  }
  to {
    transform: translateX(100%);
  }
}

/* 自定義字體 */
@font-face {
  font-family: "MyFont";
  src: url("myfont.woff2");
}

/* 特性檢測 */
@supports (display: grid) {
  .container {
    display: grid;
  }
}

選擇器體系#

選擇器分類#

類別說明範例
類型選擇器根據標籤名選擇div, p
全體選擇器選擇所有元素*
ID 選擇器根據 id 屬性#myid
Class 選擇器根據 class 屬性.myclass
屬性選擇器根據屬性[type="text"]
偽類選擇器元素特定狀態:hover, :first-child
偽元素選擇器虛擬元素::before, ::after

屬性選擇器#

/* 存在屬性 */
[disabled] {
  opacity: 0.5;
}

/* 精確匹配 */
[type="text"] {
  border: 1px solid;
}

/* 包含匹配(空格分隔的列表) */
[class~="active"] {
  color: red;
}

/* 前綴匹配(值或值-開頭) */
[lang|="en"] {
  font-family: serif;
}

/* 開頭匹配 */
[href^="https"] {
  color: green;
}

/* 結尾匹配 */
[href$=".pdf"] {
  color: red;
}

/* 包含匹配 */
[href*="example"] {
  text-decoration: underline;
}

選擇器組合#

選擇器的連線方式有優先級:

優先級連線符說明
第一(無連線)複合選擇器,表示「且」
第二空格, >, ~, +, ||關係選擇器
第三,選擇器列表,表示「或」
/* 複合選擇器:同時具有 class a 和 b */
.a.b {
}

/* 後代選擇器:a 的所有後代 b */
.a .b {
}

/* 子代選擇器:a 的直接子元素 b */
.a > .b {
}

/* 相鄰兄弟:緊跟在 a 後面的 b */
.a + .b {
}

/* 後續兄弟:a 之後的所有兄弟 b */
.a ~ .b {
}

選擇器優先級#

CSS 使用三元組 (a, b, c) 計算選擇器優先級:

  • a:ID 選擇器數量
  • b:偽類選擇器 + class 選擇器數量
  • c:偽元素選擇器 + 標籤選擇器數量
/* 優先級:(0, 1, 1) */
div.active {
}

/* 優先級:(1, 0, 0) */
#myid {
}

/* 優先級:(0, 2, 0) */
.a.b {
}

行內樣式的優先級永遠高於 CSS 規則,!important 則高於行內樣式。避免濫用 !important,它會造成樣式難以維護。

偽類選擇器#

樹結構偽類#

/* 根元素 */
:root {
}

/* 空元素 */
:empty {
}

/* 第 n 個子元素 */
:nth-child(2n) {
} /* 偶數 */
:nth-child(2n + 1) {
} /* 奇數 */
:nth-child(3) {
} /* 第 3 個 */

/* 第一個/最後一個 */
:first-child {
}
:last-child {
}

/* 唯一子元素 */
:only-child {
}

鏈接與行為偽類#

:link {
} /* 未訪問的鏈接 */
:visited {
} /* 已訪問的鏈接 */
:hover {
} /* 滑鼠懸停 */
:active {
} /* 激活狀態(點擊時) */
:focus {
} /* 獲得焦點 */
:target {
} /* URL hash 指向的元素 */

邏輯偽類#

/* 否定選擇器 */
:not(.disabled) {
}

/* 選擇器 4 級(部分瀏覽器支持) */
:is(h1, h2, h3) {
}
:where(h1, h2, h3) {
}

偽元素#

偽元素創造不存在的虛擬元素:

/* 首行 */
p::first-line {
  text-transform: uppercase;
}

/* 首字母 */
p::first-letter {
  font-size: 2em;
  float: left;
}

/* 在內容前插入 */
.icon::before {
  content: ">";
}

/* 在內容後插入 */
.external::after {
  content: url(external-icon.svg);
}

::before::after 必須設定 content 屬性才會生效,它們支援所有 CSS 屬性。

盒模型#

box-sizing#

/* content-box(預設):width/height 只包含內容 */
.content-box {
  box-sizing: content-box;
  width: 100px;
  padding: 10px;
  /* 實際寬度 = 100 + 10*2 = 120px */
}

/* border-box:width/height 包含 padding 和 border */
.border-box {
  box-sizing: border-box;
  width: 100px;
  padding: 10px;
  /* 實際寬度 = 100px */
}

推薦全域使用 border-box

*,
*::before,
*::after {
  box-sizing: border-box;
}

Flex 佈局#

Flex 是為了解決傳統 CSS 三大經典問題而設計的:

  • 垂直居中
  • 兩列等高
  • 自適應寬度

核心概念#

  • Flex 容器:設定 display: flex 的元素
  • Flex 項目:容器的直接子元素
  • 主軸(Main Axis):項目排列方向
  • 交叉軸(Cross Axis):垂直於主軸

容器屬性#

.container {
  display: flex;

  /* 主軸方向 */
  flex-direction: row | row-reverse | column | column-reverse;

  /* 換行 */
  flex-wrap: nowrap | wrap | wrap-reverse;

  /* 主軸對齊 */
  justify-content: flex-start | flex-end | center | space-between | space-around
    | space-evenly;

  /* 交叉軸對齊(單行) */
  align-items: stretch | flex-start | flex-end | center | baseline;

  /* 交叉軸對齊(多行) */
  align-content: stretch | flex-start | flex-end | center | space-between |
    space-around;
}

項目屬性#

.item {
  /* 擴展比例 */
  flex-grow: 0;

  /* 收縮比例 */
  flex-shrink: 1;

  /* 基準大小 */
  flex-basis: auto;

  /* 簡寫 */
  flex: 0 1 auto; /* grow shrink basis */
  flex: 1; /* flex: 1 1 0 */

  /* 單獨對齊 */
  align-self: auto | flex-start | flex-end | center | baseline | stretch;

  /* 排序 */
  order: 0;
}

經典問題解決方案#

垂直居中
.parent {
  display: flex;
  justify-content: center; /* 水平居中 */
  align-items: center; /* 垂直居中 */
  height: 300px;
}
兩列等高
.parent {
  display: flex;
  align-items: stretch; /* 預設值 */
}
.child {
  width: 100px;
}
自適應寬度
.parent {
  display: flex;
}
.fixed {
  width: 200px;
}
.auto {
  flex: 1; /* 填滿剩餘空間 */
}

CSS 動畫#

Transition 過渡#

.box {
  transition: property duration timing-function delay;

  /* 範例 */
  transition: all 0.3s ease-in-out;
  transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}

Animation 動畫#

@keyframes slide {
  0% {
    transform: translateX(0);
  }
  50% {
    transform: translateX(100px);
  }
  100% {
    transform: translateX(0);
  }
}

.animated {
  animation-name: slide;
  animation-duration: 2s;
  animation-timing-function: ease;
  animation-delay: 0s;
  animation-iteration-count: infinite;
  animation-direction: alternate;

  /* 簡寫 */
  animation: slide 2s ease 0s infinite alternate;
}

時間函式#

函式說明
linear線性
ease慢-快-慢(預設)
ease-in慢-快
ease-out快-慢
ease-in-out慢-快-慢
cubic-bezier(x1,y1,x2,y2)自定義貝塞爾曲線

貝塞爾曲線是一種插值曲線,其特點是「平滑」。時間曲線平滑意味著動畫較少突兀的變化,這是動畫設計所追求的效果。

CSS 變數#

:root {
  --primary-color: #007bff;
  --spacing: 8px;
}

.button {
  background: var(--primary-color);
  padding: var(--spacing);
  margin: calc(var(--spacing) * 2);
}

CSS 函式#

常用函式#

/* 計算 */
width: calc(100% - 200px);
width: min(100%, 500px);
width: max(300px, 50%);
width: clamp(300px, 50%, 800px);

/* 顏色 */
color: rgb(255, 0, 0);
color: rgba(255, 0, 0, 0.5);
color: hsl(0, 100%, 50%);

/* 變數 */
color: var(--primary-color, #000);

/* 屬性值 */
content: attr(data-label);

/* 漸變 */
background: linear-gradient(to right, red, blue);
background: radial-gradient(circle, red, blue);

實用建議#

選擇器使用原則

  • 用 ID 選擇單個元素
  • 用 class 組合選擇成組元素
  • 用標籤選擇器確定頁面風格
  • 避免過於複雜的選擇器組合

避免的做法

  • 過度依賴 !important
  • 使用過深的選擇器嵌套
  • 在組件庫中使用標籤選擇器
  • 忽略 box-sizing 的設定