動態

詳情 返回 返回

2023 年開始寫 CSS 會與眾不同 - 動態 詳情

CSS 的發展速度比以往任何時候都要快。在 Flexbox 和 Grid 之後,CSS 的發展似乎經歷了一段漫長的停滯期,但在近幾年,CSS 已經新增了許多新功能可用,而且還會有更多新功能即將推出。這個發展速度是令人興奮的,同時也有些壓倒性。

雖然 CSS 新增了很多新功能,但很多 Web 開發者都認為這些花裏胡哨的東西並沒有給自己帶來實質上的變化。換句話説,所有這些花裏胡哨的東西(CSS 新特性)實際上改變了你編寫 CSS 的方式嗎?對於大多數 Web 開發者而言,CSS 的新特性確實影響了今天編寫 CSS 的方式,但也許並沒有像我預期的那樣徹底。

雖然我看到很多博客文章以及我自己的小冊《現代 CSS》中有關這些新潮事物的介紹和示例,但我還沒有看到這些實際應用在生產或日常中使用。這並不是對任何人或任何事情的抱怨。就我個人而言,我對 CSS 的演進感到非常興奮。許多最新的功能是我們多年來一直渴望的。確實,其中有一些功能正在逐漸融入我的 CSS 中。雖然不是徹底改變,但足以讓我比以往更喜歡編寫 CSS。

2023 年對 CSS 來説是重要的一年!

從年初的 2023 Google I/O 大會到年底的 WWDC23 大會,還有 @Bramus 在烏得勒支@Frontmania 大會上的分享以及最近 Chrome 團隊分享的《CSS Wrapped: 2023!》都在聊 CSS 的最新特性。我也不例外,我花了半年的時間專門以小冊的形式在闡述 CSS 現代特性。很慶幸的是,我的小冊所介紹的 CSS 最新特性基本上(達 95% 以上的特性)都在前面這些大會的主題上出現過。

小冊地址:https://juejin.cn/book/7223230325122400288

可以説,零零總總有幾十個 CSS 新特性得到了主流 Web 平台的支持,可以説 2023 年是 CSS 很重要的一年。這些進展使開發者們曾經認為在 Web 平台上不可能實現的功能成為現實。現在,主流的現代 Web 瀏覽器都支持 CSS 容器查詢(尺寸查詢、樣式查詢和狀態查詢等)、子網格(subgrid)、關係型選擇器 :has() (也常被稱為父選擇器)、複雜的第 n-* 選擇器,以及一系列新的顏色空間和函數,例如 color()color-mix() 。Chrome 瀏覽器還支持僅用 CSS 實現的滾動驅動動畫,以及在 Web 視圖之間平滑過渡的視圖過渡效果(如果你對 Web 動畫感興趣,可以移步閲讀《Web 動畫之旅》)。最重要的是,有許多新的基礎特性出現,例如 CSS圖層(@layer)、 嵌套和作用域等,可以更好的提高Web 開發者編寫 CSS 的體驗,甚至是改變 Web 開發者編寫、維護和管理 CSS 的方式。

很精彩的一年吧!看到這麼多新特性,你是否有點遲疑了,CSS 有這麼多新東西?甚至還有可能會令你感到困惑,這還是你認識的 CSS 嗎?

試問一下,自己認識多少,又用過多少。更重要的是這些新特性有沒有真正的給你自己帶來變化!

放棄你的遲疑

通常情況之下,人面對新鮮事物的表現因個體差異有所不同,有的人可能表現為好奇、興奮和積極,而有的人可能表現為恐懼和牴觸。同樣的,Web 開發者面對 CSS 的新特性時,也可能會表現出多種反應和行為,這些反應和行為取決於他們的技術水平、項目需求、個人偏好以及對新技術的接受程度。比如,有的同學會對 CSS 新特性感到好奇、興奮(比如我),對於這部分同學而言,他們會立即行動起來,去學習和實踐這些新特性,甚至還有同學會積極參與社區建設,使這些新特性更完善。當然,也有一部分同學會表現出抗拒、保守,甚至是排斥。而且在中國社區,這部分人佔多數,因為我經會聽到身邊小夥伴對新特性感遲疑。這些花裏胡哨的東西,能用嗎?兼容性好嗎?等等!

在這裏,我想説的是,隨着時間的推移,新特性會不斷進化和改進。正如當年在推廣 CSS3 特性的時候,被問得最多的是“新特性能兼容 IE 嗎”?

現如今,我同樣會被問類似的問題:“這些新特性兼容性如何?” 同時,會有很多同學問我,如何以及在哪裏可以獲得這些新特性?其實,這是一件好事,意味着有更多的同學在放棄對新特性的遲疑、恐懼和排斥等。

這已經是一種改變。

事實上,當下的環境對於 Web 開發者而言是友好的,我們可以通過不同的方式獲得新特性的版本發佈信息、兼容性數據、互操作性以及 Web 平台的提供的信息。

版本發佈

我們可以在 Chrome、Safari 和 Firefox 等主流瀏覽器發版中獲得各 Web 平台引入的新功能、錯誤修復等相關信息。

發佈説明

Web 平台(Chrome、Safari 和 Firefox)每一次發佈新版本都會提供相應的文檔,其中包含有關新功能、改進和問題修復的詳細信息。Web 開發者可以通過閲讀發佈説明來了解相關的信息。同樣的,我們在相應的文檔中也能獲得 CSS 相關的信息,包括最新功能、功能改進和問題修復等信息。

兼容性數據

Web 開發對 CSS 新特性止步的關鍵因素還是跨平台的兼容性。如果你想了解某個新特性的兼容性相關數據,可以通過 Can I Use、Browser Compat Data 和 Time to Stable 等平台上獲取。

互操作性

Interop 是一項跨瀏覽器的努力,旨在提高 Web 的互操作性。互操作性指的是為改善 Web 技術在不同瀏覽器中的互操作性而進行的努力。互操作性是指確保在不同環境下(尤其是不同瀏覽器)使用相同的 Web 技術時,能夠獲得一致的行為和效果。簡單地説,每種技術在所有瀏覽器中達到完全相同的狀態。

Interop 是從 2021 年開始,現代瀏覽器都參與進來了,到目前已有 Interop2021、Interop2022 和 Interop2023:

從上圖中,你就能發現 2023 年各 Web 平台對 CSS 付出的努力,這也是為什麼 2023 年會有這麼多 CSS 新特性得到主流 Web 瀏覽器的支持。

平台新聞

在 web.dev 的博客中,你將獲得每個月關於 Web 平台的最新消息。這可能包括有關Web 技術的更新、改進或變化的信息:

正如上圖所示,它陳述了 2023 年 12 月份 Web 平台上的新功能。

Baseline

在 2023年 Google I/O 大會上推出了基準,旨在明確説明網絡平台功能是否可供使用。Baseline 的原始定義是,當所有主流瀏覽器(Chrome、Edge、Firefox 和 Safari)的現行版本和以往版本均支持這些功能時,這些功能已納入 Baseline。

Baseline 即將登陸 caniuse.com!這篇博文將介紹這項集成,並探索 2023 年 Baseline 中包含的一些功能。根據基準的新定義,功能生命週期分為兩個階段。第一個選項是新推出,然後在 30 個月後全面推出。如果某個功能在以下瀏覽器中可互操作,便會成為 Baseline 新提供的功能的一部分:

  • Safari(macOS 和 iOS)
  • Firefox(桌面版和 Android 版)
  • Chrome(桌面版和 Android 版)
  • Edge(桌面設備)

以後大家在 caniuse.com 查看兼容數據時,你會看到一個標誌,告知你相應功能是否已在 Baseline 中廣泛提供。換句話説,如果你看到這個標誌,你就可以大膽的使用,不用再擔心跨瀏覽器的兼容性問題。這也就解決你對 CSS 新特性最大的疑惑——兼不兼容:

通過上面所述這些資源,你將獲得 Web 平台的第一手信息,也你將獲得 CSS 相關的最新信息。如果你對 CSS 感興趣,或者説對 Web 平台相關技術感興趣,那麼請訂閲它們,這樣能確保你時刻獲得跨平台的最新信息。有助於掌握更多的技能。

請留意接下來所介紹的 CSS 特性標題前的表情符,該表情符表示的是新 Baseline 徽章!具體詳細性請猛擊此處:

  • 💯:新增主要瀏覽器的所有穩定版 (放心使用)
  • 👁️:主要瀏覽器只支持部分功能(還得等一等)

架構基礎

讓我們從 CSS 的核心功能開始。這些是對你如何編寫和組織 CSS 具有基礎性作用的特性,這些基礎性特性解決了 CSS 中一直令 Web 開發者感到頭痛的問題。可以説,這些特性將直接改變你編寫、管理和維護 CSS 代碼的姿勢。

💯 級聯層

級聯層是 CSS 中最為重要的一個概念,很多 Web 開發者懼怕級聯層,其中原因之一就是編寫的 CSS 很容易造成衝突或者被覆蓋。例如:

ul[class] { /* (0,1,1) */
    margin: 0; 
    padding: 0; 
    list-style-type: none;
} 

.nav { /* (0,1,0) */
    margin: 0 40px; /* 被 ul[class] 中的 margin 覆蓋 */
}

為了決定哪個聲明(CSS 樣式規則)會“獲勝”(從而被應用到元素上),級聯提供了相應的算法。瞭解級聯算法有助於幫助我們理解瀏覽器是如何解決樣式規則衝突,也就是瀏覽器決定哪個樣式規則運用到元素上。但需要知道的是,級聯算法在不同的規範中有不同的描述,在 Level 5 中提供了六個不同的級別。在不考慮級聯層的情況下,其標準如下:

如上面示例,我們根據該標準需要提高 .nav 權重:

ul[class] { /* (0,1,1) */
    margin: 0; 
    padding: 0; 
    list-style-type: none;
} 

ul.nav { /* (0,1,1) */
    margin: 0 40px;
}

熟悉 CSS 的同學,應該知道這些標準的優先級從高到低排列,並且一個接一個地檢查,直到確定一個獲取的聲明。如果在較高的標準上不能確定哪一個屬性聲明會獲勝,級聯將轉到下一個標準。比如下圖所示:

為了解決諸如此類的問題(級聯與權重),CSS 新增了一個級聯層的規則,即 @layer

簡單地説: 級聯層提供了一種結構化的方式來組織和平衡單一來源中的 CSS 規則,最終決定誰獲勝!。

由於 CSS 的級聯層在 CSS 級聯中有着獨特的地位,使用它有一些好處,使開發者對級聯有更多的控制。CSS 的級聯層一般位於 “Style 屬性”(Style Attribute)和 CSS 選擇器權重(Specificity)之間,即:

有了 @layer 之後,我們可以像下面這樣改造前面所展示的代碼:

@layer reset, components;

@layer reset {
    ul[class] {
        margin: 0; 
        padding: 0; 
        list-style-type: none;
    } 
}

@layer components {
    .nav {
        margin: 0 40px;
    }
}

我使用下圖來闡述有級聯層 @layer 前後,CSS 級聯的差異:

帶來的直接變化是權重計算規則變了:

有了 CSS 級聯層 @layer 特性之後,你可以拋棄以前的一些 CSS 方法論(例如 ITCSS),因為 @layer 能更好的幫助你管理 CSS 的級聯。

簡單地説,級聯層 @layer 是 CSS 的一個新特性,它影響着樣式規則的應用和優先級。以下是級聯層的一些關鍵細節:

  • 添加到級聯層: 可以通過 @layer 規則將樣式添加到指定的級聯層中。這使得開發者能夠更有序地組織樣式。
  • 匿名級聯層: 如果未使用 @layer 規則,樣式將被放入默認的匿名級聯層。這樣可以確保未指定級聯層的樣式不會影響到指定級聯層的樣式。
  • 預定義級聯層順序: 級聯層可以預定義一個順序,確保在樣式規則中未明確指定級聯層的情況下,樣式按照默認的預定義級聯層順序應用。
  • 加載外部 CSS 文件到級聯層: 可以通過 @layer 規則加載外部 CSS 文件到指定的級聯層中,這樣可以更靈活地組織和加載樣式。
  • 無級聯層樣式: 可以使用 @layer unlayered 規則將樣式添加到無級聯層的樣式層,這樣的樣式在默認情況下不會被任何級聯層繼承。
  • 級聯層嵌套: 可以將一個級聯層嵌套在另一個級聯層中,以創建更復雜的樣式層次結構。
  • 回滾級聯層: 可以通過 @layer 規則回滾到之前的級聯層狀態,以便撤銷某些樣式的更改。

級聯層為開發者提供了更精細的樣式管理和組織的能力,使得在大型項目中更容易維護和擴展樣式。

有關於 CSS 級聯層 @layer 特性更詳細的介紹,請移步閲讀《現代 CSS》中的《CSS 分層:@layer 》課程!

💯 嵌套

通常情況之下,Web 開發者如果不使用 CSS 處理器,例如 Sass(或 SCSS)、LESS 和 Stylus 等,每個 CSS 選擇器都需要顯式聲明,彼此之間分開。例如:

table.colortable td {
    text-align: center;
} 

table.colortable td .c {
    text-transform: uppercase;
}
 
table.colortable td:first-child,
table.colortable td:first-child + td {
    border: 1px solid black;
}

table.colortable th {
    text-align: center;
    background: black;
    color: white;
} 

這將使得樣式代碼重複、冗餘和分散。正因此,很多 Web 開發者會基於 CSS 處理器特性上,使用嵌套的方式來編寫 CSS 的選擇器。例如,上面的代碼用 SCSS 的嵌套語法編寫的話,會像下面這樣:

table.colortable {
    td {
        text-align: center;
        .c {
            text-transform: uppercase;
        }
        &:first-child {
            border: 1px solid black;
            + td {
                border: 1px solid black;
            }
        }
    }
    th {
        text-align: center;
        background: black;
        color: white;
    }
}

現在,CSS 也具備像 SCSS 相似的嵌套特性,可以將相關樣式規則分組到選擇器中,從而繼續創建選擇器。

table.colortable {
    & td {
        text-align: center;
        .c {
            text-transform: uppercase;
        }
        &:first-child,
        &:first-child + td {
            border: 1px solid black;
        }
    }
    & th { 
        text-align: center;
        background: black;
        color: white;
    } 
} 

嵌套可以減少樣式表的大小、減少重複選擇器的開銷,並集中組件樣式。該語法最初發布時存在一個限制:“CSS 的任何選擇器都可以嵌套到另一個選擇器中,但它必須以 & . (類名)、 # (ID)、 @ @ 規則)、 : :: * + ~ > [ 符號開頭。 ” 這些符號是一些識別符號,它會向解析器發出信號,表示它正在使用嵌套樣式。後來,通過嵌套寬鬆的語法更新,該限制已被解決,例如不需要在元素選擇器之前添加 & 識別符:

table.colortable {
    td {
        text-align: center;
        .c {
            text-transform: uppercase;
        }
        &:first-child,
        &:first-child + td {
            border: 1px solid black;
        }
    }
    th { 
        text-align: center;
        background: black;
        color: white;
    } 
}

同樣的,它也可以和 @ 規則相互嵌套,例如:

h1 {
    font-size: 2em;
    
    @media (width >= 40em) {
        & {
            font-size: 4em;
        }
    }
}

/* 寬鬆語法 */

h1 {
    font-size: 2em;
    
    @media (width >= 40em) {  
        font-size: 4em;
    }
}
有關於 CSS 嵌套特性更詳細的介紹,請移步閲讀《現代 CSS》中的《CSS 的嵌套和作用域: & 和 @scope 》課程!

👁️ 作用域

眾所周知,CSS 語言和其他程序語言不同,它是沒有作用域的概念。通常情況之下,開發者需要通過 DOM 結構和選擇器來達到類似作用域的功能。這使得 Web 開發者在編寫選擇器時,可能會發現自己在兩個世界之間交替。一方面,Web 開發者需要明確具體選擇哪些元素;另一方面,Web 開發者希望選擇器保持晚於替換,而不是與 DOM 結構緊密耦合。這也是 tailwindcss 備受歡迎的原因之一。

為此,W3C 的 CSS 工作小組為 CSS 新增了作用域 @scope 特性,該特性首先得到了 Chrome (118版本)的支持。@scope 是一個 @ 規則,它主要有兩個賣點:基於接近度的樣式為選擇器設置下限。換句話説,作用域給 CSS 帶來了兩個關鍵的東西:

  • 一組樣式可以根據在 DOM 中的接近程度覆蓋另一組樣式
  • 更多地控制選擇器針對哪些元素(即更好地操作 CSS 的級聯)

這意味着,@scope 規則可以讓你將選擇器的範圍限定為文檔的特定子樹。藉助作用域樣式,你可以非常具體地選擇元素,而無需編寫過於具體的選擇器或將它們與 DOM 結構緊密耦合。

/* 根作用域 */ 

@scope (.card) {
    img {
        border-color: green;
    }
}

限定了作用域的樣式規則 img { … } 實際上只能選擇在所匹配 .card 元素範圍內的 <img> 元素。如需阻止卡片內容區域 (.card__content) 內的 <img> 元素處於選中狀態,你可以使用更具體的 img 選擇器。另一種方法是利用 @scope at 規則這一事實,也接受用於確定下限的範圍限制

@scope (.card) to (.card__content) {
    img {
        border-color: green;
    }
}

此限定了範圍的樣式規則僅定位到祖先樹中位於 .card.card__content 元素之間的 <img> 元素。這種包含上下邊界的範圍通常稱為“圓環圖範圍”。

下面這個案例中,由於應用了範圍限制,輪播圖組件中的 <img> 元素不匹配:

Demo 地址:https://codepen.io/web-dot-dev/pen/YzBLdjG

在 CSS 級聯內,@scope 還添加了一個新條件:確定鄰近區域。該步驟在特異性之後,但出現在呈現順序之前。

按照規範:“比較出現在具有不同範圍根的樣式規則中的聲明時,範圍根和限定範圍的樣式規則主題之間具有最少的代數或同級元素躍點的聲明勝出”。

有關於 CSS 嵌套特性更詳細的介紹,請移步閲讀《現代 CSS》中的《CSS 的嵌套和作用域: & 和 @scope 》課程,或者請參閲《如何使用 @scope 限制選擇器的覆蓋面》和 @Miriam Suzanne 的《CSS @scope》。

💯 選擇器 :is() 和 :where()

:is():where() 是兩個偽類選擇器,可以用於選擇多個簡單選擇器的集合,並將它們作為一個整體來進行選擇,以簡化和優化選擇器的編寫。

  • :is() 函數接受一個包含多個簡單選擇器的列表,它返回一個與其中任何一個簡單選擇器匹配的元素,類似於數組中的 ||(邏輯或)。
  • :where() 函數也接受一個包含多個簡單選擇器的列表,將選擇器中的元素組合在一起,使其更易讀、易用。

:is():where() 選擇器屬於寬容型選擇器,使用 :is():where() 時,如果一個選擇器無法解析,整個選擇器列表不會被視為無效,而是會忽略不正確或不支持的選擇器,並使用其他的選擇器。另外,它們的使用方式基本相似,唯一的差別就是 :where() 選擇器權重總是為 0 ,而 :is() 選擇器的權重計數等同於選擇器列表中最高權重的值**。

正因為 :where():is() 選擇器可以用來改變選擇器權重,因此,可以使用它們來做為級聯層的一種簡單替代方案。比如,你正在創建一個框架或者一個庫,那麼可以使用 :where() 偽類函數,將框架或庫中的選擇器權重降低至 0 。這樣做的好處是,使用你的框架或庫的 Web 開發者,無需處理選擇器權重的問題,它可以很輕易的覆蓋你框架或庫中的樣式規則。

簡單地説,:is():where() 可以幫助我們更好地管理 CSS 選擇器權重。最為有效的使用方式是:

  • 在構建 CSS 框架或庫的時候,使用 :where() 來管理所有選擇器的權重,將選擇器權重降至為 0
  • 在使用框或庫的時候,可以使用 :is() 來提高選擇器權重,在不改變 HTML 代碼的情況之下,可以將選擇器權重提高到最高級別

考慮到 :is():where() 之間的差異,使用哪個選擇器,最終還是取決於你的具體需求。

有關於 CSS 嵌套特性更詳細的介紹,請移步閲讀《現代 CSS》中的《CSS 選擇器::where() vs. :is() 》課程!

💯 選擇器 :has()

CSS 的 :has() 選擇器被稱為 CSS 的父選擇器!它和 CSS 的容器查詢特性一樣,一直以來是 Web 開發者最想要的 CSS 功能。

就我個人而言,CSS 的 :has() 是最接近 if ... else ... 功能的。它與 HTML 的 DOM 關係緊密相連,這也是它被稱之為是關係型選擇器的主要原因。比如下面這個示例,你正在尋找後代元素的存在,但應用的樣式將是父元素。

<!-- Case ① -->
<figure> 
    <figcaption>CSS Pseudo-Class Specification</figcaption> 
    <img src="https://picsum.photos/1240/?random=11" alt=""> 
</figure> 

<!-- Case ② -->
<figure> 
    <div class="media"> 
        <img src="https://picsum.photos/1240/?random=12" alt=""> 
    </div> 
    <figcaption>CSS Pseudo-Class Specification</figcaption> 
</figure> 

<!-- Case ③ -->
<figure> 
    <img src="https://picsum.photos/1240/?random=13" alt=""> 
    <figcaption>CSS Pseudo-Class Specification</figcaption> 
</figure>

這三個 Case 對應的 DOM 樹如下圖所示:

添加下面這樣一段 CSS 代碼:

/* 匹配包含<figcaption>後代元素的<figure>元素 */ 
figure:has(figcaption) { 
    background-color: #3f51b5; 
} 

figure:has(figcaption) 將能匹配所有 figure ,因為 figure 都包含了 figcaption 元素:

Demo 地址:https://codepen.io/airen/full/jOeMapz

上面你所看到的只是 :has() 最簡單地使用,它可以幫助你做更為複雜的事情,甚至是一些帶交互行為的操作。比如下面這個示例,使用 :has() 選擇器和狀態選擇器,可以實現一個純 CSS 製作的評分組件(StarRating):

Demo 地址:https://codepen.io/airen/full/poxeoeE

也正因如此,我在《防禦式 CSS 精講》中,將 :has() 納入到條件 CSS 的範疇,因為它在很多時候,能根據相關的動態條件,允許你使用不同的 CSS。

:has() 選擇器是強大的,它除了能讓我們不使用 JavaScript 腳本實現一些具有挑戰性的 GUI 之外(比如上面這個示例),它還有很多其他的功能,比如 @wesbos 前段時間在 Twitter 分享一張圖,介紹了 :has() 選擇器十個小技巧:

圖片來源:https://twitter.com/wesbos/status/1737148340322652632

有關於 :has() 選擇器更詳細的介紹,你還可以參閲:

  • CSS 父選擇器::has()
  • CSS 選擇器::has() 能解決哪些問題
  • CSS 選擇器::has() 和 :not() 的組合

💯 複雜的第 n-* 選擇

CSS 選擇器級別 4 中的新功能是可以選擇將選擇器列表傳遞到 :nth-child():nth-last-child()

:nth-child(An+B [of S]?)
:nth-last-child(An+B [of S]?)

指定 of S 後,An+B 邏輯僅應用於與給定選擇器列表 S 匹配的元素。這實際上意味着,你可以在 An+B 執行操作之前預先過濾子項。

我們藉助 :nth-child() 偽類選擇器,可以按索引選擇 DOM 中的元素。你可以使用 An+B 微語法精確控制要選擇哪些元素。

默認情況下,:nth-*() 偽代碼會考慮所有子元素。從 Chrome 111 開始,你可以選擇將選擇器列表傳遞到 :nth-child():nth-last-child()。這樣一來,就可以在 An+B 執行操作之前預先過濾子項列表。

在下面的演示中,通過使用 of .small 對小玩偶進行預過濾,3n+1 邏輯僅應用於它們。使用下拉菜單可動態更改所使用的選擇器。

Demo 地址:https://codepen.io/web-dot-dev/full/YzBLdLW

更有意思的是,我們可以使用 :has():not()~+ 組合在一起模擬 :nth-child(An+B [of S]?):nth-last-child(An+B [of S]?) 選擇器,甚至是 CSS 中不存在的選擇器:

  • :first-in-ElementGroups-of-class(.😍) *,即選中元素組(ElementGroups)中第一個元素(類名為 .😍*),對應的選擇器為 .😍:not(:has(+ .😍))
  • :last-in-ElementGroups-of-class(.😍) *,即選中元素組(ElementGroups)中最後一個元素(類名為 .😍*),對應的選擇器為 .😍:not(:has(+ .😍))
  • :single-in-ElementGroups-of-class(.😍) ,即選中元素組(ElementGroups)中僅有的一個元素(類名為 .😍),對應的選擇器為 .😍:not(.😍 + .😍):not(:has(+ .😍))
  • :nth-in-ElementGroups-of-class(.😍) *,即選中元素組(ElementGroups)中的第 n 個元素(類名為 .😍)。比如 .😍:not(.😍 + .😍) + .😍 選擇元素組中第 2 個元素(類名為 .😍);.😍:not(.😍 + .😍) + .😍 + .😍 選擇元素組中第 3 個元素(類名為 .😍*)。
  • :nth-last-in-island-of-class(.special) ,即選中元素組(ElementGroups)中的倒數第 n 個元素(類名為 .😍)。比如 .😍:not(:has(+ .😍 + .😍)):has(+ .😍) 選中元素組中倒數第 2 個元素(類名為 .😍),.😍:not(:has(+ .😍 + .😍 + .😍)):has(+ .😍 + .😍) 選中元素組中倒數第 3 個元素(類名為 .😍)。

Demo 地址:https://codepen.io/airen/full/rNqmGrN

💯 CSS 三角函數

在 W3C 規範的 CSS 值和單位模塊 Level 4 (CSS Values and Units Module Level 4)為 Web 開發者提供了數學表達式相關的能力。除了我們所熟悉的 calc() 函數和 CSS 比較函數(比如 min()max()clamp())之外,還有 CSS 的三角函數,比如 sin()cos()tan()asin()acos()atan()atan2() 等。

現在,使用 CSS 的三角函數,你可以更輕鬆地在以一點為中心的圓圈上佈置元素:

Demo 地址:https://codepen.io/web-dot-dev/full/poGVqLO

如果你是一名動畫愛好者或者説平時經常有開發動畫的需求,那麼 CSS 三角函數將是你最好的助手。動畫開發過程中,你可以這樣來使用 CSS 的三角函數:

  • sin() 函數可用於改變元素尺寸或控制動畫時長;
  • cos() 函數可用於保持旋轉元素的尺寸不變;
  • tan() 函數可用於繪製平行四邊形;
  • asin()acos()atan()atan2() 函數可用於旋轉元素。

除此之外,三角函數在動畫中有多種應用和作用,以下是一些常見的例子:

  • 平滑運動和緩動效果: 通過使用三角函數,特別是正弦(sin())和餘弦(cos())函數,可以實現元素的平滑運動和緩動效果。這有助於使動畫看起來更自然,避免了突然的加速和減速。
  • 週期性動畫: 正弦(sin())和餘弦(cos())函數是週期性的,因此它們經常用於創建具有循環或重複動作的動畫效果。例如,通過調整正弦(sin())函數的參數,可以創建水波紋或震盪效果。
  • 旋轉動畫: 三角函數的旋轉性質使其成為創建對象旋轉動畫的理想選擇。通過在正弦(sin())和餘弦(cos())函數中使用時間變量,可以實現平滑的旋轉效果。
  • 路徑動畫: 三角函數的週期性和平滑性使其適用於定義對象沿着複雜路徑運動的動畫。通過適當調整三角函數的參數,可以創建各種路徑動畫。
  • 振盪效果: 通過使用正弦(sin())函數,可以模擬振盪效果,例如擺動或彈簧的彈性動畫。
  • 相位和頻率調整: 調整三角函數的相位和頻率可以改變動畫的速度和週期,從而提供更多的創造性控制。

這些只是三角函數在動畫中的一些常見應用,創意的設計師和開發者可以發揮出更多的潛力,創造出豐富多彩且引人入勝的動畫效果。例如下面這個摩天輪旋轉動效,就應用了 CSS 的三角函數:

Demo 地址:https://codepen.io/airen/full/JjxzWgW

你可以從《CSS 的三角函數》和 《數學的魔法:探索數學在動畫中的應用》兩節課中獲得 CSS 三角函數更多的信息!

💯 子網格 subgrid

藉助 CSS 子網格 subgrid ,你可以創建更復雜的網格佈局,並在子佈局之間實現更好的對齊。

它允許另一個網格內的網格將外部網格的行和列作為自己的行和列,方法是使用 subgrid 作為網格行或列的值。

簡單地説,在一個網格項目上顯式設置 display 的值為 gridinline-grid ,或者繼承其父網格容器的 display 值,就意味着該網格項目是一個獨立的網格格式化上下文。同時,子網格的 grid-template-columns 和(或)grid-template-rows 顯式設置值為 subgrid 時,就意味着子網格的內容參與其父網格的格式化上下文,而不會建立一個新的網格格式化上下文。

網格佈局中的子網格是非常有用的,它將為我們提供更多的方法來實現 CSS 網格之前不可能實現的功能。subgrid 非常適合用來根據彼此的動態內容對齊同級,藉助 subgrid,可以調整佈局以適應內容。

如果你對子網格佈局特性感興趣,可以移步閲讀《現代 Web 佈局》中的《網格佈局中的子網格和嵌套網格》!

排版

一直以來,Web 上的排版都是非常複雜的,所涉及到的知識也很多。在剛剛已過去的 2023 年,Web 排版取得了一些重要的更新。例如,你可以使用 text-wrap 屬性實現排版佈局調整,你可以使用 initial-letter 實現首字下沉的效果,你甚至還可以在 Web 上使用可變字體和彩色字體,為用户提供更好的閲讀體驗。

👁️ 首字下沉

首字下沉最早出現在印刷媒體上,例如報紙、雜誌、小説、教科書等。它可以為章節或段落的第一個字母增添一些時尚感。這些下沉字母能夠吸引讀者的注意力,並且還可以使用非常華麗的字體,因為它只會對一串文字中的一個字母進行處理,不會影響到文本的可讀性。

CSS 內聯佈局模塊 Level 3(CSS Inline Layout Module Level 3)提供的 initial-letter 屬性允許你更精細化的控制首字母下沉的樣式。它可以指定下沉首字母、凹陷首字母和凸起首字母的大小和下沉行數。

p::first-letter {
    initial-letter: 3 2;
}

Demo 地址:https://codepen.io/web-dot-dev/full/XWOKGaL

initial-letter 是一個小而美的 CSS 特性,可用於為首字母的放置位置設置樣式。

有關於 CSS 首字母下沉 initial-letter 特性更詳細的介紹,請移步閲讀《現代 CSS》中的《首字母下沉:initial-letter》課程!

👁️ 均衡和美觀

作為開發者,你不知道標題或段落的最終大小、字體大小,甚至是語言。有效且美觀處理文本換行所需的所有變量均在瀏覽器中提供。由於瀏覽器瞭解所有因素(例如字體大小、語言和分配區域),因此非常適合用於處理高級、優質文本佈局。

這就需要我們採用兩種新的文本換行技術,一種稱為 balance,另一種稱為 prettybalance 值旨在創建一個和諧的文本塊,而 pretty 旨在防止孤立字符並確保健康的斷字。傳統上,這兩項任務都是手動完成的,將這項工作交給瀏覽器,讓它支持任何翻譯的語言,真是太棒了。

text-wrap: balance; 會對行數有一個限制,但這取決於文本寬度下有多少行。使用 text-wrap: balance; 時,由於瀏覽器對文本包裝的行數有限制,因此應該僅用於標題和副標題。將其應用於大段文本將沒有效果,並且會帶來性能成本,因為瀏覽器儘管不會應用任何內容,但仍在嘗試計算最佳平衡。

文本排版時,你需要在平衡和美觀做抉擇。對於標題和副標題,使用 text-wrap: balance;。對於文本段落,使用 text-wrap: pretty; 以消除最後一行的孤立單詞。這些特性是漸進增強的好候選項。如果某人不在支持的瀏覽器中,它不會對體驗產生負面影響,但對於在支持的瀏覽器中的頁面視覺平衡會有所提升。

有關於 CSS text-wrap 特性更詳細的介紹,請移步閲讀《現代 CSS》中的《經典排版技術:使用 text-wrap: balance 實現文本平衡換行》課程,在這節課中還涵蓋了 CSS 中各種斷詞和換行的排版技術!

注意,這裏僅向大家介紹了 initial-lettertext-wrap 兩個排版特性,其實在 2023 年還有其他一些排版方面的特性得到 Web 平台的支持。例如:

  • F-mods:可用於 @font-face 的新特性
  • CSS 的 text-box-trim 和 text-box-edge 給排版帶來的變化
  • Web 上的可變字體
  • Web 上的彩色字體

顏色

2023年是 Web 平台的色彩之年。通過新的顏色空間和函數,可以實現動態顏色主題,你可以創建豐富、鮮豔的主題,而且還可以進行定製!

💯 高清色彩空間

URL:https://www.saji8k.com/displays/color-space/

顏色與 Web 的問題在於,CSS 並不支持高清晰度的準備工作。CSS 在 1996 年引入 Web 時,大多數計算機顯示器都非常糟糕,大多不是高清的。無論是使用 RGB 、HSL 或 HEX (十六進制)定義的顏色,都是在 sRGB 色域內,僅適用於當時的顯示器。

現在,大多數新設備都具備廣色域顯示能力,比如使用 Display P3 色域,甚至還有像 Rec.2020 更大的色域。在2023年,我們擁有了新的顏色、更多的顏色、新的顏色空間、顏色函數和新的功能。

CSS 和顏色現在可以做到:

  • 檢查用户屏幕硬件是否支持廣色域 HDR顏色
  • 檢查用户的瀏覽器是否理解像OKLCH 或 Display P3 這樣的顏色語法
  • 在 OKLAB、OKLCH、HWB、Display P3、Rec.2020、XYZ 等顏色空間中指定 HDR 顏色
  • 創建帶有 HDR 顏色的漸變
  • 在替代顏色空間中插值漸變
  • 使用 color-mix() 混合顏色
  • 使用相對顏色語法創建顏色變體

Demo 地址:https://codepen.io/web-dot-dev/full/yLZzmGM

CSS 顏色將會在哪個顏色空間中解析顏色,主要分為 sRGB 和非 sRGB 。其中在 sRGB 顏色空間中解析的顏色有:

  • 十六進制顏色
  • rgb()rgba()
  • hsl()hsla()
  • hwb()
  • 命名顏色,例如 redblack

在非 sRGB 顏色空間中解析的顏色有:

  • Lab 和 LCH:lab()lch()
  • OKLAB 和 OKLCH :oklab()oklch()
  • 任何顏色空間:color()

有關於這方面更詳細的介紹,可以參閲:

  • 現代 CSS 中的顏色格式:RGB、HSL、HWB、LAB 和 LCH
  • 新的 CSS 顏色空間:為 Web 設置高清顏色
  • CSS 中的 OKLAB 和 OKLCH

💯 color-mix() 函數

混合顏色是一項經典任務,在 2023 年,CSS 也可以做到這一點。你不僅可以為顏色混合白色或黑色,還可以混合透明度,並且可以在你選擇的任何顏色空間中執行所有這些操作。相對於傳統的顏色混合方法(比如,簡單平均值、線性加權平均值等),color-mix() 函數提供了更靈活的混合方式。除此之外,它可以在不同的顏色空間進行混合,這在調整顏色時更加靈活,並且可以根據需要選擇合適的顏色空間進行混合。

你可以把 color-mix() 想象成漸變中的一個時間點。在漸變中,我們可以看到從藍色到白色的所有路段,而 color-mix() 只顯示其中的一個步驟。當你開始考慮顏色空間並學習混合顏色空間與結果有多大差異時,事情就變得更加複雜。

URL: https://color-mix.style/

有關於 color-mix() 更詳細的介紹,請移步閲讀《現代 CSS》中的《CSS 的混合顏色:color-mix()》課程!

👁️ 相對顏色語法

CSS 顏色模塊 Level 5 為 CSS 顏色函數引入相對顏色語法,進一步增強了顏色函數功能。此語法允許你基於另一個顏色定義新顏色。你可以通過首先使用 from 關鍵字定義起始顏色,然後像往常一樣在顏色函數中指定新顏色的通道來使用它。

當你提供起始顏色時,你就可以訪問“通道關鍵字”,這些關鍵字允許你在顏色空間中引用每個通道。關鍵字取決於你使用的顏色函數。對於 rgb(),你將有 rgb 通道關鍵字。對於 oklch(),你將有 lch 關鍵字。對於每個顏色函數,你還有一個透明通道關鍵字,它引用起始顏色的 Alpha 通道。例如:

:root {
    --theme-primary: #8832CC;
}
.bg-primary-100 {
    background-color: hsl(from var(--theme-primary) h s 90%);
}

.bg-primary-200 {
    background-color: hsl(from var(--theme-primary) h s 80%);
}

.bg-primary-300 {
    background-color: hsl(from var(--theme-primary) h s 70%);
}

.bg-primary-400 {
    background-color: hsl(from var(--theme-primary) h s 60%);
}

拿其中的 hsl(from var(--theme-primary) h s 30%) 為例吧:

Demo 地址:https://codepen.io/airen/full/RweBMeY

相對顏色語法 (RCS) 是對 color-mix() 的補充,用於創建顏色變體。它比 color-mix() 更強大,但同時也是不同的顏色處理策略。color-mix() 可能會混入白色來調亮顏色,通過 RCS 可以精確訪問亮度通道,並能夠在通道上使用 calc(),以編程方式降低或提高亮度。

Demo 地址:https://codepen.io/web-dot-dev/full/YzBrmdB

RCS 可讓你對顏色進行相對和絕對處理。相對變化是指採用當前的飽和度或亮度值,並使用 calc() 進行修改。絕對變化是指將渠道值替換為全新的值,例如將不透明度設置為 50%。此語法可為時間變體等提供有意義的主題設置工具。

有關於相對顏色語法(RCS)更詳細的介紹,請移步閲讀《現代 CSS》中的《現代 CSS 中的顏色格式:RGB、HSL、HWB、LAB 和 LCH》課程中的相對顏色語法!

除此之外,你還可以使用 CSS 的 accent-color 和 color-scheme 來定製 Web 控件的 UI 顏色,以及使用 color-contrast() 函數設置顏色的對比度,提高用户體驗!

👁️ 響應淺色或深色模式的 light-dark() 函數

為了根據使用的是淺色模式還是深色模式來更改顏色,通常會使用 prefers-color-scheme 媒體查詢。為了讓事情變得更簡單,CSS 現在提供了一個名為 light-dark() 的實用函數。該函數接受兩個顏色值作為參數。根據你當前使用的顏色方案,它將輸出第一個或第二個參數。

根據規範:

如果使用的顏色方案是淺色或未知的,該函數計算為第一個顏色的計算值;如果使用的顏色方案是深色,則計算為第二個顏色的計算值。

使用的顏色方案不僅基於用户的淺色或深色模式設置,還基於 color-scheme 屬性的值。這類似於 System Colors 的計算方式。

color-scheme 屬性允許元素指示其設計為使用哪些顏色方案進行渲染。這些值與用户的偏好進行協商,最終形成一個使用的顏色方案。這意味着,要使 light-dark() 生效,你必須還包含一個 color-scheme 聲明。

:root {
    color-scheme: light dark;
}

:root {
    --text-color: light-dark(#333, #ccc); /* 在淺色模式下返回第一個值。在深色模式下返回第二個值。 */
}

由於 color-scheme 被考慮在內,這也意味着你可以在每個元素上覆蓋它的值,以強制其進入特定模式:

.dark {
    color-scheme: dark; /* light-dark() 在此元素及其子元素上始終返回 dark */
}

相比而言,使用 light-dark() 函數要比使用 prefers-color-scheme 媒體查詢要簡單和方便得多。

響應式設計

@Una Kravets 在 2021 的 Google I/O 大會上的分享 提出新的響應式設計:組件驅動式 Web 設計(Component-Driven Web Design) 。Web 生態即將進入響應式 Web 設計的新時代,並轉變我們對其含義的看法,也為會 Web 設計帶來新的變化。組件式驅動 Web 設計(或開發)也被稱為是下一代響應式 Web 設計

也就是説,我們又一次見證了響應式設計生態系統的演變,即 CSS 新增的特性將直接基於組件而不是基於頁面注入樣式響應能力。這種能力被稱為 組件驅動 Web 設計(Component-Driven Web Design) ,基於組件驅動的開發將會成為一種真正流行的開發模式。

我在《現代 Web 佈局》中也專門花了一節課的篇幅與大家一起探討了這方面的話題:《下一代響應式 Web 設計:組件驅動式 Web 設計》!

時隔兩年之後,也就是 2023 年,可以説是響應式 Web 設計的一個突破性年份。這一年促成了多項新功能,這些功能徹底改變了我們構建響應式 Web 的方式,並開創了基於組件的響應式設計新模式。CSS 容器查詢(尺寸查詢、樣式查詢和狀態查詢)和 :has() 選擇器的結合支持組件根據其父元素的大小以及任何子元素的存在或狀態擁有其自己的響應式和邏輯樣式。這意味着你終於可以將頁面級別的佈局(宏觀佈局)與組件級別的佈局(微觀佈局)分開,並只需編寫一次邏輯即可在所有地方使用你的組件!

💯 容器查詢之尺寸查詢

容器查詢允許你根據元素容器的大小、計算樣式和狀態來應用樣式。其最大的特點是: 容器查詢允許開發者定義任何一個元素為包含上下文,查詢容器的後代元素可以根據查詢容器的大小或計算樣式、狀態的變化來改變風格

換句話説,一個查詢容器是通過使用容器類型屬性(container-typecontainer)來指定其查詢類型。同時,查詢容器的後代元素的樣式規則可以通過使用 @container 條件組規則進行獨立設置。簡單地説,查詢容器(也被稱為 CSS 包容)提供了一種方法來隔離頁面的各個部分,並向瀏覽器聲明這些部分在樣式和佈局方面與頁面的其他部分是獨立的

容器查詢最早是隻有尺寸查詢,但隨着時間的推移,容器查詢新增了樣式查詢和狀態查詢。也就是説,容器查詢包含三種類型:

  • 尺寸查詢:根據查詢容器大小來調整其後代元素的樣式
  • 樣式查詢:根據查詢容器樣式或 CSS 變量來調整其後代元素的樣式
  • 狀態查詢:根據查詢容狀態來調整其後代元素的樣式

其中尺寸查詢支持查詢父元素的尺寸,而不是使用視口(瀏覽器視窗)的全局尺寸信息來應用 CSS 樣式。這意味着,你可以在多個佈局和多個視圖中為組件設置動態樣式。

如需使用此功能,請先在要查詢的元素上設置元素,然後與媒體查詢類似,使用帶有尺寸參數的 @container 來應用樣式。除了容器查詢,你還可以獲得容器查詢大小。在以下演示中,容器查詢大小 cqi(表示內嵌容器的大小)用於調整卡片標頭的大小。

Demo 地址:https://codepen.io/web-dot-dev/full/eYxBNQy

如果你對該特性感興趣,可以移步閲讀《下一代響應式 Web 設計:容器查詢》和《CSS 容器查詢之尺寸查詢》。注意,上成提到的 cqi 是容器查詢單位,它有點類似於視窗單位,這方面更詳細的介紹,可以參閲《現代 CSS 中的相對單位》。

👁️ 容器查詢之樣式查詢

樣式查詢與尺寸查詢是非常相似的,你可以使用樣式查詢查詢容器的計算樣式,但通過 style() 函數來區分尺寸查詢,style() 函數接受任何有效的樣式聲明:

@container style(font-style: italic) {
    em {
        background: var(--highlight);
        color: var(--highlight-text);
    }
}

@container style(--button: pill) {
    button {
        border-radius: 50%;
        padding-inline: 1em;
    }
}

@container colors style(background-color: black) {
    a:any-link {
        color: var(--link-on-dark);
    }
}

注意,樣式查詢的條件(即 style() 函數中的聲明)比較的是計算值。

很多時候,樣式查詢也同樣能達到尺寸查詢相同的結果:

也就是説,你可以在使用 @container style() 時查詢父元素上的自定義屬性的值。例如,查詢自定義屬性值是否存在,或者是否設置為特定的值,例如 @container style(--rain: true)

Demo 地址:https://codepen.io/web-dot-dev/full/mdvBgqv

儘管這聽起來類似於在 CSS 中使用類名稱,但樣式查詢具有一些優勢。首先,對於樣式查詢,您可以根據偽狀態的需要更新 CSS 中的值。此外,在未來版本的實現中,你將能夠查詢值的範圍(例如 style(60 <= --weather <= 70))和根據屬性值對(例如 style(font-style: italic))來確定應用的樣式。

有關於樣式查詢更詳細的介紹,請移步閲讀《現代 CSS》中的《CSS 容器查詢之樣式查詢和狀態查詢》課程!

👁️ 容器查詢之狀態查詢

@Una Kravets 在 2023 年的 CSS Day 中分享了一個“CSS 社區現狀”相關的話題,主題內容中就有 CSS 容器查詢的狀態查詢相關的話題。

事實上,CSS 的狀態查詢和 CSS 樣式查詢有點類似,我們主要使用 state() 函數來區分 CSS 容器查詢中的尺寸查詢和樣式查詢。例如:

@container state(dir: rtl) {
    /* RTL 佈局 */
}

不過大家需要知道的是,到目前為止,CSS 的狀態查詢還僅是 Chromium 團隊正在試驗的一種新的查詢類型。

💯 更新媒體查詢

藉助 update 媒體查詢,你可以使界面適應設備的刷新率。該功能可以報告 fastslownone 值,這些值與不同設備的功能相關。

你設計的大多數設備都可能具有較快的刷新率。這包括桌面設備和大多數移動設備。電子閲讀器以及低能耗付款系統等設備的刷新率可能會較慢。瞭解設備無法處理動畫或頻繁更新,意味着你可以節省電池電量或減少錯誤的視圖更新。

Demo 地址:https://codepen.io/web-dot-dev/full/wvNrVNa

在製作動畫時,如果你想針對網絡慢的用户減少運動,這個媒體查詢就非常有用。另外,它常和 prefers-reduced-motion: reduce 媒體查詢一起使用,旨在為開啓減少運動偏好的用户提供不同的動畫效果:

@media (prefers-reduced-motion: reduce), (update: slow)  {
    *,
    ::before,
    ::after {
        animation-delay: -1ms !important;
        animation-duration: 1ms !important;
        animation-iteration-count: 1 !important;
        background-attachment: initial !important;
        scroll-behavior: auto !important;
        transition-duration: 0s !important;
        transition-delay: 0s !important;
    }
 }
如果你對這方面感興趣,尤其是如何製作可訪問 Web 動畫方面感興趣,可以移步閲讀《Web 動畫之旅》的《提升可訪問性動畫的關鍵技巧》!

💯 腳本媒體查詢

腳本媒體查詢(scripting)可用於檢查 JavaScript 是否可用。這對於漸進式增強非常有用。在此媒體查詢推出之前,一個用於檢測 JavaScript 是否可用的策略是在 HTML 中放置一個 nojs 類,然後使用 JavaScript 將其移除。這些腳本可以移除,因為 CSS 現在可以檢測 JavaScript 並進行相應調整。

.steam {
    transition: opacity .2s ease;
}

@media (scripting: none) {
    .steam {
        opacity: 0;
    }
}

@media (scripting: enabled) {
    .steam {
        opacity: 1;
    }
}

Demo 地址:https://codepen.io/web-dot-dev/full/ExrwqrQ

假設有一個網站上的主題切換,由於沒有 JavaScript 可用,腳本媒體查詢有助於使切換符合系統偏好設置。或者考慮使用 switch 組件 —— 如果 JavaScript 可用,那麼只需通過手勢來滑動開關,而不必打開和關閉。如果腳本可用,就可以有大量升級用户體驗的機會;如果停用腳本,則可以提供有意義的基礎體驗。

👁️ 降低透明度的媒體查詢

非透明界面可能會導致頭痛,或因各種類型的視覺缺陷而產生視覺上的視覺疲勞。因此,Windows、macOS 和 iOS 具有系統偏好設置,可降低或移除界面的透明度。這個針對 prefers-reduced-transparency 的媒體查詢非常適合使用其他偏好設置媒體查詢,這讓您可以既能發揮創意,又能為用户做出調整。

Demo 地址:https://codepen.io/web-dot-dev/full/RwvLXvO

在某些情況下,你可以提供一種不會將內容疊加在其他內容上的備用佈局。在其他情況下,可以將顏色的不透明度調整為不透明或幾乎不透明。@Adam Argyle 的博文中提供了更多鼓舞人心的演示,可根據用户偏好進行調整。

💯 媒體查詢範圍

在媒體查詢中,min-width (或 min-height)和 max-width (或 max-height)等稱為查詢範圍。只是使用 min-max- 時常令人感到困惑,至少我自己有這種困惑:“使用 min-widthmax-width 很多時候傻傻分不清楚他們的範圍”。為此,我總是喜歡使用下圖來做判斷:

但是 Media Queries Level 4 規範引入了一種新的語法,它使用常見的數學比較運算符(如 <>= )來確定視窗寬度的範圍,這在語法上更有意義,同時編寫的代碼更少,更易於理解代碼。

/* 老方式的查詢範圍語法 * / 
@media ( min-width : 375px ) {
    /* 視窗寬度大於或等於 375px */ 
}

@media ( max-width : 768px ) {
    /* 視窗寬度小於或等於 768px */
}

@media ( min-width : 375px ) and ( max-width : 768px ) {
    /* 視窗寬度在 375px ~ 768px 之間 */ 
}

/* 新式的查詢範圍語法 */ 
@media (width >= 375px) {
    /* 視窗寬度大於或等於 375px */ 
}

@media (width <= 768px) { 
    /* 視窗寬度小於或等於 768px */
}  

@media (375px <= width <= 768px) { 
    /* 視窗寬度在 375px ~ 768px 之間 */ 
} 

Media Queries Level 4 規範中最大的變化是我們有了比較值而不是組合值的新操作符:

  • < :計算一個值是否小於另一個值
  • > :計算一個值是否大於另一個值
  • = :計算一個值是否等於另一個值
  • >= :計算一個值是否大於或等於另一個值
  • <= :計算一個值是否小於或等於另一個值

有了這些新的操作符之後,CSS 媒體查詢的語法規則也相應的變成下圖這樣:

通常,當我們編寫 CSS 媒體查詢時,我們會創建一個所謂的斷點,即一個設計“中斷”的條件,並應用一組樣式來修復它。一個設計可以有一堆斷點!它們通常基於兩個寬度之間的視窗:斷點開始的地方和斷點結束的地方。

新語法除了語法的視覺差異之外,它們的工作方式也略有不同。使用 min-max- 相當於使用數學比較運算符:

  • max-width 相當於 <= 運算符,例如,(max-width: 320px)(width <= 320px) 相同
  • min-width 相當於 >= 運算符,例如,(min-width: 320px)(width >= 320px) 相同

注意,兩者都不等同於 >< 操作符。

有關於 CSS 媒體查詢更詳細的介紹,請移步閲讀《現代 CSS》中的《CSS 媒體查詢新特性:@media 》!

交互動畫

交互動畫是數字體驗的基石。它幫助用户獲取他們點擊的內容以及他們在虛擬空間中的位置的反饋。2023 年,有許多令人興奮的功能陸續推出,使得交互動畫更容易組織和實施,實現了平滑的用户體驗和更精緻的網絡體驗。

我自己正在編寫一本與 Web 動畫相關的小冊,如果你對 Web 動畫感興趣的話,可以關注《Web 動畫之旅》!

👁️ 視圖過渡

視圖過渡會對 Web 的用户體驗產生巨大影響。藉助 CSS View Transitions API,你可以在單頁應用的兩個頁面狀態之間創建視覺過渡。這些轉換可以是整頁轉換,也可以是網頁上某些較小的轉換,例如向列表中添加或移除新項。

CSS View Transitions API 的核心是 document.startViewTranstion 函數。傳入將 DOM 更新為新狀態的函數,此 API 會為你處理所有工作。為此,它會拍攝前後快照,然後在兩者之間轉換。使用 CSS,你可以控制捕獲的內容,並視需要自定義這些快照的動畫呈現方式。

:root {
    view-transition-name: none;
}
::view-transition-group(*) {
    animation-duration: 0.5s;
}

Demo 地址:https://codepen.io/web-dot-dev/full/ExrLGJx

有關於視圖過渡更詳細的介紹,請移步閲讀《Web 動畫之旅》的《使用 CSS 視圖過渡創造流暢的界面動效》;或者參閲《現代 CSS》中的《解鎖 CSS View Transitions API 的魔力》!

👁️ 滾動驅動動效

滾動驅動的動畫是從 Chrome 115 開始提供的一項令人興奮的功能。它們允許你將現有的 CSS 動畫或使用 Web Animations API 構建的動畫與滾動器的滾動偏移關聯起來。當你上下滾動,或者在水平滾動器中左右滾動時,關聯的動畫將直接響應地前後倒播。

使用 ScrollTimeline,你可以跟蹤滾動器的整體進度,正如以下演示所示。當你滾動到頁面末尾時,文本會逐個字符地顯示出來。

Demo 地址:https://codepen.io/web-dot-dev/full/JjxvwqG

使用 ViewTimeline,你可以跟蹤元素在滾動視口中的移動。這類似於 IntersectionObserver 跟蹤元素的方式。在下面的演示中,每個圖像在進入滾動視口的瞬間開始逐漸顯現,直到位於中心位置。

Demo 地址:https://codepen.io/web-dot-dev/full/XWOqovr

由於滾動驅動的動畫與 CSS 動畫和 Web Animations API 一起工作,你可以從這些 API 帶來的所有優勢中受益。這包括能夠使這些動畫脱離主線程運行。現在,您只需添加幾行額外的代碼,即可擁有絲滑順滑的動畫、由滾動操作驅動、在主線程之外運行,還有什麼不喜歡呢?

通過 CSS 應用滾動驅動的動畫時,查找控制滾動條的查詢機制始終向上遍歷 DOM 樹,因而僅限於滾動祖先實體。但通常情況下,需要添加動畫效果的元素不是滾動條的子元素,而是位於完全不同的子樹中的元素。

若要允許動畫元素查找非祖先實體的已命名滾動時間軸,請對共享父項使用 timeline-scope 屬性。這樣一來,就可以將具有該名稱的已定義 scroll-timelineview-timeline 附加到其中,從而賦予其更廣泛的範圍。這樣,該共享父級的任何子級都可以使用具有該名稱的時間軸。

在共享父級上聲明 timeline-scope 後,滾動條上聲明的 scroll-timeline 可供使用該滾動條作為 animation-timeline 的元素找到。

現如今天,我們只需要使用純 CSS 就可以實現。將 CSS 的滾動捕捉、自定義屬性和滾動驅動動效相結合,就可以實現一個視差滾動的動畫效果:

Demo 地址:https://codepen.io/airen/full/YzBMgQB

上面這個示例還帶有響應式功能,當你將瀏覽器屏幕縮小到小於 768px 時,頁面也可以整屏幕滾動,帶視差效果更明顯:

有關於 CSS 滾動驅動動效更詳細的介紹,請移步閲讀《Web 動畫之旅》的《CSS 滾動驅動動效的藝術》;或者參閲《現代 CSS》中的《CSS 滾動驅動動效》!

👁️ 離散屬性動畫

2023 年的另一個新功能是能夠對離散屬性進行動畫處理,例如在 display: nonedisplay: block 之間進行動畫處理。從 Chrome 116 開始,你可以在關鍵幀規則中使用 displaycontent-visibility。你還可以在 50% 的位置而不是 0% 的位置上過渡任何離散屬性。這是通過使用 transition-behavior 屬性的 allow-discrete 值,或在 transition 屬性中作為速記方式來實現的。

transition-behavior 屬性值 allow-discrete ,允許對離散屬性進行一些平滑的過渡。這使得在離散屬性上應用過渡變得更加靈活。這意味着,使用 allow-discrete 模式,你可以實現對離散屬性的平滑過渡,而不是突然切換。這對於一些需要在離散屬性上應用過渡的情況非常有用,使得在這些屬性上創建更加流暢的動畫效果成為可能。

@layer transition {
    .card {
        transition: opacity 0.25s, display 0.25s;
        transition-behavior: allow-discrete;
    }

    .fade-out {
        opacity: 0;
        display: none;
    }
}

Demo 地址:https://codepen.io/airen/full/xxMxrgy

有關於離散屬性動畫更詳細的介紹,可以在閲《Web 動畫之旅》的《幀動畫與過渡動畫:誰更適合你的業務場景》!

👁️ @starting-style

上圖來源於 @XboxYan

@starting-style 是 CSS 新增的一個 @ 規則,該規則藉助新的 Web 功能,用於實現對 display: none 的元素進行入場和出場的動畫。該規則提供了一種在元素在頁面上打開之前,瀏覽器可以查找的“打開之前(before-open)”樣式的方式。這對於入場動畫以及對彈出窗口或對話框等元素進行動畫處理非常有用。它還可用於在創建元素並希望賦予其動畫效果時使用。以下示例將 popover 屬性以動畫形式呈現,使其從視口外平滑進入視圖並進入頂層。

/*   IS-OPEN STATE   */
dialog[open] {
    translate: 0 0;
}

/*   EXIT STATE   */
dialog {
    transition: 
        translate 0.7s ease-out, 
        overlay 0.7s ease-out, 
        display 0.7s ease-out allow-discrete;
    translate: 0 100vh;
}

/*   0. BEFORE-OPEN STATE   */
@starting-style {
    dialog[open] {
        translate: 0 100vh;
    }
}

Demo 地址:https://codepen.io/web-dot-dev/full/OJdjjdX

有關於 @starting-style 更詳細的介紹,可以參閲《Web 動畫之旅》的《幀動畫與過渡動畫:誰更適合你的業務場景》!

👁️ overlay

新的CSS overlay 屬性可以添加到你的過渡中,讓具有頂層樣式(例如 popover 和 dialog)的元素平滑地從頂層動畫移出。如果你沒有使用 overlay 過渡,你的元素將立即返回到被裁剪、變換和覆蓋的狀態,你將看不到過渡發生。類似地,將 overlay 添加到頂層元素時,::backdrop 也可以平滑地動畫移出。

Demo 地址:https://codepen.io/web-dot-dev/full/zYeJbgw

如果你對進場和退場動畫感興趣,或者説想製作一個流暢的進場和退場動畫,請參閲 @Una Kravets 和 @Joey Arhar 的教程《四個新的 CSS 功能,可實現流暢的進入和退出動畫》。

👁️ 錨點定位

CSS 錨點定位(CSS Anchor Positioning)是一種用於在 Web 頁面上定位元素的新方法,它是 CSS 的一個新特性。W3C 規範是這樣描述的:“通過錨點定位(通過錨定位函數 anchor()anchor-size())可以將一個絕對定位的元素與頁面上的一個或多個其他元素錨定在一起,同時還允許他們嘗試多種可能的位置,以找到避免重疊和溢出的最佳位置”。

簡單地説,CSS 錨點定位提高了元素絕對定位的能力,Web 開發者可以使用一種更簡單、更自然的方式來定位元素之間的關係,使得頁面元素能夠根據其包含塊內的其他元素的位置和大小進行定位和調整。該功能的出現為 Web 開發人員提供了更多的控制權和靈活性,同時減少了對 JavaScript 依賴,使頁面的性能更加優化。

CSS 錨點定位通過引入一組屬性和值,使元素能夠與彼此連接,為 Web 佈局中的定位提供了一種全新的範式:

  • 首先,使用 anchor-name 來定義一個錨點,經過標記的元素會作為絕對定位的基準目標
  • 其次,將 anchor()anchor-size() 函數用作被定位元素的內嵌屬性(toprightbottomleft 或它們的邏輯等效屬性)的值
  • 最後,使用 @position-fallback 規則為錨點定位提供回退機制,即設置多套不同的錨點定位規則,以適應更為複雜的 Web 佈局

以往要實現熔岩燈菜單效果,我們不得不依賴 JavaScript 腳本來完成。現在,我們可以使用 CSS 錨點定位來實現它。例如:

Demo 地址:https://codepen.io/airen/full/RwEpMxM

如果你想了解 CSS 錨點定位更多的內容,可以移步閲讀《現代 CSS》中的《CSS 錨點定位:探索下一代 Web 佈局》!

👁️ 動畫合成

W3C 規範是這樣描述 CSS 合成動畫的:

The animation-composition property defines the composite operation used when multiple animations affect the same property simultaneously.

大致的意思是説:“animation-composition 屬性定義了在多個動畫同時影響同一屬性時所使用的複合操作”。

換句話説,CSS 動畫合成(animation-composition)是指同時使用多個 CSS 動畫效果來影響同一屬性時,通過一些屬性和方法來控制這些動畫如何組合影響動畫元素的屬性值。這使得在多個動畫效果同時作用於元素時,可以更加靈活地控制動畫的表現方式,包括屬性值的疊加、替換等。這為 Web 開發人員提供了更多的控制權,以創建複雜的動畫效果,而不必依賴於單一的動畫規則。

有關於 animation-composition 更詳細的介紹,可以參閲《Web 動畫之旅》的《多個 CSS 動畫與動畫合成:創造更復雜的動畫效果》,也可以移步閲讀 《現代 CSS》中的《CSS 動畫合成:animation-composition》!

👁️ 緩動函數 linear()

不要讓這個函數的名字誤導你。linear() 函數(不要與 linear 關鍵字混淆)允許你以簡單的方式創建複雜的緩動函數,以犧牲一些精度為代價。

在引入 linear() 之前(在 Chrome 113 中發佈),在 CSS 中創建彈跳或彈簧效果是不可能的。由於有了 linear(),可以通過將這些緩動函數簡化為一系列點,然後在這些點之間進行線性插值來近似這些緩動效果。

:root {
  --linear: linear(0, 1);
  --quad-in: linear( 0, 0.0039, 0.0156, 0.0352, 0.0625, 0.0977, 0.1407, 0.1914, 0.2499, 0.3164, 0.3906 62.5%, 0.5625, 0.7656, 1 );
  --quad-out: linear( 0, 0.2342, 0.4374, 0.6093 37.49%, 0.6835, 0.7499, 0.8086, 0.8593, 0.9023, 0.9375, 0.9648, 0.9844, 0.9961, 1 );
  --quad-in-out: linear( 0, 0.0027, 0.0106 7.29%, 0.0425, 0.0957, 0.1701 29.16%, 0.2477, 0.3401 41.23%, 0.5982 55.18%, 0.7044 61.56%, 0.7987, 0.875 75%, 0.9297, 0.9687, 0.9922, 1 );
  --power-1-in: linear( 0, 0.0039, 0.0156, 0.0352, 0.0625, 0.0977, 0.1407, 0.1914, 0.2499, 0.3164, 0.3906 62.5%, 0.5625, 0.7656, 1 );
  --power-1-out: linear( 0, 0.2342, 0.4374, 0.6093 37.49%, 0.6835, 0.7499, 0.8086, 0.8593, 0.9023, 0.9375, 0.9648, 0.9844, 0.9961, 1 );
  --power-1-in-out: linear( 0, 0.0027, 0.0106 7.29%, 0.0425, 0.0957, 0.1701 29.16%, 0.2477, 0.3401 41.23%, 0.5982 55.18%, 0.7044 61.56%, 0.7987, 0.875 75%, 0.9297, 0.9687, 0.9922, 1 );
  --cubic-in: linear( 0, 0.0014 11.11%, 0.0071 19.24%, 0.0188 26.6%, 0.037 33.33%, 0.0634 39.87%, 0.0978 46.07%, 0.1407 52.02%, 0.1925 57.74%, 0.2559 63.49%, 0.3295 69.07%, 0.4135 74.5%, 0.5083 79.81%, 0.6141 85%, 0.7312 90.09%, 1 );
  --cubic-out: linear( 0, 0.2688 9.91%, 0.3859 15%, 0.4917 20.19%, 0.5865 25.5%, 0.6705 30.93%, 0.7441 36.51%, 0.8075 42.26%, 0.8593 47.98%, 0.9022 53.93%, 0.9366 60.13%, 0.963 66.67%, 0.9812 73.4%, 0.9929 80.76%, 0.9986 88.89%, 1 );
  --cubic-in-out: linear( 0, 0.0036 9.62%, 0.0185 16.66%, 0.0489 23.03%, 0.0962 28.86%, 0.1705 34.93%, 0.269 40.66%, 0.3867 45.89%, 0.5833 52.95%, 0.683 57.05%, 0.7829 62.14%, 0.8621 67.46%, 0.8991 70.68%, 0.9299 74.03%, 0.9545 77.52%, 0.9735 81.21%, 0.9865 85%, 0.9949 89.15%, 1 );
  --power-2-in: linear( 0, 0.0014 11.11%, 0.0071 19.24%, 0.0188 26.6%, 0.037 33.33%, 0.0634 39.87%, 0.0978 46.07%, 0.1407 52.02%, 0.1925 57.74%, 0.2559 63.49%, 0.3295 69.07%, 0.4135 74.5%, 0.5083 79.81%, 0.6141 85%, 0.7312 90.09%, 1 );
  --power-2-out: linear( 0, 0.2688 9.91%, 0.3859 15%, 0.4917 20.19%, 0.5865 25.5%, 0.6705 30.93%, 0.7441 36.51%, 0.8075 42.26%, 0.8593 47.98%, 0.9022 53.93%, 0.9366 60.13%, 0.963 66.67%, 0.9812 73.4%, 0.9929 80.76%, 0.9986 88.89%, 1 );
  --power-2-in-out: linear( 0, 0.0036 9.62%, 0.0185 16.66%, 0.0489 23.03%, 0.0962 28.86%, 0.1705 34.93%, 0.269 40.66%, 0.3867 45.89%, 0.5833 52.95%, 0.683 57.05%, 0.7829 62.14%, 0.8621 67.46%, 0.8991 70.68%, 0.9299 74.03%, 0.9545 77.52%, 0.9735 81.21%, 0.9865 85%, 0.9949 89.15%, 1 );
  --quart-in: linear( 0, 0.0039 25%, 0.0117 32.89%, 0.0248 39.68%, 0.0457 46.22%, 0.0743 52.21%, 0.1113 57.77%, 0.1575 63%, 0.218 68.33%, 0.2901 73.39%, 0.3745 78.23%, 0.4718 82.88%, 0.5827 87.37%, 0.7074 91.71%, 0.8462 95.91%, 1 );
  --quart-out: linear( 0, 0.1538 4.09%, 0.2926 8.29%, 0.4173 12.63%, 0.5282 17.12%, 0.6255 21.77%, 0.7099 26.61%, 0.782 31.67%, 0.8425 37%, 0.8887 42.23%, 0.9257 47.79%, 0.9543 53.78%, 0.9752 60.32%, 0.9883 67.11%, 0.9961 75%, 1 );
  --quart-in-out: linear( 0, 0.0029 13.8%, 0.0184 21.9%, 0.0339 25.51%, 0.0551 28.81%, 0.0827 31.88%, 0.1168 34.76%, 0.1962 39.57%, 0.3005 44.02%, 0.4084 47.53%, 0.6242 53.45%, 0.7493 57.93%, 0.8495 62.97%, 0.8888 65.67%, 0.9213 68.51%, 0.9629 73.9%, 0.9876 80.16%, 0.998 87.5%, 1 );
  --power-3-in: linear( 0, 0.0039 25%, 0.0117 32.89%, 0.0248 39.68%, 0.0457 46.22%, 0.0743 52.21%, 0.1113 57.77%, 0.1575 63%, 0.218 68.33%, 0.2901 73.39%, 0.3745 78.23%, 0.4718 82.88%, 0.5827 87.37%, 0.7074 91.71%, 0.8462 95.91%, 1 );
  --power-3-out: linear( 0, 0.1538 4.09%, 0.2926 8.29%, 0.4173 12.63%, 0.5282 17.12%, 0.6255 21.77%, 0.7099 26.61%, 0.782 31.67%, 0.8425 37%, 0.8887 42.23%, 0.9257 47.79%, 0.9543 53.78%, 0.9752 60.32%, 0.9883 67.11%, 0.9961 75%, 1 );
  --power-3-in-out: linear( 0, 0.0029 13.8%, 0.0184 21.9%, 0.0339 25.51%, 0.0551 28.81%, 0.0827 31.88%, 0.1168 34.76%, 0.1962 39.57%, 0.3005 44.02%, 0.4084 47.53%, 0.6242 53.45%, 0.7493 57.93%, 0.8495 62.97%, 0.8888 65.67%, 0.9213 68.51%, 0.9629 73.9%, 0.9876 80.16%, 0.998 87.5%, 1 );
  --quint-in: linear( 0, 0.0024 29.91%, 0.008 38.03%, 0.0179 44.72%, 0.035 51.16%, 0.0595 56.88%, 0.0922 62.08%, 0.1338 66.88%, 0.1914 71.85%, 0.262 76.5%, 0.3461 80.88%, 0.4447 85.04%, 0.5587 89.01%, 0.689 92.82%, 0.8359 96.48%, 1 );
  --quint-out: linear( 0, 0.1641 3.52%, 0.311 7.18%, 0.4413 10.99%, 0.5553 14.96%, 0.6539 19.12%, 0.738 23.5%, 0.8086 28.15%, 0.8662 33.12%, 0.9078 37.92%, 0.9405 43.12%, 0.965 48.84%, 0.9821 55.28%, 0.992 61.97%, 0.9976 70.09%, 1 );
  --quint-in-out: linear( 0, 0.0012 14.95%, 0.0089 22.36%, 0.0297 28.43%, 0.0668 33.43%, 0.0979 36.08%, 0.1363 38.55%, 0.2373 43.07%, 0.3675 47.01%, 0.5984 52.15%, 0.7121 55.23%, 0.8192 59.21%, 0.898 63.62%, 0.9297 66.23%, 0.9546 69.06%, 0.9733 72.17%, 0.9864 75.67%, 0.9982 83.73%, 1 );
  --power-4-in: linear( 0, 0.0024 29.91%, 0.008 38.03%, 0.0179 44.72%, 0.035 51.16%, 0.0595 56.88%, 0.0922 62.08%, 0.1338 66.88%, 0.1914 71.85%, 0.262 76.5%, 0.3461 80.88%, 0.4447 85.04%, 0.5587 89.01%, 0.689 92.82%, 0.8359 96.48%, 1 );
  --power-4-out: linear( 0, 0.1641 3.52%, 0.311 7.18%, 0.4413 10.99%, 0.5553 14.96%, 0.6539 19.12%, 0.738 23.5%, 0.8086 28.15%, 0.8662 33.12%, 0.9078 37.92%, 0.9405 43.12%, 0.965 48.84%, 0.9821 55.28%, 0.992 61.97%, 0.9976 70.09%, 1 );
  --power-4-in-out: linear( 0, 0.0012 14.95%, 0.0089 22.36%, 0.0297 28.43%, 0.0668 33.43%, 0.0979 36.08%, 0.1363 38.55%, 0.2373 43.07%, 0.3675 47.01%, 0.5984 52.15%, 0.7121 55.23%, 0.8192 59.21%, 0.898 63.62%, 0.9297 66.23%, 0.9546 69.06%, 0.9733 72.17%, 0.9864 75.67%, 0.9982 83.73%, 1 );
  --strong-in: linear( 0, 0.0024 29.91%, 0.008 38.03%, 0.0179 44.72%, 0.035 51.16%, 0.0595 56.88%, 0.0922 62.08%, 0.1338 66.88%, 0.1914 71.85%, 0.262 76.5%, 0.3461 80.88%, 0.4447 85.04%, 0.5587 89.01%, 0.689 92.82%, 0.8359 96.48%, 1 );
  --strong-out: linear( 0, 0.1641 3.52%, 0.311 7.18%, 0.4413 10.99%, 0.5553 14.96%, 0.6539 19.12%, 0.738 23.5%, 0.8086 28.15%, 0.8662 33.12%, 0.9078 37.92%, 0.9405 43.12%, 0.965 48.84%, 0.9821 55.28%, 0.992 61.97%, 0.9976 70.09%, 1 );
  --strong-in-out: linear( 0, 0.0012 14.95%, 0.0089 22.36%, 0.0297 28.43%, 0.0668 33.43%, 0.0979 36.08%, 0.1363 38.55%, 0.2373 43.07%, 0.3675 47.01%, 0.5984 52.15%, 0.7121 55.23%, 0.8192 59.21%, 0.898 63.62%, 0.9297 66.23%, 0.9546 69.06%, 0.9733 72.17%, 0.9864 75.67%, 0.9982 83.73%, 1 );
  --elastic-in: linear( 0, 0.0019 13.34%, -0.0056 27.76%, -0.0012 31.86%, 0.0147 39.29%, 0.0161 42.46%, 0.0039 46.74%, -0.0416 54.3%, -0.046 57.29%, -0.0357, -0.0122 61.67%, 0.1176 69.29%, 0.1302 70.79%, 0.1306 72.16%, 0.1088 74.09%, 0.059 75.99%, -0.0317 78.19%, -0.3151 83.8%, -0.3643 85.52%, -0.3726, -0.3705 87.06%, -0.3463, -0.2959 89.3%, -0.1144 91.51%, 0.7822 97.9%, 1 );
  --elastic-out: linear( 0, 0.2178 2.1%, 1.1144 8.49%, 1.2959 10.7%, 1.3463 11.81%, 1.3705 12.94%, 1.3726, 1.3643 14.48%, 1.3151 16.2%, 1.0317 21.81%, 0.941 24.01%, 0.8912 25.91%, 0.8694 27.84%, 0.8698 29.21%, 0.8824 30.71%, 1.0122 38.33%, 1.0357, 1.046 42.71%, 1.0416 45.7%, 0.9961 53.26%, 0.9839 57.54%, 0.9853 60.71%, 1.0012 68.14%, 1.0056 72.24%, 0.9981 86.66%, 1 );
  --elastic-in-out: linear( 0, 0.0009 8.51%, -0.0047 19.22%, 0.0016 22.39%, 0.023 27.81%, 0.0237 30.08%, 0.0144 31.81%, -0.0051 33.48%, -0.1116 39.25%, -0.1181 40.59%, -0.1058 41.79%, -0.0455, 0.0701 45.34%, 0.9702 55.19%, 1.0696 56.97%, 1.0987 57.88%, 1.1146 58.82%, 1.1181 59.83%, 1.1092 60.95%, 1.0057 66.48%, 0.986 68.14%, 0.9765 69.84%, 0.9769 72.16%, 0.9984 77.61%, 1.0047 80.79%, 0.9991 91.48%, 1 );
  --bounce-in: linear( 0, 0.0117, 0.0156, 0.0117, 0, 0.0273, 0.0468, 0.0586, 0.0625, 0.0586, 0.0468, 0.0273, 0 27.27%, 0.1093, 0.1875 36.36%, 0.2148, 0.2343, 0.2461, 0.25, 0.2461, 0.2344, 0.2148 52.28%, 0.1875 54.55%, 0.1095, 0, 0.2341, 0.4375, 0.6092, 0.75, 0.8593, 0.9375 90.91%, 0.9648, 0.9843, 0.9961, 1 );
  --bounce-out: linear( 0, 0.0039, 0.0157, 0.0352, 0.0625 9.09%, 0.1407, 0.25, 0.3908, 0.5625, 0.7654, 1, 0.8907, 0.8125 45.45%, 0.7852, 0.7657, 0.7539, 0.75, 0.7539, 0.7657, 0.7852, 0.8125 63.64%, 0.8905, 1 72.73%, 0.9727, 0.9532, 0.9414, 0.9375, 0.9414, 0.9531, 0.9726, 1, 0.9883, 0.9844, 0.9883, 1 );
  --bounce-in-out: linear( 0, 0.0078, 0, 0.0235, 0.0313, 0.0235, 0.0001 13.63%, 0.0549 15.92%, 0.0938, 0.1172, 0.125, 0.1172, 0.0939 27.26%, 0.0554 29.51%, 0.0003 31.82%, 0.2192, 0.3751 40.91%, 0.4332, 0.4734 45.8%, 0.4947 48.12%, 0.5027 51.35%, 0.5153 53.19%, 0.5437, 0.5868 57.58%, 0.6579, 0.7504 62.87%, 0.9999 68.19%, 0.9453, 0.9061, 0.8828, 0.875, 0.8828, 0.9063, 0.9451 84.08%, 0.9999 86.37%, 0.9765, 0.9688, 0.9765, 1, 0.9922, 1 );
  --expo-in: linear( 0, 0.0085 31.26%, 0.0167 40.94%, 0.0289 48.86%, 0.0471 55.92%, 0.0717 61.99%, 0.1038 67.32%, 0.1443 72.07%, 0.1989 76.7%, 0.2659 80.89%, 0.3465 84.71%, 0.4419 88.22%, 0.554 91.48%, 0.6835 94.51%, 0.8316 97.34%, 1 );
  --expo-out: linear( 0, 0.1684 2.66%, 0.3165 5.49%, 0.446 8.52%, 0.5581 11.78%, 0.6535 15.29%, 0.7341 19.11%, 0.8011 23.3%, 0.8557 27.93%, 0.8962 32.68%, 0.9283 38.01%, 0.9529 44.08%, 0.9711 51.14%, 0.9833 59.06%, 0.9915 68.74%, 1 );
  --expo-in-out: linear( 0, 0.0053 17.18%, 0.0195 26.59%, 0.0326 30.31%, 0.0506 33.48%, 0.0744 36.25%, 0.1046 38.71%, 0.1798 42.62%, 0.2846 45.93%, 0.3991 48.37%, 0.6358 52.29%, 0.765 55.45%, 0.8622 59.3%, 0.8986 61.51%, 0.9279 63.97%, 0.9481 66.34%, 0.9641 69.01%, 0.9856 75.57%, 0.9957 84.37%, 1 );
  --circ-in: linear( -0, 0.0048 9.8%, 0.0192 19.5%, 0.043 29.02%, 0.0761 38.26%, 0.1181 47.13%, 0.1685 55.56%, 0.227 63.44%, 0.2929 70.71%, 0.3656 77.3%, 0.4445 83.15%, 0.5285 88.19%, 0.6173 92.39%, 0.7099 95.7%, 0.805 98.08%, 0.9021 99.52%, 1 );
  --circ-out: linear( 0, 0.0979 0.48%, 0.195 1.92%, 0.2901 4.3%, 0.3827 7.61%, 0.4715 11.81%, 0.5555 16.85%, 0.6344 22.7%, 0.7071 29.29%, 0.773 36.56%, 0.8315 44.44%, 0.8819 52.87%, 0.9239 61.74%, 0.957 70.98%, 0.9808 80.5%, 0.9952 90.2%, 1 );
  --circ-in-out: linear( -0, 0.0033 5.75%, 0.0132 11.43%, 0.0296 16.95%, 0.0522 22.25%, 0.0808 27.25%, 0.1149 31.89%, 0.1542 36.11%, 0.1981 39.85%, 0.2779 44.79%, 0.3654 48.15%, 0.4422 49.66%, 0.5807 50.66%, 0.6769 53.24%, 0.7253 55.37%, 0.7714 58.01%, 0.8142 61.11%, 0.8536 64.65%, 0.9158 72.23%, 0.9619 80.87%, 0.9904 90.25%, 1 );
  --sine-in: linear( 0, 0.0035, 0.0141 10.7%, 0.0318 16.09%, 0.0566 21.51%, 0.0885 26.98%, 0.1278 32.53%, 0.2288 43.93%, 0.3563 55.48%, 0.5171 67.92%, 0.7139 81.53%, 1 );
  --sine-out: linear( 0, 0.2861 18.47%, 0.4829 32.08%, 0.6437 44.52%, 0.7712 56.07%, 0.8722 67.47%, 0.9115 73.02%, 0.9434 78.49%, 0.9682 83.91%, 0.9859 89.3%, 0.9965, 1 );
  --sine-in-out: linear( 0, 0.007 5.35%, 0.0282 10.75%, 0.0638 16.26%, 0.1144 21.96%, 0.1833 28.16%, 0.2717 34.9%, 0.6868 62.19%, 0.775 68.54%, 0.8457 74.3%, 0.9141 81.07%, 0.9621 87.52%, 0.9905 93.8%, 1 );
  --back-in: linear( 0, -0.0029 4.31%, -0.0119 9.02%, -0.0838 31.27%, -0.0956 36.64%, -0.1 41.45%, -0.0953 47.03%, -0.0792 52.25%, -0.0512 57.19%, -0.0111 61.92%, 0.0512 67.19%, 0.131 72.27%, 0.2284 77.18%, 0.3443 81.96%, 0.479 86.62%, 0.6329 91.17%, 0.8065 95.63%, 1 );
  --back-out: linear( 0, 0.1935 4.37%, 0.3671 8.83%, 0.521 13.38%, 0.6557 18.04%, 0.7716 22.82%, 0.869 27.73%, 0.9488 32.81%, 1.0111 38.08%, 1.0512 42.81%, 1.0792 47.75%, 1.0953 52.97%, 1.1 58.55%, 1.0956 63.36%, 1.0838 68.73%, 1.0119 90.98%, 1.0029 95.69%, 1 );
  --back-in-out: linear( 0, -0.0077 5.2%, -0.0452 16.98%, -0.0493 22.35%, -0.0418 25.57%, -0.0258 28.57%, -0.0007 31.42%, 0.0335 34.15%, 0.1242 39.03%, 0.2505 43.65%, 0.3844 47.35%, 0.656 53.68%, 0.81 58.37%, 0.9282 63.52%, 0.9719 66.23%, 1.0055 69.04%, 1.0255 71.4%, 1.0396 73.87%, 1.0477 76.48%, 1.05 79.27%, 1.0419 84.36%, 1.0059 95.49%, 1 );
  --spring-1: linear(0, 0.006, 0.025 2.8%, 0.101 6.1%, 0.539 18.9%, 0.721 25.3%, 0.849 31.5%,0.937 38.1%, 0.968 41.8%, 0.991 45.7%, 1.006 50.1%, 1.015 55%, 1.017 63.9%,1.001);
  --spring-2: linear(0, 0.007, 0.029 2.2%, 0.118 4.7%, 0.625 14.4%, 0.826 19%, 0.902, 0.962,1.008 26.1%, 1.041 28.7%, 1.064 32.1%, 1.07 36%, 1.061 40.5%, 1.015 53.4%,0.999 61.6%, 0.995 71.2%, 1);
  --spring-3: linear(0, 0.009, 0.035 2.1%, 0.141 4.4%, 0.723 12.9%, 0.938 16.7%, 1.017, 1.077,1.121, 1.149 24.3%, 1.159, 1.163, 1.161, 1.154 29.9%, 1.129 32.8%,1.051 39.6%, 1.017 43.1%, 0.991, 0.977 51%, 0.974 53.8%, 0.975 57.1%,0.997 69.8%, 1.003 76.9%, 1);
  --spring-4: linear(0, 0.009, 0.037 1.7%, 0.153 3.6%, 0.776 10.3%, 1.001, 1.142 16%, 1.185, 1.209 19%, 1.215 19.9% 20.8%, 1.199, 1.165 25%, 1.056 30.3%, 1.008 33%, 0.973,0.955 39.2%, 0.953 41.1%, 0.957 43.3%, 0.998 53.3%, 1.009 59.1% 63.7%,0.998 78.9%, 1);
  --spring-5: linear(0, 0.01, 0.04 1.6%, 0.161 3.3%, 0.816 9.4%, 1.046, 1.189 14.4%, 1.231,1.254 17%, 1.259, 1.257 18.6%, 1.236, 1.194 22.3%, 1.057 27%, 0.999 29.4%,0.955 32.1%, 0.942, 0.935 34.9%, 0.933, 0.939 38.4%, 1 47.3%, 1.011,1.017 52.6%, 1.016 56.4%, 1 65.2%, 0.996 70.2%, 1.001 87.2%, 1);
  --bounce-1: linear(0, 0.004, 0.016, 0.035, 0.063, 0.098, 0.141, 0.191, 0.25, 0.316, 0.391 36.8%,0.563, 0.766, 1 58.8%, 0.946, 0.908 69.1%, 0.895, 0.885, 0.879, 0.878, 0.879,0.885, 0.895, 0.908 89.7%, 0.946, 1);
  --bounce-2: linear(0, 0.004, 0.016, 0.035, 0.063, 0.098, 0.141 15.1%, 0.25, 0.391, 0.562, 0.765,1, 0.892 45.2%, 0.849, 0.815, 0.788, 0.769, 0.757, 0.753, 0.757, 0.769, 0.788,0.815, 0.85, 0.892 75.2%, 1 80.2%, 0.973, 0.954, 0.943, 0.939, 0.943, 0.954,0.973, 1);
  --bounce-3: linear(0, 0.004, 0.016, 0.035, 0.062, 0.098, 0.141 11.4%, 0.25, 0.39, 0.562, 0.764,1 30.3%, 0.847 34.8%, 0.787, 0.737, 0.699, 0.672, 0.655, 0.65, 0.656, 0.672,0.699, 0.738, 0.787, 0.847 61.7%, 1 66.2%, 0.946, 0.908, 0.885 74.2%, 0.879,0.878, 0.879, 0.885 79.5%, 0.908, 0.946, 1 87.4%, 0.981, 0.968, 0.96, 0.957, 0.96, 0.968, 0.981, 1);
  --bounce-4: linear(0, 0.004, 0.016 3%, 0.062, 0.141, 0.25, 0.391, 0.562 18.2%, 1 24.3%, 0.81,0.676 32.3%, 0.629, 0.595, 0.575, 0.568, 0.575, 0.595, 0.629, 0.676 48.2%,0.811, 1 56.2%, 0.918, 0.86, 0.825, 0.814, 0.825, 0.86, 0.918, 1 77.2%,0.94 80.6%, 0.925, 0.92, 0.925, 0.94 87.5%, 1 90.9%, 0.974, 0.965, 0.974, 1);
  --bounce-5: linear(0, 0.004, 0.016 2.5%, 0.063, 0.141, 0.25 10.1%, 0.562, 1 20.2%, 0.783, 0.627,0.534 30.9%, 0.511, 0.503, 0.511, 0.534 38%, 0.627, 0.782, 1 48.7%, 0.892,0.815, 0.769 56.3%, 0.757, 0.753, 0.757, 0.769 61.3%, 0.815, 0.892, 1 68.8%,0.908 72.4%, 0.885, 0.878, 0.885, 0.908 79.4%, 1 83%, 0.954 85.5%, 0.943,0.939, 0.943, 0.954 90.5%, 1 93%, 0.977, 0.97, 0.977, 1);
}

這些緩動曲線會使你的動畫更具動力感、重量感等,它們使動畫與現實生活中的物體運動又更近了一層。

Demo 地址:https://codepen.io/web-dot-dev/full/bGzMOJM

有關於 linear() 函數更詳細的介紹,可以參閲《Web 動畫之旅》的《使用 linear() 函數創建令人驚歎的動畫曲線》!

👁️ Scrollend 事件

許多界面包含滾動互動,有時界面需要同步與當前滾動位置相關的信息,或根據當前狀態提取數據。在 scrollend 事件之前,你必須使用不準確的超時方法,該方法可以在用户的手指仍在屏幕上時觸發。藉助 scrollend 事件,你可以實現精確計時的 scrollend 事件,瞭解用户是否仍在進行手勢操作。

Demo 地址:https://codepen.io/web-dot-dev/full/OJdxKqJ

這對瀏覽器擁有非常重要,因為在滾動過程中,JavaScript 無法跟蹤手指在屏幕上的存在,而相關信息就是無法獲得。現在可以刪除大量不準確的滾動結束嘗試代碼,並將其替換為瀏覽器擁有的高精確度事件。

有關於 scrollend 事件更詳細的介紹,可以參閲 @Adam Argyle 的《Scrollend(一種新的 JavaScript 事件)》!

💯 滾動捕捉

其實滾動捕捉並不是 2023 年剛得到支持的特性,但我還是將這個特性列入到這篇文章,主要原因之一,它的的確確可以幫助我們改善用户的體驗,是一個非常不錯,而且易用的特性。

CSS 滾動捕捉為我們提供了一種方法,可以在用户滾動瀏覽文檔時,將其滾動到特定的點(位置) 。這對於在移動設備上,甚至在桌面端上為某些類型的應用程序(比如 <Carousel> 件)創造一個更類似於應用程序(原生客户端)的體驗是很有幫助的。 簡而言之,CSS 滾動捕捉可以:

  • 防止滾動時出現尷尬的滾動位置 ;
  • 創建更好的滾動體驗 。

CSS 滾動捕捉相關的屬性和 CSS 的 Flexbox、Grid 屬性類似,分為作用於容器(滾動容器)屬性作用於定位子項(滾動容器子元素)屬性。其中作用於滾動容器的屬性主要有 scroll-snap-typescroll-paddingscroll-snap-stop ;作用於定位子項的屬性主要有 scroll-marginscroll-snap-align

現在,你可以將滾動捕捉和滾動驅動動畫以及自定義滾動條相結合起來,就可以不依賴任何 JavaScript 實現下圖這樣的 3D 封面效果:

Demo 地址:https://codepen.io/airen/full/yLZWJog

大家需要知道的是,除了滾動捕捉之外,還有其他的方式也可以提高用户的滾動體驗。更詳細的可以參閲《防禦式 CSS 精講》中的《CSS 如何改善滾動體驗》、《美化滾動條 UI:自定義滾動條 UI》和《CSS 的滾動捕捉》!

往期回顧

首先我要聲明的是,前面所介紹的內容是我自己覺得 2023 年 CSS 特性發展最快的部分!另外,上面內容有些是摘取 Chrome 團隊剛發佈的《CSS Wrapped: 2023!》,但我捨棄了組件部分的介紹,主要原因這部分內容更多的是與 HTML 有關。

其實,自 2020 年,我個人基本上有一個慣例,那就是每年都會發一篇有關於 CSS 新特性相關的總結性文章。如果你對往期內容感興趣,可以閲讀下面這些文章:

  • 2020 年 CSS 有哪些新特性
  • 應用於下一代 Web 的樣式
  • 2021 年你可能不知道的 CSS 特性
  • 2022 年的 CSS
  • CSS In 2023

如果你也對 CSS 感興趣,也可以關注我的小冊:

  • 《現代 Web 佈局》:主要講述可用於 Web 佈局的現代 CSS 特性
  • 《現代 CSS》:主要講述 CSS 領域最新特性,前面所介紹的特性,基本上在該小冊中能找到
  • 《Web 動畫之旅》:主要講述如何製作 Web 動畫,從理論、設計原則到實戰,如果你喜歡動畫的話,那這本小冊的內容你肯定會喜歡
  • 《防禦式 CSS 精講》:主要講述我們在編寫 CSS 的時候,應該以“萬一”的心理來編寫。該小冊也稱得上是 CSS 技巧集錦,可以實實大大的幫助你解決很多生產中碰到的 CSS 問題

寫在最後

CSS 在過去幾年內(尤其是在 2023 年)大獲成功。如果你對 CSS 感興趣,或者想提高這方面的技能以及想最及時獲得 CSS 最新特性。那麼請關注我!我將帶你領略不一樣的 CSS!

Add a new 評論

Some HTML is okay.