🎴

もっとくわしく UnoCSS ~公式プリセット篇~

2024/08/18に公開
2

🫡 はじめに

ナイトウ(@engineer_naito)と申します。
今回は CSS フレームワーク Atomic CSS エンジンである UnoCSS について紹介したいと思います。

https://zenn.dev/comm_vue_nuxt/articles/what_is_unocss

こちらの記事では、UnoCSS の成り立ちや特徴を紹介しました
今回は UnoCSS の公式プリセットについて紹介したいと思います。

公式プリセットを活用することで UnoCSS をパワーを最大限に生かすことができます。
公式プリセット(UnoCSS の特徴)を知って、UnoCSS を好きになっていただきたいです。

また、前回記事で作者の Anthony Fu さんからコメントをいただきました!!!

https://zenn.dev/comm_vue_nuxt/articles/what_is_unocss#comment-ba63c20e23e285

antfu さんが前回記事にコメントをくださりました

質問があれば思い切って質問してみてはいかがでしょうか 💁

📑 ドキュメント

https://unocss.dev/

https://github.com/unocss/unocss

🛝 公式 Playground

https://unocss.dev/play/

公式 Playground が提供されています。
こちらを活用して、UnoCSS を体験してみてください。

また、この記事内でサンプルコード付きの公式 Playground へのリンクを用意しています。
(e.g. 公式 Playground 👈)
ぜひ実際にコードを動かして UnoCSS を体験しながら記事を読んでください 👍

1️⃣ UnoCSS とは

UnoCSS 公式ドキュメントトップページ のスクショ

Instant On-demand Atomic CSS Engine

Customizable · Powerful · Fast · Joyful

UnoCSS は、オンデマンドで動作する Atomic CSS エンジンです。
Tailwind CSS, Windi CSS の影響を受け、より迅速柔軟なスタイル定義を可能にするために設計されました。

🛠️ プリセット

プリセットは UnoCSS の心臓です。

UnoCSS のプリセットはあらかじめ定義された一連のスタイルやルールのセットです。
プリセットを使うことで共通のスタイルや機能を手軽にプロジェクトに追加でき、設定する手間が省けます。

プリセットは公式で提供されているものもありますし、自分でカスタムプリセットを作成することも可能です。
コミュニティプリセットもあります。

プリセットを利用するには uno.config.ts に記述します。

uno.config.ts
import { defineConfig, presetAttributify, presetUno } from "unocss";

export default defineConfig({
  presets: [
    presetAttributify({ /* プリセットのオプション */}),
    presetUno(),
    // ...
  ],
});

独自のプリセットを定義することもできます。

my-preset.ts
import { Preset } from "unocss";

export const myPreset: Preset = {
  name: "my-preset",
  rules: [
    [/^m-([.\d]+)$/, ([_, num]) => ({ margin: `${num}px` })],
    [/^p-([.\d]+)$/, ([_, num]) => ({ padding: `${num}px` })],
  ],
  // ...
};
uno.config.ts
import { defineConfig } from "unocss";
import { myPreset } from "./my-preset";

export default defineConfig({
  presets: [
    myPreset,
  ],
});

📌 公式プリセット

https://unocss.dev/presets/#presets

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 rempx に変換

公式 Playground で公式プリセットを利用したグリッドレイアウトのスクショ

公式 Playground


公式プリセットを順番に見ていきましょう。

☝️ preset-uno

UnoCSS のデフォルトプリセットです。
@unocss/preset-wind, @unocss/prest-mini を継承しています。

uno.config.ts
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;
}

ユーティリティの詳細は各フレームワークの公式ドキュメントを参照するのがよいでしょう。

https://tailwindcss.com/

https://windicss.org/


💨 preset-wind

@unocss/preset-wind は、UnoCSS で Tailwind CSS, Windi CSS のユーティリティを利用できるプリセットです。

uno.config.ts
import { defineConfig, presetWind } from "unocss";

export default defineConfig({
  presets: [
    presetWind(),
  ],
});

🐤 preset-mini

UnoCSS のためのベーシックなプリセットです。

uno.config.ts
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);
  }
}

またはプリセットオプションで設定することができます。

uno.config.ts
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>

公式 Playground

<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>

公式 Playground での preset-icons の使用例

🧑‍💻 インストール

Iconify はアイコンのデータソースとして用います。
アイコンセットを使用するには、@iconify-json/* パターンに従って、
該当するアイコンセットを devDependencies にインストールする必要があります。
(例: @iconify-json/mdi, @iconify-json/tabler)

アイコンセットとパッケージ名は以下で確認できます。

https://github.com/iconify/icon-sets/blob/master/collections.md

利用可能な全てのコレクションは IcônesIconify で参照することができます。


2024.08.19 追記

コメントで autoInstall オプションについて教えていただきました。

uno.config.ts
import { defineConfig, presetIcons } from "unocss";

export default defineConfig({
  presets: [
    presetIcons({
      autoInstall: true,
    }),
  ],
});

とすることで、必要なアイコンセットを UnoCSS が自動的にインストールしてくれます。
手動でパッケージをインストールする必要がなくなり、開発がスムーズに進められるようになります。

➕ 追加プロパティ

アイコンのデフォルトの動作を制御するために、追加の CSS プロパティを指定することができます。

uno.config.ts
import { defineConfig, presetIcons } from "unocss";

export default defineConfig({
  presets: [
    presetIcons({
      extraProperties: {
        "display": "inline-block",
        "vertical-align": "middle",
        // ...
      },
    }),
  ],
});

✍️ モードの上書き

公式 Playground

デフォルトではこのプリセットはアイコンの特性に基づいて、それぞれのアイコンに対してレンダリングモードを自動的に選択します。
ただし、場合によっては各アイコンに対してレンダリングモードを明示的に設定したいこともあるでしょう。

  • ?bgbackground-image を意味し、アイコンを背景画像としてレンダリングします。
  • ?maskmask を意味し、アイコンをマスク画像としてレンダリングします。
<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 ファイルは巨大だからです。

🎀 バンドラー

バンドラーを使用するのであれば、コレクションを動的インポートとして提供できます。
コレクションは非同期チャンクとしてバンドルされ、必要なときに読み込まれます。

uno.config.ts
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 が推奨されています。

uno.config.ts
import { defineConfig, presetIcons } from "unocss";

export default defineConfig({
  presets: [
    presetIcons({
      cdn: "https://esm.sh/",
    }),
  ],
});
🧰 カスタムコレクション

公式 Playground

CustomIconLoaderInlineCollection を利用して、独自のカスタムコレクションを提供することもできます。

uno.config.ts
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 属性を用いることが可能になります。

uno.config.ts
import { defineConfig, presetAttributify } from "unocss";

export default defineConfig({
  presets: [
    presetAttributify({ /* プリセットオプション */ }),
  ],
});

公式 Playground

ユーティリティクラスを用いてボタンを作成しようとすると、以下のようになることがあります。

<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-smtext-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 ルールを適用する場合に便利です。

uno.config.ts
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!

と書くことができます。

プレフィックスを付与することもできます。

uno.config.ts
export default defineConfig({
  presets: [
    presetTagify({
      prefix: "uno-",
    }),
  ],
});

🔠 preset-web-fonts

@unocss/preset-web-fonts プリセットを使うことで、フォント名を指定するだけで簡単に Web フォントを利用できます。

config.uno.ts
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 月現在、フォントのプロバイダは

のみです。

また、none を指定することで、フォントをシステムフォントとして扱うことができます。

🖌️ preset-typography

@unocss/preset-typography プリセットにより、標準の HTML 要素にタイポグラフィスタイルを簡単に適用するための 「散文("prose")」クラスが提供されています。
これらの「散文」を使用することで、例えば段落や見出し、リスト、リンクなどの要素に対して、統一されたタイポグラフィのスタイルを適用できます。

uno.config.ts
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 を適用することでタイポグラフィスタイルを取り消すことができます。


マークダウンの例

公式 Playground

公式 Playground で preset-typography を使用したスクショ


uno.config.ts
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 プリセットを用いることで rempx へ変換することができます。
(デフォルトでは 1rem = 16px)

uno.config.ts
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 rempx に変換

@unocss/preset-wind は、Tailwind CSS や Windi CSS に慣れ親しんでいるユーザーにとってうってつけのプリセットです。
Tailwind CSS の魅力をそのままに、軽量でシンプルな使い勝手を提供します。
学習コストも小さく、今までの知識を活かしながらすぐに UnoCSS を使い始めることができるでしょう。

ぼくの推し公式プリセットは @unocss/preset-attributify です 😎
@unocss/preset-attributify を用いれば、HTML 属性としてスタイルを指定できます。
コードがより直感的で読みやすくなるのが個人的に好きです。

💚 最後に

この記事の執筆にあたって GANGAN さんにレビューしていただき、たくさんのアドバイスをもらいました。
本当にありがとうございます。

https://github.com/shinGangan/comm_vue_nuxt/issues/62


今回の記事が UnoCSS を利用する際の参考になれば幸いです。
UnoCSS の魅力をもっと知りたい方は公式サイトやコミュニティリソースを活用して、積極的に情報を収集してみてください。

公式プリセットだけでなくコミュニティが作成したプリセットも多く存在します。

https://unocss.dev/presets/community

これらのプリセットは特定のニーズやユースケースに特化していることが多いです。
コミュニティプリセットを見ることでいろんなアイデアをゲットできるだけでなく、より深く UnoCSS を知ることができるでしょう。

今後 UnoCSS ユーザがもっと増えて、日本語情報が増えることを強く願っています。
一緒にコミュニティを盛り上げていきましょう 💪

最後まで読んでいただきありがとうございました!

GitHubで編集を提案
Vue・Nuxt 情報が集まる広場 / Plaza for Vue・Nuxt.

Discussion

ryoppippiryoppippi

記事をありがとうございます!
ちなみにアイコンについてですが、自分はIconsで欲しいアイコンを探して、installはauto installを用いています(npmは見に行ったことがない)。とても楽です。

ナイトウナイトウ

ryoppippi さん

ありがとうございます
auto install というのがあるんですね
絶対その方法がいいですね!
調べてみて、追記させていただきます🙇‍♂️