為什麼用 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?
簡化方式有幾種:
@apply
複用 class- 屬性化模式
- 前綴自引用
@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
, @screen
和 theme()
.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>
屬性化模式
個人覺得轉換成屬性有點反人類,所以我不會用。
需要使用 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
編譯 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
不過有幾種情況不會編譯:
- 使用
@apply
自定義的 class - 使用 CSS 自定義的樣式
- 動態樣式
其實自定義的樣式也不需要編譯,但還是可以照著下面的解決方式實現。
使用 @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>
但不得不說,一旦條件判斷一多,這樣寫就變挺蠢的,也不知道方便到哪去了。