この記事は ここのえ Advent Calendar 2023 Day 3の記事です。
Intro
ここ暫くはTailwind CSSをメインに使用しているのですが、先日公開されたState of CSS 2023で UnoCSS が紹介されていました。Tailwindとの親和性が高いので、触ってみることにしました。
What's UnoCSS?
軽く調べてみた感じ、TailwindCSSからインスピレーションを得た WindiCSS
を開発していたメンバーの1人が UnoCSS
を作り始めた、という経緯のようです。公式Docによると、WindiCSSの「精神的後継」という立場だそうです。
UnoCSS
の特徴として、Atomic CSS Engine であることが挙げられます。柔軟性・拡張性に優れ、必要な機能のみを導入することができます。
UnoCSSの簡単な仕組み
少し掘り下げると、UnoCSSのCoreはあくまでベースのシステムだけ提供し、Preset がCSSユーティリティを提供する、という関係性です。
例えばTailwindのように margin
を調整するために m-1
のようなユーティリティを作るとします。UnoCSSではどうすれば良いでしょうか?
これは uno.config.ts
内にルールを定義しておくことで、それに対応したユーティリティが使えるようになります。
// uno.config.ts
import { defineConfig } from 'unocss'
export default defineConfig({
rules: [
['m-1', { margin: '1px' }]
],
})
これで、<div class="m-1">
のようにクラスを当てれば適用されるようになります。
ですが、これではm-1
の時にしか対応できないので、任意のmarginサイズに対応できるようにしたいので、正規表現を使って以下のように書き換えます。
// uno.config.ts
export default defineConfig({
rules: [
+ [/^m-([\.\d]+)$/, ([_, num]) => ({ margin: `${num}px` })],
],
})
これによって、m-100
や m-3.14
といった自由な値のユーティリティを貼れるようになりました。
ただこれをプロジェクト毎に用意するのは面倒なので、Preset という仕組みがあります。
公式やコミュニティが作ったPresetが用意されています。自分が必要な機能が入っているPresetを随時導入していくことで、便利にUnoCSSを使うことができます。
→ 公式のPresetリスト
Atomic CSSについての考え方は、UnoCSS作者の Anthony Fu 氏による Reimagine Atomic CSS という記事が非常に分かりやすいので、オススメです。
導入
npm
やyarn
で導入します。
yarn add -D unocss
これだけではベースしか導入されていないので、好きなプリセットを入れます。例として基本の @unocss/preset-uno
を導入します。
yarn add -D @unocss/preset-uno
導入したら、先述した uno.config.ts
を作ってプリセットを読み込みます。
// uno.config.ts
import { defineConfig } from 'unocss'
import presetUno from '@unocss/preset-uno'
export default defineConfig({
presets: [
presetUno(),
],
})
数多くの環境に対応しており、Vite や ESLint なども対応しています。
Preset
公式Presetに絞って紹介します。
@unocss/preset-uno, @unocss/preset-wind
preset-wind
は Tailwind, Windi互換のプリセットです。
Tailwindなどで使っていたユーティリティがそのまま使用できます。
設定されているルールも「Tailwindのドキュメントを見てね!」という潔さ。
preset-wind
ですが、最低限のルールだけ定義した preset-mini
を継承しています。
また preset-uno
は同梱されているデフォルトのプリセットですが、こちらは preset-wind
と同一の内容になっているようです1。
@unocsss/preset-attributify
クラスで当てるだけでなく、直接ユーティリティを指定できるようになります。
<p
bg="red"
text="lg white"
p="x-10 y-5"
border="~ rounded"
>
aaaa
</p>
@unocss/preset-icons
アイコンが表示できるようになります。
Iconifyをデータソースとしており、使うアイコンのパッケージを導入しておく必要があります。
例:Material Design Iconsを使う場合
yarn add -D @unocss/preset-icons @iconify-json/mdi
<div class="i-mdi-account-box text-blue-500" />
@unocss/preset-web-fonts
Google Fonts
や FontShare
のフォントを容易に読み込めます。
uno.config.ts
の設定が必要です。
export default defineConfig({
presets: [
presetWebFonts({
provider: 'google',
fonts: {
lobster: 'Lobster',
}
})
],
})
<p class="font-lobster text-lg">test</p>
@unocss/preset-tagify
flex
などがタグとして使えるようになります。
ちょっとインパクトが大きいので、使う場合はチームで相談。もちろんPresetごとに導入・導入見送りができるので、導入しないのも一つの選択です。
<flex>
<p>This is a flex box</p>
<i-mdi-account-box />
</flex>
Transformers
こちらはPreset
と異なり、新しい構文をサポートするための機能です。
Directives transformer
@apply
や@screen
といったディレクティブをサポートします。
.test
@apply mt-4 border text-lg
@screen sm
.test
@apply text-2xl
Variant group transformer
グループにまとめて書くことができます。
元々Windi CSSにあった機能のようですが、かなり便利そうです。
<div class="font-(bold gray) bg-blue hover:(bg-red font-medium)">test</div>
UnoCSS Inspector
Vite開発サーバを使用している時、localhost:5173/__unocss
にアクセスすると、UnoCSSのインスペクタを確認することができます。
まとめ
UnoCSSを触ってみた感じとしては、Tailwindのユーティリティがそのまま使えることもあり、抵抗なく使う事が出来ました。
Tailwindの既存サイトについても、tailwind.config.js
などの自前テーマやカラースキームさえ移行すれば、そのままコードが転用できるので移行コストもかなり低そうです。
大きな違いとしては、公式のプリセットで定義されたルールが小数点以下も対応しているため、m-55
やm-12.3
のようなTailwindで存在しなかった数値も[]
を使わずに指定でき、ストレスフリーで良いと思いました。
今回動作スピードについての検証は行いませんでしたが、ホットリロードの反映速度もかなり高速に感じました。UnoCSSの公式ページでは、
It's 5x faster than Windi CSS or Tailwind JIT.
と謳われているので、実際早そうです。
Intaractive Docs や、 PlayGround も使いやすいので、開発も進めやすいです。また v1.0
にはなっていませんが、現時点でかなり良好なので今後のプロダクトで採用したいな~という気持ちです。