首頁 > web前端 > css教學 > CSS在打字稿中使用香草 - 提取

CSS在打字稿中使用香草 - 提取

Jennifer Aniston
發布: 2025-03-19 09:16:16
原創
797 人瀏覽過

CSS in TypeScript with vanilla-extract

vanilla-extract:一款新型的、與框架無關的CSS-in-TypeScript庫。它提供了一種輕量級、健壯且直觀的樣式編寫方式。 vanilla-extract並非強制性的CSS框架,而是一個靈活的開發者工具。近年來,CSS工具領域相對穩定,PostCSS、Sass、CSS Modules和styled-components等工具在2017年之前就已經出現(有些甚至更早),並且至今仍很流行。 Tailwind是近年來少數幾款在CSS工具領域帶來變革的工具之一。

vanilla-extract旨在再次引髮變革。它於今年發布,並受益於一些最新的趨勢,包括:

  • JavaScript開發者轉向TypeScript
  • 瀏覽器對CSS自定義屬性的支持
  • 實用優先樣式

vanilla-extract中包含許多巧妙的創新,我認為這使其意義重大。

零運行時開銷

CSS-in-JS庫通常在運行時將樣式註入文檔中。這有一些好處,包括關鍵CSS提取和動態樣式。

但通常情況下,單獨的CSS文件性能更好。這是因為JavaScript代碼需要經過更昂貴的解析/編譯過程,而單獨的CSS文件可以被緩存,同時HTTP2協議降低了額外請求的成本。此外,自定義屬性現在可以免費提供許多動態樣式。

因此,vanilla-extract效仿Linaria和astroturf,而不是在運行時注入樣式。這些庫允許您使用在構建時被提取並用於構建CSS文件的JavaScript函數來編寫樣式。儘管您使用TypeScript編寫vanilla-extract,但這不會影響生產JavaScript包的整體大小。

TypeScript支持

vanilla-extract的一大價值主張是它提供了類型檢查。如果保持代碼庫的類型安全非常重要,那麼為什麼不也對樣式做同樣的事情呢?

TypeScript提供了許多好處。首先是自動完成。如果您鍵入“fo”,那麼在支持TypeScript的編輯器中,您會得到一個下拉列表,其中包含字體選項(fontFamily、fontKerning、fontWeight或其他匹配項)供您選擇。這使得CSS屬性在編輯器的便捷性下變得易於發現。如果您不記得fontVariant的名稱,但知道它以“font”開頭,您可以鍵入它並滾動瀏覽選項。在VS Code中,您無需下載任何額外工具即可實現此功能。

這極大地加快了樣式的編寫速度:

這也意味著您的編輯器會時刻監視您,確保您不會犯任何拼寫錯誤,這些錯誤可能會導致令人沮喪的bug。

vanilla-extract類型還在其類型定義中提供了語法解釋指向您正在編輯的CSS屬性的MDN文檔的鏈接。當樣式行為異常時,這省去了瘋狂谷歌搜索的步驟。

使用TypeScript編寫意味著您使用駝峰式命名法來表示CSS屬性,例如backgroundColor。對於習慣使用常規CSS語法(例如background-color)的開發者來說,這可能需要一些改變。

集成

vanilla-extract為所有最新的打包工具提供了第一方集成。以下是它目前支持的完整集成列表:

  • webpack
  • esbuild
  • Vite
  • Snowpack
  • NextJS
  • Gatsby

它也完全與框架無關。您只需從vanilla-Extract導入類名,這些類名會在構建時轉換為字符串。

使用方法

要使用vanilla-Extract,您可以編寫一個.css.ts文件,您的組件可以導入該文件。對這些函數的調用會在構建步驟中轉換為哈希和作用域類名字符串。這聽起來可能類似於CSS Modules,這並非巧合:vanilla-Extract的創建者之一Mark Dalgleish也是CSS Modules的共同創建者。

style()函數

您可以使用style()函數創建一個自動作用域的CSS類。您傳入元素的樣式,然後導出返回值。在您的用戶代碼中的某個地方導入此值,它將轉換為作用域類名。

 // title.css.ts
import {style} from "@vanilla-extract/css";

export const titleStyle = style({
  backgroundColor: "hsl(210deg,30%,90%)",
  fontFamily: "helvetica, Sans-Serif",
  color: "hsl(210deg,60%,25%)",
  padding: 30,
  borderRadius: 20,
});
登入後複製
 // title.ts
import {titleStyle} from "./title.css";

document.getElementById("root").innerHTML = `<h1> Vanilla Extract</h1> `;
登入後複製

媒體查詢和偽選擇器也可以包含在樣式聲明中:

 // title.css.ts
backgroundColor: "hsl(210deg,30%,90%)",
fontFamily: "helvetica, Sans-Serif",
color: "hsl(210deg,60%,25%)",
padding: 30,
borderRadius: 20,
"@media": {
  "screen and (max-width: 700px)": {
    padding: 10
  }
},
":hover":{
  backgroundColor: "hsl(210deg,70%,80%)"
}
登入後複製

這些style函數調用是對CSS的輕量級抽象——所有屬性名稱和值都映射到您熟悉的CSS屬性和值。需要習慣的一個變化是,值有時可以聲明為數字(例如padding: 30),默認為像素單位值,而某些值需要聲明為字符串(例如padding: "10px 20px 15px 15px")。

style函數中的屬性只能影響單個HTML節點。這意味著您不能使用嵌套來聲明元素子元素的樣式——這在Sass或PostCSS中您可能習慣了。相反,您需要分別設置子元素的樣式。如果子元素需要基於父元素的不同樣式,您可以使用selectors屬性添加依賴於父元素的樣式:

 // title.css.ts
export const innerSpan = style({
  selectors:{[`${titleStyle} &`]:{
    color: "hsl(190deg,90%,25%)",
    fontStyle: "italic",
    textDecoration: "underline"
  }}
});
登入後複製
 // title.ts
import {titleStyle,innerSpan} from "./title.css";
document.getElementById("root").innerHTML = 
`<h1> Vanilla Extract</h1>
Unstyled`;
登入後複製

或者,您也可以使用主題API(我們接下來會講到)在父元素中創建自定義屬性,這些屬性由子節點使用。這聽起來可能比較嚴格,但它被故意設計成這樣是為了提高大型代碼庫的可維護性。這意味著您將確切地知道項目中每個元素的樣式在哪裡聲明。

主題

您可以使用createTheme函數在TypeScript對像中構建變量:

 // title.css.ts
import {style,createTheme } from "@vanilla-extract/css";

// 創建主題export const [mainTheme,vars] = createTheme({
  color:{
    text: "hsl(210deg,60%,25%)",
    background: "hsl(210deg,30%,90%)"
  },
  lengths:{
    mediumGap: "30px"
  }
})

// 使用主題export const titleStyle = style({
  backgroundColor:vars.color.background,
  color: vars.color.text,
  fontFamily: "helvetica, Sans-Serif",
  padding: vars.lengths.mediumGap,
  borderRadius: 20,
});
登入後複製

然後,vanilla-extract允許您創建主題的變體。 TypeScript幫助它確保您的變體使用相同的屬性名稱,因此如果您忘記向主題添加background屬性,您會收到警告。

以下是如何創建常規主題和暗模式:

 // title.css.ts
import {style,createTheme } from "@vanilla-extract/css";

export const [mainTheme,vars] = createTheme({
  color:{
    text: "hsl(210deg,60%,25%)",
    background: "hsl(210deg,30%,90%)"
  },
  lengths:{
    mediumGap: "30px"
  }
})
// 主題變體- 注意這部分不使用數組語法export const darkMode = createTheme(vars,{
  color:{
    text:"hsl(210deg,60%,80%)",
    background: "hsl(210deg,30%,7%)",
  },
  lengths:{
    mediumGap: "30px"
  }
})
// 使用主題export const titleStyle = style({
  backgroundColor: vars.color.background,
  color: vars.color.text,
  fontFamily: "helvetica, Sans-Serif",
  padding: vars.lengths.mediumGap,
  borderRadius: 20,
});
登入後複製

然後,使用JavaScript,您可以動態地應用vanilla-extract返回的類名來切換主題:

 // title.ts
import {titleStyle,mainTheme,darkMode} from "./title.css";

document.getElementById("root").innerHTML = 
`<div>
  <h1>Vanilla Extract</h1>
  Dark mode
</div>`
登入後複製

這是如何在底層工作的?您在createTheme函數中聲明的對象將轉換為附加到元素類的CSS自定義屬性。這些自定義屬性經過哈希處理以防止衝突。我們的mainTheme示例的輸出CSS如下所示:

 .src__ohrzop0 {
  --color-brand__ohrzop1: hsl(210deg,80%,25%);
  --color-text__ohrzop2: hsl(210deg,60%,25%);
  --color-background__ohrzop3: hsl(210deg,30%,90%);
  --lengths-mediumGap__ohrzop4: 30px;
}
登入後複製

而我們的darkMode主題的CSS輸出如下所示:

 .src__ohrzop5 {
  --color-brand__ohrzop1: hsl(210deg,80%,60%);
  --color-text__ohrzop2: hsl(210deg,60%,80%);
  --color-background__ohrzop3: hsl(210deg,30%,10%);
  --lengths-mediumGap__ohrzop4: 30px;
}
登入後複製

因此,我們只需要更改用戶代碼中的類名即可。將darkmode類名應用於父元素, mainTheme自定義屬性將被darkMode自定義屬性替換。

Recipes API

stylecreateTheme函數本身就足以樣式化一個網站,但vanilla-extract提供了一些額外的API來提高可重用性。 Recipes API允許您為元素創建許多變體,您可以在標記或用戶代碼中從中選擇。

首先,需要單獨安裝它:

 npm install @vanilla-extract/recipes
登入後複製

以下是它的工作原理。您導入recipe函數並傳入一個包含basevariants屬性的對象:

 // button.css.ts
import { recipe } from '@vanilla-extract/recipes';

export const buttonStyles = recipe({
  base:{
    // 應用於所有按鈕的樣式都放在這裡},
  variants:{
    // 我們從中選擇的樣式放在這裡}
});
登入後複製

base中,您可以聲明將應用於所有變體的樣式。在variants中,您可以提供不同的方式來自定義元素:

 // button.css.ts
import { recipe } from '@vanilla-extract/recipes';
export const buttonStyles = recipe({
  base: {
    fontWeight: "bold",
  },
  variants: {
    color: {
      normal: {
        backgroundColor: "hsl(210deg,30%,90%)",
      },
      callToAction: {
        backgroundColor: "hsl(210deg,80%,65%)",
      },
    },
    size: {
      large: {
        padding: 30,
      },
      medium: {
        padding: 15,
      },
    },
  },
});
登入後複製

然後您可以在標記中聲明要使用的變體:

 // button.ts
import { buttonStyles } from "./button.css";

Click me
登入後複製

vanilla-extract利用TypeScript為您自己的變體名稱提供自動完成!

您可以隨意命名變體,並在其中放入任何您想要的屬性,例如:

 // button.css.ts
export const buttonStyles = recipe({
  variants: {
    animal: {
      dog: {
        backgroundImage: 'url("./dog.png")',
      },
      cat: {
        backgroundImage: 'url("./cat.png")',
      },
      rabbit: {
        backgroundImage: 'url("./rabbit.png")',
      },
    },
  },
});
登入後複製

您可以看到這對於構建設計系統非常有用,因為您可以創建可重用的組件並控制它們變化的方式。這些變化使用TypeScript變得很容易發現——您只需要鍵入CMD/CTRL Space(在大多數編輯器上),您就會得到一個下拉列表,其中列出了自定義組件的不同方法。

實用優先與Sprinkles

Sprinkles是一個基於vanilla-extract構建的實用優先框架。 vanilla-extract文檔是這樣描述它的:

基本上,它就像構建您自己的零運行時、類型安全的Tailwind、Styled System等版本。

因此,如果您不喜歡命名事物(我們都做噩夢,創建了一個外部包裝div,然後意識到我們需要用……外部外部包裝器包裝它),Sprinkles可能是您首選的vanilla-extract使用方式。

Sprinkles API也需要單獨安裝:

 npm install @vanilla-extract/sprinkles
登入後複製

現在我們可以創建一些構建塊供我們的實用函數使用。讓我們通過聲明幾個對象來創建一個顏色和長度列表。 JavaScript鍵名可以是任何我們想要的。值需要是我們計劃使用的CSS屬性的有效CSS值:

 // sprinkles.css.ts
const colors = {
  blue100: "hsl(210deg,70%,15%)",
  blue200: "hsl(210deg,60%,25%)",
  blue300: "hsl(210deg,55%,35%)",
  blue400: "hsl(210deg,50%,45%)",
  blue500: "hsl(210deg,45%,55%)",
  blue600: "hsl(210deg,50%,65%)",
  blue700: "hsl(207deg,55%,75%)",
  blue800: "hsl(205deg,60%,80%)",
  blue900: "hsl(203deg,70%,85%)",
};

const lengths = {
  small: "4px",
  medium: "8px",
  large: "16px",
  humungous: "64px"
};
登入後複製

我們可以使用defineProperties函數聲明這些值將應用於哪些CSS屬性:

  • 向其傳遞一個包含properties屬性的對象。
  • properties中,我們聲明一個對象,其中是用戶可以設置的CSS屬性(這些需要是有效的CSS屬性),而是我們之前創建的對象(我們的顏色和長度列表)。
 // sprinkles.css.ts
import { defineProperties } from "@vanilla-extract/sprinkles";

const colors = {
  blue100: "hsl(210deg,70%,15%)"
  // etc.
}

const lengths = {
  small: "4px",
  // etc.
}

const properties = defineProperties({
  properties: {
    // 此對象的鍵需要是有效的CSS屬性// 值是我們為用戶提供的選項color: colors,
    backgroundColor: colors,
    padding: lengths,
  },
});
登入後複製

然後最後一步是將defineProperties的返回值傳遞給createSprinkles函數,並導出返回值:

 // sprinkles.css.ts
import { defineProperties, createSprinkles } from "@vanilla-extract/sprinkles";

const colors = {
  blue100: "hsl(210deg,70%,15%)"
  // etc.
}

const lengths = {
  small: "4px",
  // etc. 
}

const properties = defineProperties({
  properties: {
    color: colors,
    // etc. 
  },
});
export const sprinkles = createSprinkles(properties);
登入後複製

然後我們可以在組件內通過在class屬性中調用sprinkles函數並為每個元素選擇我們想要的選項來開始內聯樣式化。

 // index.ts
import { sprinkles } from "./sprinkles.css";
document.getElementById("root").innerHTML = `Click me
`;
登入後複製

JavaScript輸出為每個樣式屬性保存一個類名字符串。這些類名與輸出CSS文件中的單個規則匹配。

 Click me
登入後複製

如您所見,此API允許您使用一組預定義的約束在標記內設置元素的樣式。您還可以避免為每個元素想出類名的困難任務。結果是感覺非常像Tailwind的東西,但也受益於圍繞TypeScript構建的所有基礎設施。

Sprinkles API還允許您編寫條件和簡寫,以使用實用程序類創建響應式樣式。

總結

vanilla-extract感覺是CSS工具領域的一大進步。在將其構建為一個直觀、健壯的樣式化解決方案方面,投入了大量的思考,該解決方案利用了靜態類型提供的所有功能。

進一步閱讀

  • vanilla-extract 文檔
  • Mark關於vanilla-extract的演講
  • vanilla-extract Discord
  • CodeSandbox上的代碼示例

This revised output maintains the original meaning while using different wording and sentence structures. The images remain in their original format and location.

以上是CSS在打字稿中使用香草 - 提取的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板