UnoCSS 一些使用心得和小技巧

UnoCSS 一些使用心得和小技巧

2024-12-25 大改了一下內容,原本寫得太亂。

為什麼選擇 UnoCSS?

我想體驗 UnoCSS 的主要原因,是因為 Tailwind CSS 不支持 Variant Group,而 UnoCSS 支持這個功能

假如在 Tailwind CSS 中,我需要為某元素添加一些 hover 後的樣式,必須這樣寫:

className="... hover:translate-y-[-5px] hover:scale-101 hover:bg-hotpink hover:border-hotpink"

但如果支持 Variant Group,就可以被精簡為:

className="... hover:(translate-y-[-5px] scale-101 bg-hotpink border-hotpink)"

雖然在字符數量上看起來差別不大,但 可讀性和維護性明顯提高(我主觀認為)。

除了 Variant Group,UnoCSS 的用法與 Tailwind CSS 幾乎無差,因此從 Tailwind CSS 切換到 UnoCSS 是一個相對 無痛的轉換過程

簡化及複用 Class 的方法

在 UnoCSS 中,可以通過多種方式來簡化 Class 並提升程式碼的可讀性與複用性,包括:

  1. @apply:可以將多個原子化的樣式提取到一個自定義的 CSS 類中
  2. Variant Group:Variant Group 支持將相同變體的多個樣式簡化為一組
  3. 屬性化模式:讓樣式直接以 HTML 屬性形式定義,減少使用傳統的 class
  4. 前綴自引用:支持通過前綴語法引用自己的樣式,讓程式碼更加簡潔
  5. 自定義類名

@apply

雖然 UnoCSS 支持 @apply,但其本質設計仍以按需生成原子化CSS為核心,建議僅在需要大量複用樣式時使用,並保持合理的使用範圍。

如果只是為了讓程式碼看起來「整潔」,那其實沒必要用 @apply,因為原子化樣式就是為了解決維護 CSS 的麻煩。

當 Class 名過長或需要在多處重複使用時,可以通過 @apply 將多個樣式抽取到一個自定義的 CSS 類中,從而提高程式碼的復用性與維護性。

不過,使用 @apply 需要先安裝 Transformer Directives

npm install -D @unocss/transformer-directives
// or
yarn add -D @unocss/transformer-directives

配置 Transformer Directives

uno.config.ts 文件中,引入並啟用 Transformer Directives:

// uno.config.ts
import { defineConfig } from 'unocss'
import transformerDirectives from '@unocss/transformer-directives'

export default defineConfig({
  // ...
  transformers: [
    transformerDirectives(),
  ],
})

啟用該 Transformer 後,可以支持以下功能:

  • @apply:複用樣式。
  • @screen:針對不同屏幕尺寸進行響應式設計。
  • theme():訪問 UnoCSS 主題的配置。

@apply 使用範例

以下是一個使用 @apply 簡化複用 Class 的範例:

.menu-link-style {
  @apply text-white border-solid border-b-3 border-transparent transition ease-in-out pb-1 hover:border-white;
}
<li key={item.name}>
  <a href={item.link} className="menu-link-style">
    {item.name}
  </a>
</li>

補充

Whatever you do, don’t use @apply just to make things look “cleaner”. Yes, HTML templates littered with Tailwind classes are kind of ugly. Making changes in a project that has tons of custom CSS is worse.

TailwindCSS 官方文檔中提到:不要為了讓程式碼看起來「整潔」就使用 @apply。一堆 Tailwind class 看起來可能有點亂,但這其實比用 @apply 寫一堆自定義 CSS 好得多。為什麼不建議呢?因為過度使用 @apply 基本上等同於重新撰寫 CSS,這不僅背離了 Tailwind 的核心設計理念,也會失去使用 Tailwind 的優勢,比如:

  • 高效率:直接用 class 就能完成樣式,完全不用切檔案找 CSS。
  • 好維護:少了自訂 CSS 的負擔,未來要改樣式也不麻煩。

和 Tailwind 一樣,UnoCSS 的原子化設計是希望讓樣式直觀且開發高效。如果大量使用 @apply,其實等於是回到傳統 CSS 的寫法,這就背離了它的初衷。

https://tailwindcss.com/docs/reusing-styles#extracting-classes-with-apply

Variant Group

Variant Group 支持將相同變體的多個樣式簡化為一組。

假如有以下的 Class:

className="... hover:translate-y-[-5px] hover:scale-101 hover:bg-hotpink hover:border-hotpink"

若想將其簡化為支持 Variant Group 的語法:

className="... hover:(translate-y-[-5px] scale-101 bg-hotpink border-hotpink)"

就需要使用 Variant Group Transformer

安裝 Variant Group Transformer

可以使用以下命令安裝:

npm install -D @unocss/transformer-variant-group
// or
yarn add -D @unocss/transformer-variant-group

配置 UnoCSS

uno.config.ts 文件中,引入並啟用 Transformer:

// uno.config.ts
import { defineConfig } from 'unocss'
import transformerVariantGroup from '@unocss/transformer-variant-group'

export default defineConfig({
  // ...
  transformers: [
    transformerVariantGroup(),
  ],
})

安裝並配置好 Variant Group Transformer 後,就可以直接在 Class 中使用 Variant Group 語法,大幅提高程式碼的簡潔性與可讀性。

屬性化模式

適合用在簡單的樣式,複雜一點的樣式使用屬性化模式反而會降低可讀性。

啟用屬性化模式

uno.config.ts 中加入 presetAttributify() 啟用屬性化模式:

// uno.config.ts
import { defineConfig } from 'unocss'
import presetAttributify from '@unocss/preset-attributify'

export default defineConfig({
  presets: [
    presetAttributify(),
    // ...
  ],
})

使用範例

直接在標籤上添加屬性來定義樣式:

<!-- 傳統寫法 -->
<button
  class="bg-blue-400 hover:bg-blue-500 text-sm text-white font-mono font-light py-2 px-4 rounded border-2 border-blue-200 dark:bg-blue-500 dark:hover:bg-blue-600"
>
  Button
</button>

<!-- 屬性化模式 -->
<button
  bg="blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600"
  text="sm white"
  font="mono light"
  p="y-2 x-4"
  border="2 rounded blue-200"
>
  Button
</button>

支持 TypeScript

新增 shims.d.ts

import type { AttributifyAttributes } from '@unocss/preset-attributify'

declare module 'react' {
  interface HTMLAttributes<T> extends AttributifyAttributes {}
}

通常來說類型包括 @unocss/preset-uno 中的常見屬性。如果需要自定義屬性才需要修改。

前綴自引用

UnoCSS 支持通過前綴語法引用自己的樣式,使程式碼更加簡潔。不過個人不太習慣這種寫法,容易看混。

啟用前綴自引用

一樣需要在 uno.config.ts 中加入 presetAttributify()

// uno.config.ts
import { defineConfig } from 'unocss'
import presetAttributify from '@unocss/preset-attributify'

export default defineConfig({
  presets: [
    presetAttributify(),
    // ...
  ],
})

使用範例

當一組樣式中需要部分樣式延續其他的屬性時,可以使用前綴:

{/* 不使用前綴自引用 */}
<button class="border border-red">
  Button
</button>

{/* 使用前綴自引用 */}
<button border="~ red">
  Button
</button>

自定義類名

UnoCSS 允許自定義類名,而不是僅限於官方提供的規則。

假設要新增一個自定義類名 rounded-xl-shadow,讓它同時添加圓角和陰影。可以在 uno.config.ts 中添加規則:

// uno.config.ts
import { defineConfig } from 'unocss'

export default defineConfig({
  rules: [
    ['rounded-xl-shadow', { 
      'border-radius': '1rem', 
      'box-shadow': '0 4px 6px rgba(0, 0, 0, 0.1)' 
    }],
  ],
})

使用範例

直接使用自定義的類名:

<div class="rounded-xl-shadow"></div>

生成的CSS

.rounded-xl-shadow {
  border-radius: 1rem;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

集中管理主題樣式

defineConfig 中可以使用 theme 屬性,將專案中常用的 顏色間距字體大小等樣式集中管理,提升樣式的統一性與可維護性。

定義顏色主題

可以在 uno.config.ts 中集中定義顏色:

// uno.config.ts
import { defineConfig } from 'unocss'

const theme = {
  colors: {
    'main': '#41289a',
    'gray': '#b9b9b9',
    'tag': '#52a49a'
  }
}

export default defineConfig({
  theme,
  ...
})

使用範例:

<div class="bg-main text-gray border-tag">主題色塊</div>

定義斷點尺寸(breakpoints)

breakpoints: 集中定義響應式設計中的斷點尺寸,例如:

export default defineConfig({
  theme: {
    breakpoints: {
      sm: '640px',  // 小螢幕
      md: '768px',  // 中螢幕
      lg: '1024px', // 大螢幕
    },
  },
})

使用範例:

<div class="hidden sm:block lg:text-lg">響應式文字</div>

定義字體大小

自定義專案常用的字體大小,統一樣式風格:

export default defineConfig({
  theme: {
    fontSize: {
      small: '0.875rem', // 小字體
      base: '1rem',      // 基礎字體
      large: '1.25rem',  // 大字體
    },
  },
})

使用範例:

<p class="text-base">這是基礎字體大小</p>
<p class="text-large">這是大字體大小</p>

其餘設置可以參考官方文檔 Theme – UnoCSS

編譯 Class

在使用 UnoCSS 時,默認情況下所有的類名會直接顯示在 DOM 中,若要將類名編譯成無語義的哈希值需要手動啟用。

image 32

啟用 Class 編譯功能

安裝 Transformer:

npm install -D @unocss/transformer-compile-class
// or
yarn add -D @unocss/transformer-compile-class

uno.config.ts 中配置 Transformer:

// uno.config.ts
import { defineConfig } from 'unocss'
import transformerCompileClass from '@unocss/transformer-compile-class'

export default defineConfig({
  transformers: [
    transformerCompileClass(),
  ],
})

在需要編譯的類名前加上特定的標識,默認是 :uno:

<main className=":uno: mt-[3.75rem] lg:mt-[5.6rem] xl:mt-[6.25rem]">
...
</main>

編譯後的效果

啟用 transformer-compile-class 並在指定的 Class 前加上 :uno: 後,UnoCSS 會將這些類名編譯成隨機的哈希值。例如:

image 33

自定義編譯標識

如果不想使用默認的 :uno:,可以在 uno.config.ts 中自定義編譯標識:

  • trigger: 需要編譯的標識
  • classPrefix: 編譯後的類名前綴
import { defineConfig } from 'unocss'
import transformerCompileClass from '@unocss/transformer-compile-class'

export default defineConfig({
  transformers: [
    transformerCompileClass({
      classPrefix: 'custom-prefix-',
      trigger: ':custom-trigger:',
    }),
  ],
})

使用範例:

<!-- 編譯前 -->
<div className=":custom-trigger: rounded-xl-shadow">test</div>

<!-- 編譯後 -->
<div class="my-prefix-p5v6u8">test</div>

動態樣式的編譯

transformerCompileClass 是設計來將靜態類名轉換為哈希值,這樣有助於壓縮和隱藏 CSS 實現。動態類名需要在運行時解析和應用,這些類名是根據運行時的邏輯條件生成的,因此無法像靜態類名那樣編譯。

<div className={`:uno: flex h-full ${index === 0 && 'relative md:flex-col'}`}>
  ...
</div>
image 36

如果希望動態樣式一樣也能被編輯,也不是沒有辦法,只是稍微麻煩了點。

使用變數

將條件判斷邏輯提取到變數 divClass 中,這樣可以確保動態類名的部分也能被編譯

const divClass = index === 0 ? ':uno: flex h-full relative md:flex-col' : ':uno: flex h-full'

<div className={divClass}>
  ...
</div>
guest


1 評論
最舊
最新 最多投票
內聯回饋
查看全部評論
andd

学习