CSS
Bun 的打包工具內建支援帶有現代特性的 CSS
Bun 的打包工具內建支援 CSS,具有以下功能:
- 將現代/未來特性轉換為可在所有瀏覽器上執行的程式碼(包括新增廠商字首)
- 壓縮
- CSS 模組
- Tailwind(通過原生打包工具外掛)
轉譯
預設啟用轉譯和廠商字首處理,因此你可以使用現代及未來的 CSS 特性,而無需擔心瀏覽器相容性。
Bun 的 CSS 解析器和打包器直接移植自 LightningCSS,其打包方式受 esbuild 啟發。轉譯器會將現代 CSS 語法轉換為向後相容的等效寫法,使其能夠在各種瀏覽器中正常執行。
瀏覽器相容性
預設情況下,Bun 的 CSS 打包目標瀏覽器包括:
- Edge 80+
- Firefox 78+
- Chrome 80+
- Safari 14+
- Opera 67+
語法降級
巢狀
藉助 CSS 巢狀,你可以直接在父級程式碼塊中編寫子級樣式,而無需在 CSS 檔案中重複父級選擇器。
/* 使用巢狀 */
.card {
background: white;
border-radius: 4px;
.title {
font-size: 1.2rem;
font-weight: bold;
}
.content {
padding: 1rem;
}
}
Bun 的 CSS 打包工具會自動將此巢狀語法轉換成傳統的扁平 CSS,以相容所有瀏覽器:
/* 編譯輸出 */
.card {
background: white;
border-radius: 4px;
}
.card .title {
font-size: 1.2rem;
font-weight: bold;
}
.card .content {
padding: 1rem;
}
你還可以在選擇器中巢狀媒體查詢和其他 at 規則:
.responsive-element {
display: block;
@media (min-width: 768px) {
display: flex;
}
}
編譯後為:
.responsive-element {
display: block;
}
@media (min-width: 768px) {
.responsive-element {
display: flex;
}
}
顏色混合
color-mix() 函式會在所選色彩空間中按給定比例混合兩種顏色。使用它可以建立顏色變體,而無需自行計算結果值。
.button {
/* 在 RGB 色彩空間中以 30%:70% 混合藍色和紅色 */
background-color: color-mix(in srgb, blue 30%, red);
/* 為懸停狀態建立較淺的變體 */
&:hover {
background-color: color-mix(in srgb, blue 30%, red, white 20%);
}
}
Bun 的 CSS 打包工具會在建置時評估這些顏色混合(當所有顏色值已確定,非 CSS 變數),生成適用於所有瀏覽器的靜態顏色值:
.button {
/* 精確計算得到的結果色 */
background-color: #b31a1a;
}
.button:hover {
background-color: #c54747;
}
相對顏色
相對顏色語法可以修改現有顏色的單個組成部分。無需重新計算整個顏色,即可調整亮度、飽和度或單獨的顏色通道。
.theme-color {
/* 以基礎色為起點,將亮度增加 15% */
--accent: lch(from purple calc(l + 15%) c h);
/* 以我們的品牌藍色為基色,製作一個去飽和版本 */
--subtle-blue: oklch(from var(--brand-blue) l calc(c * 0.8) h);
}
Bun 的 CSS 打包工具會在建置時計算這些相對顏色修改(不使用 CSS 變數時),生成適用於瀏覽器的靜態顏色值:
.theme-color {
--accent: lch(69.32% 58.34 328.37);
--subtle-blue: oklch(60.92% 0.112 240.01);
}
可將其用於主題生成、無障礙顏色變體,或根據基礎顏色生成顏色階,而不是為每個值硬編碼。
LAB 顏色
現代 CSS 支援感知均勻的顏色空間 LAB、LCH、OKLAB 和 OKLCH,它們可以表示超出標準 RGB 色域的顏色。
.vibrant-element {
/* 一種超出 sRGB 色域邊界的鮮豔紅色 */
color: lab(55% 78 35);
/* 使用感知色彩空間的平滑漸變 */
background: linear-gradient(to right, oklch(65% 0.25 10deg), oklch(65% 0.25 250deg));
}
Bun 的 CSS 打包器會將這些顏色格式轉換為向後相容的替代方案,以支援不支援這些格式的瀏覽器:
.vibrant-element {
/* 退回到最接近的 RGB 近似 */
color: #ff0f52;
/* 支援更寬色域的瀏覽器使用 P3 備選 */
color: color(display-p3 1 0.12 0.37);
/* 支援該格式的瀏覽器保持原值 */
color: lab(55% 78 35);
background: linear-gradient(to right, #cd4e15, #3887ab);
background: linear-gradient(to right, oklch(65% 0.25 10deg), oklch(65% 0.25 250deg));
}
顏色函式
color() 函式可以在傳統 RGB 之外的預定義色彩空間中指定顏色,讓你能夠使用更寬的色域。
.vivid-element {
/* 使用 Display P3 色彩空間實現更寬色域 */
color: color(display-p3 1 0.1 0.3);
/* 使用 A98 RGB 色彩空間 */
background-color: color(a98-rgb 0.44 0.5 0.37);
}
對於不支援這些色彩空間的瀏覽器,Bun 的 CSS 打包器會新增 RGB 備選值:
.vivid-element {
/* 優先使用 RGB 備選以獲得最大相容 */
color: #fa1a4c;
/* 保留原色以支援相容瀏覽器 */
color: color(display-p3 1 0.1 0.3);
background-color: #6a805d;
background-color: color(a98-rgb 0.44 0.5 0.37);
}
HWB 顏色
HWB(色相、白度、黑度)顏色模型基於純色相中混合了多少白色或黑色來表示顏色。與 RGB 或 HSL 值相比,使用這種方式可以更直接地建立淺色和深色變體。
.easy-theming {
/* 純青色,無白無黑 */
--primary: hwb(180 0% 0%);
/* 同色相,加入 20% 白色(色調) */
--primary-light: hwb(180 20% 0%);
/* 同色相,加入 30% 黑色(陰影) */
--primary-dark: hwb(180 0% 30%);
/* 新增白色和黑色的柔和版本 */
--primary-muted: hwb(180 30% 20%);
}
Bun 的 CSS 打包器會將 HWB 顏色轉換為 RGB,以相容所有瀏覽器:
.easy-theming {
--primary: #00ffff;
--primary-light: #33ffff;
--primary-dark: #00b3b3;
--primary-muted: #339999;
}
顏色表示法
現代 CSS 支援使用空格分隔的 RGB 和 HSL 值(不帶逗號),以及帶 alpha 通道的十六進位制顏色。
.modern-styling {
/* 空格分隔的 RGB 表示法(無逗號) */
color: rgb(50 100 200);
/* 空格分隔帶 alpha 的 RGB */
border-color: rgba(100 50 200 / 75%);
/* 帶 alpha 通道的 8 位十六進位制 */
background-color: #00aaff80;
/* 簡化的 HSL 表示 */
box-shadow: 0 5px 10px hsl(200 50% 30% / 40%);
}
Bun 的 CSS 打包器會將這些格式轉換為舊版瀏覽器支援的格式:
.modern-styling {
/* 轉為舊版需要的逗號格式 */
color: rgb(50, 100, 200);
/* 透明度通道妥善處理 */
border-color: rgba(100, 50, 200, 0.75);
/* 十六進位制帶 alpha 轉為 rgba */
background-color: rgba(0, 170, 255, 0.5);
box-shadow: 0 5px 10px rgba(38, 115, 153, 0.4);
}
light-dark() 顏色函式
light-dark() 函式接受兩種顏色,並根據當前顏色方案應用其中一種,使樣式無需使用媒體查詢即可遵循使用者的系統偏好。
:root {
/* 定義支援的顏色方案 */
color-scheme: light dark;
}
.themed-component {
/* 根據系統偏好自動選擇顏色 */
background-color: light-dark(#ffffff, #121212);
color: light-dark(#333333, #eeeeee);
border-color: light-dark(#dddddd, #555555);
}
/* 需要時覆蓋系統偏好 */
.light-theme {
color-scheme: light;
}
.dark-theme {
color-scheme: dark;
}
對於不支援 light-dark() 的瀏覽器,Bun 的 CSS 打包器會將其轉換為帶有回退值的 CSS 變數:
:root {
--buncss-light: initial;
--buncss-dark: ;
color-scheme: light dark;
}
@media (prefers-color-scheme: dark) {
:root {
--buncss-light: ;
--buncss-dark: initial;
}
}
.light-theme {
--buncss-light: initial;
--buncss-dark: ;
color-scheme: light;
}
.dark-theme {
--buncss-light: ;
--buncss-dark: initial;
color-scheme: dark;
}
.themed-component {
background-color: var(--buncss-light, #ffffff) var(--buncss-dark, #121212);
color: var(--buncss-light, #333333) var(--buncss-dark, #eeeeee);
border-color: var(--buncss-light, #dddddd) var(--buncss-dark, #555555);
}
邏輯屬性
CSS 邏輯屬性根據文件的書寫模式和文本方向,而不是螢幕的物理方向來定義佈局、間距和尺寸,因此佈局可以適應不同的書寫系統。
.multilingual-component {
/* 適應書寫方向的外邊距 */
margin-inline-start: 1rem;
/* 無論文本方向,都合理的內邊距 */
padding-block: 1rem 2rem;
/* 頂部起始角的邊框半徑 */
border-start-start-radius: 4px;
/* 尊重書寫模式的尺寸 */
inline-size: 80%;
block-size: auto;
}
對於不完全支援邏輯屬性的瀏覽器,Bun 的 CSS 打包器會針對每種文本方向將其編譯為物理屬性:
/* 左到右語言 */
.multilingual-component:dir(ltr) {
margin-left: 1rem;
padding-top: 1rem;
padding-bottom: 2rem;
border-top-left-radius: 4px;
width: 80%;
height: auto;
}
/* 右到左語言 */
.multilingual-component:dir(rtl) {
margin-right: 1rem;
padding-top: 1rem;
padding-bottom: 2rem;
border-top-right-radius: 4px;
width: 80%;
height: auto;
}
如果不支援 :dir() 選擇器,Bun 會生成額外的回退方案。
:dir() 選擇器
:dir() 偽類根據元素的文本方向(RTL 或 LTR)設定樣式,該方向由文件或顯式的 direction 屬性決定。使用它可以編寫無需 JavaScript 的方向感知樣式。
/* 根據文本方向應用不同樣式 */
.nav-arrow:dir(ltr) {
transform: rotate(0deg);
}
.nav-arrow:dir(rtl) {
transform: rotate(180deg);
}
/* 基於文本流的位置定位元素 */
.sidebar:dir(ltr) {
border-right: 1px solid #ddd;
}
.sidebar:dir(rtl) {
border-left: 1px solid #ddd;
}
對於不支援 :dir() 選擇器的瀏覽器,Bun 的 CSS 打包器會將其轉換為相容性更廣的 :lang() 選擇器,並使用適當的語言對映:
/* 作為回退轉換為基於語言的選擇器 */
.nav-arrow:lang(en, fr, de, es, it, pt, nl) {
transform: rotate(0deg);
}
.nav-arrow:lang(ar, he, fa, ur) {
transform: rotate(180deg);
}
.sidebar:lang(en, fr, de, es, it, pt, nl) {
border-right: 1px solid #ddd;
}
.sidebar:lang(ar, he, fa, ur) {
border-left: 1px solid #ddd;
}
如果不支援向 :lang() 傳入多個引數,Bun 會生成進一步的回退方案。
:lang() 選擇器
:lang() 偽類根據元素的語言定位元素。要為相關語言組合規則,可以向單個 :lang() 傳入多個語言程式碼。
/* 針對中日韓語言的排版調整 */
:lang(zh, ja, ko) {
line-height: 1.8;
font-size: 1.05em;
}
/* 不同語言組的引號樣式 */
blockquote:lang(fr, it, es, pt) {
font-style: italic;
}
blockquote:lang(de, nl, da, sv) {
font-weight: 500;
}
對於不支援在 :lang() 選擇器中使用多個引數的瀏覽器,Bun 的 CSS 打包器會將此語法轉換為行為相同的 :is() 選擇器:
/* 使用 :is() 組合多個語言以提高相容 */
:is(:lang(zh), :lang(ja), :lang(ko)) {
line-height: 1.8;
font-size: 1.05em;
}
blockquote:is(:lang(fr), :lang(it), :lang(es), :lang(pt)) {
font-style: italic;
}
blockquote:is(:lang(de), :lang(nl), :lang(da), :lang(sv)) {
font-weight: 500;
}
如果需要,Bun 還可以為 :is() 生成額外的回退方案。
:is() 選擇器
:is() 偽類函式(之前稱為 :matches())接收一個選擇器列表,如果列表中的任意選擇器匹配,則匹配成功。
/* 不用分別寫這些 */
/*
.article h1,
.article h2,
.article h3 {
margin-top: 1.5em;
}
*/
/* 可以寫成 */
.article :is(h1, h2, h3) {
margin-top: 1.5em;
}
/* 複雜範例,多個組合 */
:is(header, main, footer) :is(h1, h2, .title) {
font-family: "Heading Font", sans-serif;
}
對於不支援 :is() 的瀏覽器,Bun 的 CSS 打包工具提供使用廠商字首的替代寫法:
/* 使用 -webkit-any 回退 */
.article :-webkit-any(h1, h2, h3) {
margin-top: 1.5em;
}
/* 使用 -moz-any 回退 */
.article :-moz-any(h1, h2, h3) {
margin-top: 1.5em;
}
/* 現代瀏覽器保留原寫法 */
.article :is(h1, h2, h3) {
margin-top: 1.5em;
}
/* 複雜範例的回退 */
:-webkit-any(header, main, footer) :-webkit-any(h1, h2, .title) {
font-family: "Heading Font", sans-serif;
}
:-moz-any(header, main, footer) :-moz-any(h1, h2, .title) {
font-family: "Heading Font", sans-serif;
}
:is(header, main, footer) :is(h1, h2, .title) {
font-family: "Heading Font", sans-serif;
}
:not() 選擇器
:not() 偽類會排除匹配某個選擇器的元素。現代版本支援多個引數,可以通過一個 :not() 排除多種模式。
/* 選擇所有按鈕,排除主按鈕和次按鈕 */
button:not(.primary, .secondary) {
background-color: #f5f5f5;
border: 1px solid #ddd;
}
/* 應用樣式於所有標題,排除側邊欄或頁尾內的 */
h2:not(.sidebar *, footer *) {
margin-top: 2em;
}
對於不支援 :not() 中多個引數的瀏覽器,Bun 的 CSS 打包器會將此語法轉換為行為相同且相容性更好的形式:
/* 轉換為使用帶 :is() 的 :not() */
button:not(:is(.primary, .secondary)) {
background-color: #f5f5f5;
border: 1px solid #ddd;
}
h2:not(:is(.sidebar *, footer *)) {
margin-top: 2em;
}
如果不支援 :is(),Bun 會生成進一步的回退:
/* 額外回退以確保最大相容 */
button:not(:-webkit-any(.primary, .secondary)) {
background-color: #f5f5f5;
border: 1px solid #ddd;
}
button:not(:-moz-any(.primary, .secondary)) {
background-color: #f5f5f5;
border: 1px solid #ddd;
}
button:not(:is(.primary, .secondary)) {
background-color: #f5f5f5;
border: 1px solid #ddd;
}
轉換後的選擇器保留了原始選擇器的優先順序和行為。
數學函式
CSS 包含以下數學函式:
- 標準數學函式:
round()、mod()、rem()、abs()、sign() - 三角函式:
sin()、cos()、tan()、asin()、acos()、atan()、atan2() - 指數函式:
pow()、sqrt()、exp()、log()、hypot()
.dynamic-sizing {
/* 限定值在最小和最大之間 */
width: clamp(200px, 50%, 800px);
/* 四捨五入到最接近的倍數 */
padding: round(14.8px, 5px);
/* 動畫或佈局的三角函式 */
transform: rotate(calc(sin(45deg) * 50deg));
/* 組合多個函式的複雜數學 */
--scale-factor: pow(1.25, 3);
font-size: calc(16px * var(--scale-factor));
}
當所有值都是已知常量(而非變數)時,Bun 的 CSS 打包器會在建置時計算這些表示式:
.dynamic-sizing {
width: clamp(200px, 50%, 800px);
padding: 15px;
transform: rotate(35.36deg);
--scale-factor: 1.953125;
font-size: calc(16px * var(--scale-factor));
}
媒體查詢範圍
媒體查詢範圍語法使用比較運算子(<、>、<=、>=)表示斷點,而不是使用更冗長的 min- 和 max- 字首。
/* 比較運算子的現代寫法 */
@media (width >= 768px) {
.container {
max-width: 720px;
}
}
/* 使用 <= 和 >= 的包含範圍 */
@media (768px <= width <= 1199px) {
.sidebar {
display: flex;
}
}
/* 使用 < 和 > 的排他範圍 */
@media (width > 320px) and (width < 768px) {
.mobile-only {
display: block;
}
}
Bun 的 CSS 打包器會將範圍查詢轉換為傳統的媒體查詢語法,以相容所有瀏覽器:
/* 轉換為傳統 min/max 語法 */
@media (min-width: 768px) {
.container {
max-width: 720px;
}
}
@media (min-width: 768px) and (max-width: 1199px) {
.sidebar {
display: flex;
}
}
@media (min-width: 321px) and (max-width: 767px) {
.mobile-only {
display: block;
}
}
簡寫屬性
CSS 引入了幾種將多個長屬性組合在一起的簡寫屬性。
/* 對齊簡寫 */
.flex-container {
/* align-items 和 justify-items 的簡寫 */
place-items: center start;
/* align-content 和 justify-content 的簡寫 */
place-content: space-between center;
}
.grid-item {
/* align-self 和 justify-self 的簡寫 */
place-self: end center;
}
/* 雙值溢位屬性 */
.content-box {
/* 第一個值對應水平,第二個垂直 */
overflow: hidden auto;
}
/* 增強的文本裝飾 */
.fancy-link {
/* 結合多條文本裝飾屬性 */
text-decoration: underline dotted blue 2px;
}
/* 雙值 display 語法 */
.component {
/* 外層和內層顯示型別 */
display: inline flex;
}
對於不支援這些簡寫屬性的瀏覽器,Bun 會將它們轉換為對應的元件長屬性:
.flex-container {
/* 展開對齊屬性 */
align-items: center;
justify-items: start;
align-content: space-between;
justify-content: center;
}
.grid-item {
align-self: end;
justify-self: center;
}
.content-box {
/* 分別控制溢位方向 */
overflow-x: hidden;
overflow-y: auto;
}
.fancy-link {
/* 單獨文本裝飾屬性 */
text-decoration-line: underline;
text-decoration-style: dotted;
text-decoration-color: blue;
text-decoration-thickness: 2px;
}
.component {
/* 單值 display */
display: inline-flex;
}
雙位置漸變
雙位置漸變語法會在兩個相鄰位置指定相同的顏色,從而建立硬色標:產生銳利的過渡,而不是平滑的漸變。它適用於條紋、色帶以及其他多色設計。
.striped-background {
/* 在 30%-40% 製造綠色到紅色的硬切換 */
background: linear-gradient(
to right,
yellow 0%,
green 20%,
green 30%,
red 30%,
/* 雙點位置製造硬邊界 */ red 70%,
blue 70%,
blue 100%
);
}
.progress-bar {
/* 製造明顯色塊 */
background: linear-gradient(
to right,
#4caf50 0% 25%,
/* 綠色從 0% 到 25% */ #ffc107 25% 50%,
/* 黃色從 25% 到 50% */ #2196f3 50% 75%,
/* 藍色從 50% 到 75% */ #9c27b0 75% 100% /* 紫色從 75% 到 100% */
);
}
對於不支援此語法的瀏覽器,Bun 的 CSS 打包器會通過複製顏色標記,將其轉換為傳統格式:
.striped-background {
background: linear-gradient(
to right,
yellow 0%,
green 20%,
green 30%,
red 30%,
/* 分成兩段色點 */ red 70%,
blue 70%,
blue 100%
);
}
.progress-bar {
background: linear-gradient(
to right,
#4caf50 0%,
#4caf50 25%,
/* 兩段色點綠段 */ #ffc107 25%,
#ffc107 50%,
/* 兩段色點黃段 */ #2196f3 50%,
#2196f3 75%,
/* 兩段色點藍段 */ #9c27b0 75%,
#9c27b0 100% /* 兩段色點紫段 */
);
}
system-ui 字型
system-ui 通用字體系列使用裝置的原生 UI 字型。
.native-interface {
/* 使用系統預設 UI 字型 */
font-family: system-ui;
}
.fallback-aware {
/* 帶顯式回退的系統 UI 字型 */
font-family: system-ui, sans-serif;
}
對於不支援 system-ui 的瀏覽器,Bun 的 CSS 打包器會將其擴充套件為跨平臺字型堆疊:
.native-interface {
/* 為所有主流平臺擴充套件 */
font-family:
system-ui,
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
"Noto Sans",
Ubuntu,
Cantarell,
"Helvetica Neue";
}
.fallback-aware {
/* 擴充套件字型堆疊後保留原輪廓回退 */
font-family:
system-ui,
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
"Noto Sans",
Ubuntu,
Cantarell,
"Helvetica Neue",
sans-serif;
}
擴充套件後的字型堆疊包含適用於 macOS/iOS、Windows、Android 和 Linux 的系統字型,以及針對舊版瀏覽器的回退字型。
CSS 模組
Bun 的打包器還支援 CSS 模組,具有以下功能:
- 無需設定即可檢測 CSS 模組檔案(
.module.css) - 組合(
composes屬性) - 將 CSS 模組匯入 JSX/TSX
- 針對 CSS 模組無效用法的警告/錯誤
CSS 模組是一種 CSS 檔案(副檔名為 .module.css),其中所有類名和動畫都限定在該檔案的作用域內。這有助於避免類名衝突,因為 CSS 宣告預設是全域性作用域的。
Bun 的打包器會將區域性作用域的類名轉換為唯一識別符號。
快速入門
建立一個以 .module.css 結尾的 CSS 檔案:
.button {
color: red;
}
.button {
color: blue;
}
然後你可以匯入此檔案,比如在 TSX 檔案中:
import styles from "./styles.module.css";
import otherStyles from "./other-styles.module.css";
export default function App() {
return (
<>
<button className={styles.button}>紅色按鈕!</button>
<button className={otherStyles.button}>藍色按鈕!</button>
</>
);
}
匯入 CSS 模組會得到一個物件,該物件將每個類名對映到其唯一識別符號:
import styles from "./styles.module.css";
import otherStyles from "./other-styles.module.css";
console.log(styles);
console.log(otherStyles);
輸出結果如下:
{
button: "button_123";
}
{
button: "button_456";
}
每個檔案中的類名都是唯一的,因此不會發生衝突。
組合
CSS 模組可以將類選擇器組合在一起,以便在多個類之間複用樣式規則。
例如:
.button {
composes: background;
color: red;
}
.background {
background-color: blue;
}
這與以下寫法相同:
.button {
background-color: blue;
color: red;
}
.background {
background-color: blue;
}
使用 composes 時需要遵循以下兩條規則:
#button {
/* 無效!`#button` 不是類選擇器 */
composes: background;
}
.button,
.button-secondary {
/* 無效!`.button, .button-secondary` 不是簡單選擇器 */
composes: background;
}
從獨立 CSS 模組檔案組合
你也可以從其他 CSS 模組檔案中組合類:
.background {
background-color: blue;
}
.button {
composes: background from "./background.module.css";
color: red;
}