Site icon May's Notes

UnoCSS 一些使用心得和小技巧

unocss

為什麼用 UnoCSS

為什麼我要用 UnoCSS 而不用 Tailwind CSS 呢?其實只有一個原因,就是因為 Tailwind CSS 不支持 Variant group,而 Uno CSS 支持。

如果使用 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)"

當然,也可以藉由自定義 Class 來解決這個問題,但若這段 Class 不需要複用的話就顯得有點多餘了。

除了 Variant group 之外,另一個原因就是 Uno CSS 其他使用上跟 Tailwind CSS 基本無差,所以改用 UnoCSS 算是無痛轉換。

相同變體如何精簡?

直接拿前面提到的例子,若想把這段 Class:

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

簡化成:

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

就需要用到 Variant group transformer

npm install -D @unocss/transformer-variant-group
// or
yarn add -D @unocss/transformer-variant-group
// uno.config.ts
import { defineConfig } from 'unocss'
import transformerVariantGroup from '@unocss/transformer-variant-group'

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

安裝完 Transformer 就能使用 Variant group 了。

Class 太長如何簡化? 如何複用 class?

簡化方式有幾種:

  1. @apply 複用 class
  2. 屬性化模式
  3. 前綴自引用

@apply

當 class 太長或者需要複用的時候可以使用 @apply,但要使用前得先安裝 @unocss/transformer-directives

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

uno.config.ts

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

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

這個 transformer 可以轉換 @apply, @screentheme()

.m-menu-border {
  @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="m-menu-border">
    {item.name}
  </a>
</li>

Directives transformer – unocss

屬性化模式

個人覺得轉換成屬性有點反人類,所以我不會用。

需要使用 presetAttributify

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

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 中的常見屬性。如果需要自定義屬性才需要修改。

前綴自引用

前綴自引用跟屬性化模式一樣都需要引入 presetAttributify

對於具有與前綴相同的工具類(如flex、grid、border)的工具類,提供了一個特殊的 ~ 值。

<button class="border border-red">Button</button>

可以簡化成

<button border="~ red">Button</button>

集中管理主題顏色

defineConfig 中可以定義 Theme,將專案中常用的顏色、樣式集中管理

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

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

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

除了顏色之外還可以管理 breakpoints, variants, rules 或者自定義 shortcuts

theme – unocss

編譯 Class

要是不編譯 class 的話,其他人打開控制台一看就能看見你的 class 是怎麼寫的,有一種裸奔的感覺(x

npm install -D @unocss/transformer-compile-class
// or
yarn add -D @unocss/transformer-compile-class
// uno.config.ts
import { defineConfig } from 'unocss'
import transformerCompileClass from '@unocss/transformer-compile-class'

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

只需要在要編譯樣式的 className 最前方加上 :uno: 即可

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

這個 :uno: 是預設的。

編譯完之後 class 就會變成 uno-xxxxxx

不過有幾種情況不會編譯:

  1. 使用 @apply 自定義的 class
  2. 使用 CSS 自定義的樣式
  3. 動態樣式

其實自定義的樣式也不需要編譯,但還是可以照著下面的解決方式實現。

使用 @apply 自定義的 class

.scale-button {
  @apply transition ease-in-out duration-300 hover:(-translate-y-1 scale-110);
}

如果將 :uno: 直接寫在 scale-button 前面是沒有用的。

<button className=":uno: scale-button">送出</button>

解決方法

把使用 @apply 定義的 class 改為使用 shorcuts

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

export default defineConfig({
  ...
  shortcuts: {
    'scale-button': 'transition ease-in-out duration-300 hover:(-translate-y-1 scale-110)',
  }
})
<button className=":uno: scale-button">送出</button>

使用 CSS 自定義的樣式

.m-menu {
  transform: translate(100%);
  visibility: hidden;
  transition: transform 0.3s ease-in-out;
}
<div className=":uno: m-menu ...">
  ...
</div>

解決方式

一樣可以轉換為 shorcuts 解決

動態樣式

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

條件判斷的部分 class 不會被編譯

解決方式

直接建一個變數來定義條件判斷完成的 class

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

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

但不得不說,一旦條件判斷一多,這樣寫就變挺蠢的,也不知道方便到哪去了。

Exit mobile version