もっとくわしく UnoCSS ~公式プリセット篇~
🫡 はじめに
ナイトウ(@engineer_naito)と申します。
今回は CSS フレームワーク Atomic CSS エンジンである UnoCSS について紹介したいと思います。
こちらの記事では、UnoCSS の成り立ちや特徴を紹介しました
今回は UnoCSS の公式プリセットについて紹介したいと思います。
公式プリセットを活用することで UnoCSS をパワーを最大限に生かすことができます。
公式プリセット(UnoCSS の特徴)を知って、UnoCSS を好きになっていただきたいです。
また、前回記事で作者の Anthony Fu さんからコメントをいただきました!!!
https://zenn.dev/comm_vue_nuxt/articles/what_is_unocss#comment-ba63c20e23e285
質問があれば思い切って質問してみてはいかがでしょうか 💁
📑 ドキュメント
🛝 公式 Playground
公式 Playground が提供されています。
こちらを活用して、UnoCSS を体験してみてください。
また、この記事内でサンプルコード付きの公式 Playground へのリンクを用意しています。
(e.g. 公式 Playground 👈)
ぜひ実際にコードを動かして UnoCSS を体験しながら記事を読んでください 👍
1️⃣ UnoCSS とは
Instant On-demand Atomic CSS Engine
Customizable · Powerful · Fast · Joyful
UnoCSS は、オンデマンドで動作する Atomic CSS エンジンです。
Tailwind CSS, Windi CSS の影響を受け、より迅速で柔軟なスタイル定義を可能にするために設計されました。
🛠️ プリセット
プリセットは UnoCSS の心臓です。
UnoCSS のプリセットはあらかじめ定義された一連のスタイルやルールのセットです。
プリセットを使うことで共通のスタイルや機能を手軽にプロジェクトに追加でき、設定する手間が省けます。
プリセットは公式で提供されているものもありますし、自分でカスタムプリセットを作成することも可能です。
コミュニティプリセットもあります。
プリセットを利用するには uno.config.ts
に記述します。
import { defineConfig, presetAttributify, presetUno } from "unocss";
export default defineConfig({
presets: [
presetAttributify({ /* プリセットのオプション */}),
presetUno(),
// ...
],
});
独自のプリセットを定義することもできます。
import { Preset } from "unocss";
export const myPreset: Preset = {
name: "my-preset",
rules: [
[/^m-([.\d]+)$/, ([_, num]) => ({ margin: `${num}px` })],
[/^p-([.\d]+)$/, ([_, num]) => ({ padding: `${num}px` })],
],
// ...
};
import { defineConfig } from "unocss";
import { myPreset } from "./my-preset";
export default defineConfig({
presets: [
myPreset,
],
});
📌 公式プリセット
2024 年 8 月時点では、公式プリセットとして以下が提供されています。
プリセット名 | 説明 |
---|---|
@unocss/preset-uno |
デフォルトプリセット |
@unocss/preset-mini |
最小かつ必要不可欠なルールとバリアント |
@unocss/preset-wind |
Tailwind CSS, Windi CSS のコンパクトなプリセット |
@unocss/preset-attributify |
属性化モード |
@unocss/preset-tagify |
タグ化モード |
@unocss/preset-icons |
純粋な CSS によるアイコン Powered by Iconify |
@unocss/preset-web-fonts |
Web フォント |
@unocss/preset-typography |
タイポグラフィプリセット |
@unocss/preset-rem-to-px |
rem を px に変換 |
公式プリセットを順番に見ていきましょう。
☝️ preset-uno
UnoCSS のデフォルトプリセットです。
@unocss/preset-wind
, @unocss/prest-mini
を継承しています。
import { defineConfig, presetUno } from "unocss";
export default defineConfig({
presets: [
presetUno(),
],
});
このプリセットは、
- Tailwind CSS
- Windi CSS
- Bootstrap
- Tachyons
などの人気ユーティリティファーストフレームワークの共通のスーパーセットを提供しようとしています。
/* Tachyons */
.ma4 {
margin: 1rem;
}
/* Tailwind CSS */
.ml-3 {
margin-left: 0.75rem;
}
/* Bootstrap */
.ms-2 {
margin-inline-start: 0.5rem;
}
/* Windi CSS */
.mt-10px {
margin-top: 10px;
}
ユーティリティの詳細は各フレームワークの公式ドキュメントを参照するのがよいでしょう。
💨 preset-wind
@unocss/preset-wind
は、UnoCSS で Tailwind CSS, Windi CSS のユーティリティを利用できるプリセットです。
import { defineConfig, presetWind } from "unocss";
export default defineConfig({
presets: [
presetWind(),
],
});
🐤 preset-mini
UnoCSS のためのベーシックなプリセットです。
import { defineConfig, presetMini } from "unocss";
export default defineConfig({
presets: [
presetMini(),
],
});
@unocss/prest-mini
は @unocss/preset-wind
のサブセットです。
必要不可欠なユーティリティは含まれていますが、Tailwind CSS で導入された "opinionated" なものや複雑なものは除外されています。
(container
, animation
, gradient
, など)
Tailwind CSS や Windi CSS のおなじみのユーティリティをベースにして、独自のカスタムプリセットを作成するための良い出発点になるでしょう。
🌚 ダークモード
このプリセットでは、dark:
バリアントを使用したクラスベースのダークモードを生成します。
<div class="dark:bg-red:10" />
からは、
.dark .dark\:bg-red\:10 {
background-color: rgb(248 113 113 / 0.1);
}
が生成されます。
メディアクエリベースのダークモードを使用するのであれば、@dark:
バリアントを使用します。
<div class="@dark:bg-red:10" />
@media (prefers-color-scheme: dark) {
.\@dark\:bg-red\:10 {
background-color: rgb(248 113 113 / 0.1);
}
}
またはプリセットオプションで設定することができます。
import { defineConfig, presetMini } from "unocss";
export default defineConfig({
presets: [
presetMini({
dark: "media",
}),
],
});
🗄️ CSS @layer
CSS のレイヤー @layer
は、layer-xx:
バリアントでサポートされています。
<div class="layer-foo:p4" />
<div class="layer-bar:m4" />
からは、
@layer foo {
.layer-foo\:p4 {
padding: 1rem;
}
}
@layer bar {
.layer-bar\:m4 {
margin: 1rem;
}
}
が生成されます。
🍱 preset-icons
以下の規約に従うことで、純粋な CSS でアイコンを使用できます。
<prefix><collection>-<icon>
<prefix><collection>:<icon>
<div class="i-ph-anchor-simple-thin"></div>
<div class="i-mdi-alarm text-orange-400"></div>
<div class="i-logos-vue text-3xl"></div>
<button class="i-carbon-sun dark:i-carbon-moon"></button>
<div
class="i-twemoji-grinning-face-with-smiling-eyes hover:i-twemoji-face-with-tears-of-joy"
></div>
🧑💻 インストール
Iconify はアイコンのデータソースとして用います。
アイコンセットを使用するには、@iconify-json/*
パターンに従って、
該当するアイコンセットを devDependencies
にインストールする必要があります。
(例: @iconify-json/mdi
, @iconify-json/tabler
)
アイコンセットとパッケージ名は以下で確認できます。
利用可能な全てのコレクションは Icônes や Iconify で参照することができます。
2024.08.19 追記
コメントで autoInstall
オプションについて教えていただきました。
import { defineConfig, presetIcons } from "unocss";
export default defineConfig({
presets: [
presetIcons({
autoInstall: true,
}),
],
});
とすることで、必要なアイコンセットを UnoCSS が自動的にインストールしてくれます。
手動でパッケージをインストールする必要がなくなり、開発がスムーズに進められるようになります。
➕ 追加プロパティ
アイコンのデフォルトの動作を制御するために、追加の CSS プロパティを指定することができます。
import { defineConfig, presetIcons } from "unocss";
export default defineConfig({
presets: [
presetIcons({
extraProperties: {
"display": "inline-block",
"vertical-align": "middle",
// ...
},
}),
],
});
✍️ モードの上書き
デフォルトではこのプリセットはアイコンの特性に基づいて、それぞれのアイコンに対してレンダリングモードを自動的に選択します。
ただし、場合によっては各アイコンに対してレンダリングモードを明示的に設定したいこともあるでしょう。
-
?bg
はbackground-image
を意味し、アイコンを背景画像としてレンダリングします。 -
?mask
はmask
を意味し、アイコンをマスク画像としてレンダリングします。
<div
class="w-full flex items-center justify-center gap-x-30 text-4xl min-h-screen"
>
<div class="i-vscode-icons:file-type-light-pnpm"></div>
<div class="i-vscode-icons:file-type-light-pnpm?mask text-red-300"></div>
</div>
⚙️ アイコンコレクションとリゾルバの設定
🌐 ブラウザ
iconify
のコレクションをロードするには、@iconify/json
ではなく、@iconify-json/[the-collection-you-want]
を使うべきです。
json
ファイルは巨大だからです。
🎀 バンドラー
バンドラーを使用するのであれば、コレクションを動的インポートとして提供できます。
コレクションは非同期チャンクとしてバンドルされ、必要なときに読み込まれます。
import { defineConfig, presetIcons } from "unocss";
import presetIcons from "@unocss/preset-icons/browser";
export default defineConfig({
presets: [
presetIcons({
collections: {
carbon: () => import("@iconify-json/carbon/icons.json").then(i => i.default),
mdi: () => import("@iconify-json/mdi/icons.json").then(i => i.default),
logos: () => import("@iconify-json/logos/icons.json").then(i => i.default),
},
}),
],
});
🚛 CDN
CDN から取得する場合は、cdn
オプションを指定できます。
CDN プロバイダーとしては、esm.sh
が推奨されています。
import { defineConfig, presetIcons } from "unocss";
export default defineConfig({
presets: [
presetIcons({
cdn: "https://esm.sh/",
}),
],
});
🧰 カスタムコレクション
CustomIconLoader
や InlineCollection
を利用して、独自のカスタムコレクションを提供することもできます。
import { defineConfig, presetIcons } from "unocss";
export default defineConfig({
presetIcons({
collections: {
custom: {
circle: "<svg viewBox='0 0 120 120'><circle cx='60' cy='60' r='50'></circle></svg>",
/* ... */
},
carbon: () => import("@iconify-json/carbon/icons.json").then(i => i.default as any),
/* ... */
},
});
});
🌳 Node.js
Node.js
ではインストール済みのアイコンデータセットを自動的に探します。
iconify
コレクションを登録する必要はありません。
🍀 preset-attributify
このプリセットで属性化モードを利用できます。
ユーティリティをクラスではなく HTML 属性を用いることが可能になります。
import { defineConfig, 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>
と、属性に分割できます。
text-sm
と text-white
のように重複したプレフィックスを除いてグループ化(text="sm white"
)することができます。
また、flex
, grid
, border
のようにプレフィックスと全く同じ名前のユーティリティについては ~
が使えます。
<button class="border border-red">Button</button>
<button border="~ red">Button</button>
値のない属性も使用できます。
<div class="m-2 rounded text-teal-400">Valueless Attribute</div>
<div m-2 rounded text-teal-400>Valueless Attribute</div>
🏷️ preset-tagify
このプリセットでタグ化モードを利用できます。
このプリセットは特定の要素に対して単一の UnoCSS ルールを適用する場合に便利です。
import { defineConfig, presetTagify } from "unocss";
export default defineConfig({
presets: [
presetTagify({ /* プリセットオプション */ }),
],
});
タグ化モードを使えば、
<span class="text-red"> red text </span>
<div class="flex">flexbox</div>
I'm feeling <span class="i-line-md-emoji-grin"></span> today!
は、
<text-red> red text </text-red>
<flex> flexbox </flex>
I'm feeling <i-line-md-emoji-grin /> today!
と書くことができます。
プレフィックスを付与することもできます。
export default defineConfig({
presets: [
presetTagify({
prefix: "uno-",
}),
],
});
🔠 preset-web-fonts
@unocss/preset-web-fonts
プリセットを使うことで、フォント名を指定するだけで簡単に Web フォントを利用できます。
import { defineConfig, presetWebFonts, presetUno } from "unocss";
export default defineConfig({
presets: [
presetUno(),
presetWebFonts({
provider: "google",
fonts: {
sans: "Roboto",
mono: ["Fira Code", "Fira Mono:400,700"],
},
}),
],
})
2024 年 8 月現在、フォントのプロバイダは
-
google
: Google Fonts -
bunny
: Bunny Fonts -
fontshare
: Fontshare
のみです。
また、none
を指定することで、フォントをシステムフォントとして扱うことができます。
🖌️ preset-typography
@unocss/preset-typography
プリセットにより、標準の HTML 要素にタイポグラフィスタイルを簡単に適用するための 「散文("prose")」クラスが提供されています。
これらの「散文」を使用することで、例えば段落や見出し、リスト、リンクなどの要素に対して、統一されたタイポグラフィのスタイルを適用できます。
import { defineConfig, presetUno, presetTypography } from "unocss";
export default defineConfig({
presets: [
presetUno(), // 必須
presetTypography(),
],
});
<article text-base prose prose-truegray xl="text-xl">
{{ markdown }}
<p class="not-prose">Some text</p>
</article>
要素に not-prose
を適用することでタイポグラフィスタイルを取り消すことができます。
マークダウンの例
import { defineConfig, presetUno, presetTypography } from "unocss";
export default defineConfig({
presets: [
presetUno(), // 必須
presetTypography({
selectorName: "markdown",
// cssExtend は CSS セレクタをキー、CSS 宣言ブロックを値として持つオブジェクト
cssExtend: {
"code": {
color: "#8B5CF6",
},
"a:hover": {
color: "#F43F5E",
},
"a:visited": {
color: "#14B8A6",
},
},
}),
],
});
<div class="flex flex-col gap-y-25 justify-center items-center min-h-screen">
<div class="markdown">
<h1>タイトル</h1>
<p>テキストテキスト<a href="#">リンク</a>テキストテキスト</p>
<p>コード: <code>console.log("Hello World!");</code></p>
<p class="not-markdown">
`not` を使うと、<code>console.log("Hello World!");</code> <-
スタイルが当たらない!
</p>
</div>
<div class="markdown markdown-yellow">
<h1>タイトル</h1>
<p>テキストテキスト<a href="#">リンク</a>テキストテキスト</p>
<p>コード: <code>console.log("Hello World!");</code></p>
<p class="not-markdown">
`not` を使うと、<code>console.log("Hello World!");</code> <-
スタイルが当たらない!
</p>
</div>
</div>
💇 preset-rem-to-px
@unocss/preset-rem-to-px
プリセットを用いることで rem
を px
へ変換することができます。
(デフォルトでは 1rem = 16px
)
import { defineConfig } from "unocss";
import presetRemToPx from "@unocss/preset-rem-to-px";
export default defineConfig({
rules:[
[/^m-([.\d]+)$/, ([, num]) => ({ margin: `${num}rem` })],
],
presets: [presetRemToPx()],
});
<div class="m-2">rem to px</div>
からは
.m-2 {
margin: 2rem;
}
が生成されますが、@unocss/preset-rem-to-px
を使えば、
.m-2 {
margin: 32px;
}
となります。
💭 まとめ
UnoCSS はフレームワークではなくエンジンであると謳っていますが、強力な公式プリセットがたくさん提供されています。
公式プリセットを使うことで、すぐに開発を開始できるだけでなく、UnoCSS のパワーをもっと引き出すことができます。
プリセット名 | 説明 |
---|---|
@unocss/preset-uno |
デフォルトプリセット |
@unocss/preset-mini |
最小かつ必要不可欠なルールとバリアント |
@unocss/preset-wind |
Tailwind CSS, Windi CSS のコンパクトなプリセット |
@unocss/preset-attributify |
属性化モード |
@unocss/preset-tagify |
タグ化モード |
@unocss/preset-icons |
純粋な CSS によるアイコン Powered by Iconify |
@unocss/preset-web-fonts |
Web フォント |
@unocss/preset-typography |
タイポグラフィプリセット |
@unocss/preset-rem-to-px |
rem を px に変換 |
@unocss/preset-wind
は、Tailwind CSS や Windi CSS に慣れ親しんでいるユーザーにとってうってつけのプリセットです。
Tailwind CSS の魅力をそのままに、軽量でシンプルな使い勝手を提供します。
学習コストも小さく、今までの知識を活かしながらすぐに UnoCSS を使い始めることができるでしょう。
ぼくの推し公式プリセットは @unocss/preset-attributify
です 😎
@unocss/preset-attributify
を用いれば、HTML 属性としてスタイルを指定できます。
コードがより直感的で読みやすくなるのが個人的に好きです。
💚 最後に
この記事の執筆にあたって GANGAN さんにレビューしていただき、たくさんのアドバイスをもらいました。
本当にありがとうございます。
今回の記事が UnoCSS を利用する際の参考になれば幸いです。
UnoCSS の魅力をもっと知りたい方は公式サイトやコミュニティリソースを活用して、積極的に情報を収集してみてください。
公式プリセットだけでなくコミュニティが作成したプリセットも多く存在します。
これらのプリセットは特定のニーズやユースケースに特化していることが多いです。
コミュニティプリセットを見ることでいろんなアイデアをゲットできるだけでなく、より深く UnoCSS を知ることができるでしょう。
今後 UnoCSS ユーザがもっと増えて、日本語情報が増えることを強く願っています。
一緒にコミュニティを盛り上げていきましょう 💪
最後まで読んでいただきありがとうございました!
Discussion
記事をありがとうございます!
ちなみにアイコンについてですが、自分はIconsで欲しいアイコンを探して、installはauto installを用いています(npmは見に行ったことがない)。とても楽です。
ryoppippi さん
ありがとうございます
auto install というのがあるんですね
絶対その方法がいいですね!
調べてみて、追記させていただきます🙇♂️