UnoCSS Svelte Scoped

UnoCSSのためのSvelte Scoped ViteプラグインとSvelteプリプロセッサ。

Svelte Scoped

各Svelteコンポーネントのユーティリティスタイルの生成されたCSSを、グローバルCSSファイルではなく、Svelteコンポーネントの<style>ブロックに直接配置します。

このコンポーネント:

<div class="mb-1" />

は次のように変換されます:

<div class="uno-ei382o" />

<style>
  :global(.uno-ei382o) {
    margin-bottom: 0.25rem;
  }
</style>

使用するタイミング

ユースケース説明使用するパッケージ
小規模アプリ:x:1つのグローバルCSSファイルを持つ方が便利です。Svelte/SvelteKit用の通常のViteプラグインを使用してください。unocss/vite
大規模アプリSvelte Scopedは、増え続けるグローバルCSSファイルを避けるのに役立ちます。@unocss/svelte-scoped/vite
コンポーネントライブラリ生成されたスタイルは、消費するアプリのビルドパイプラインでUnoCSSを使用する必要なく、ビルドされたコンポーネントに直接配置されます。@unocss/svelte-scoped/preprocess

仕組み

通常のUnoCSS/Tailwind CSSのセットアップでは、ユーティリティスタイルを適切な順序でグローバルCSSファイルに配置します。対照的に、Svelte Scopedはスタイルを多くの任意の順序のSvelteコンポーネントCSSファイルに分散させます。しかし、ユーティリティスタイルをグローバルに保つ必要があり、右から左へのスタイルや以下にリストされる他のユースケースのために必要に応じてコンテキストを認識できるようにする必要があります。この課題は、Svelteの:global()ラッパーを使用して、デフォルトのSvelte CSSハッシュ化メソッドをオプトアウトし、ファイル名とクラス名に基づくハッシュを使用して、スタイルの競合なしにグローバルにできるユニークなクラス名をコンパイルすることで解決されます。

使用法

Svelte Scopedはユーティリティクラス名を書き換えるため、書く場所が制限されます:

サポートされる構文
クラス属性<div className="mb-1" />
クラスディレクティブ<div class:mb-1={condition} />
クラスディレクティブ省略形<div />
クラスプロップ<Button className="mb-1" />
clsx ライク<div className={["mb-1", { logo, 'font-bold': isBold() }, isUnderlined() && 'underline' ]} />

Svelte Scopedはユーティリティスタイルを使用するプロジェクトのドロップイン置換として設計されています。そのため、クラス属性内で見つかる式もサポートされています(例:<div className="mb-1 {foo ? 'mr-1' : 'mr-2'}" />)が、今後はclsx構文を使用することをお勧めします。また、クラス名を<script>ブロックに配置したり、attributifyモードを使用したりするなど、他の方法で使用している場合は、Svelte Scopedを使用する前に追加の手順が必要です。safelistオプションを利用し、以下のpresetsセクションでさらにヒントを確認できます。

コンテキスト認識

スタイルはアプリのSvelteコンポーネント全体に分散されますが、それでもグローバルクラスであり、特定のコンポーネントの外にある要素に関連して機能します。以下はいくつかの例です:

親依存

親コンポーネントに見つかる属性に依存するクラス:

<div class="dark:mb-2 rtl:right-0"></div>

は次のように変換されます:

<div class="uno-3hashz"></div>

<style>
  :global(.dark .uno-3hashz) {
    margin-bottom: 0.5rem;
  }
  :global([dir="rtl"] .uno-3hashz) {
    right: 0rem;
  }
</style>

子供の影響

別のコンポーネントにある3つの子要素の間にスペースを追加できます:

<div class="space-x-1">
  <div>Status: online</div>
  <Button>FAQ</Button>
  <Button>Login</Button>
</div>

は次のように変換されます:

<div class="uno-7haszz">
  <div>Status: online</div>
  <Button>FAQ</Button>
  <Button>Login</Button>
</div>

<style>
  :global(.uno-7haszz > :not([hidden]) ~ :not([hidden])) {
    --un-space-x-reverse: 0;
    margin-left: calc(0.25rem * calc(1 - var(--un-space-x-reverse)));
    margin-right: calc(0.25rem * var(--un-space-x-reverse));
  }
</style>

子コンポーネントへのクラスの渡し

コンポーネントにclassプロップを追加して、そのコンポーネントが消費される場所にカスタムクラスを渡すことができます。

<Button class="px-2 py-1">Login</Button>

は次のように変換されます:

<Button class="uno-4hshza">Login</Button>

<style>
  :global(.uno-4hshza) {
    padding-left:0.5rem;
    padding-right:0.5rem;
    padding-top:0.25rem;
    padding-bottom:0.25rem;
  }
</style>

受信コンポーネントでクラスを実装する簡単な方法は、{$$props.class}を使用して要素に配置することです。例:div class="{$$props.class} foo bar" />

ディレクティブの適用

<style>ブロック内で--at-apply@apply、またはapplyVariablesオプションを使用して設定されたカスタム値を使用してディレクティブを適用できます。

Svelte Scopedは、通常の@unocss/transformer-directivesパッケージが正しく処理できないdark:text-whiteのようなコンテキスト依存のクラスも適切に処理します。これはSvelteスタイルブロック専用に構築されていないためです。例えば、Svelte Scopedを使用すると、このコンポーネント:

<div />

<style>
  div {
    --at-apply: rtl:ml-2;
  }
</style>

は次のように変換されます:

<div />

<style>
  :global([dir=\\"rtl\\"]) div {
    margin-right: 0.5rem;
  }
</style>

rtl:ml-2が正しく機能するためには、[dir="rtl"]セレクタが:global()でラップされて、コンポーネントにその属性を持つ要素がないためにSvelteコンパイラが自動的に削除しないようにします。しかし、div:global()ラッパーに含めることができません。そうすると、そのスタイルがアプリ内のすべてのdivに影響を与えるからです。

その他のスタイルブロックディレクティブ

theme()の使用もサポートされていますが、@screenサポートされていません

Viteプラグイン

SvelteまたはSvelteKitアプリで、生成されたスタイルをSvelteコンポーネントに直接注入し、必要最小限のスタイルをグローバルスタイルシートに配置します。StackblitzのSvelteKitの例をチェックしてください:

Open in StackBlitz

インストール

::: code-group

pnpm
pnpm add -D unocss @unocss/svelte-scoped
yarn
yarn add -D unocss @unocss/svelte-scoped
npm
npm install -D unocss @unocss/svelte-scoped
bun
bun add -D unocss @unocss/svelte-scoped

:::

プラグインを追加

Viteの設定に@unocss/svelte-scoped/viteを追加します:

vite.config.ts
import { sveltekit } from '@sveltejs/kit/vite'
import UnoCSS from '@unocss/svelte-scoped/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    UnoCSS({
      // injectReset: '@unocss/reset/normalize.css', // すべてのリセットオプションの型定義や独自のリセットを渡す方法を参照
      // ...その他のSvelte Scopedオプション
    }),
    sveltekit(),
  ],
})

設定ファイルを追加

以下に記載されているようにuno.config.tsファイルを設定します。

グローバルスタイル

ほとんどのスタイルは個々のコンポーネントに配置されますが、プリフライト、セーフリスト、およびオプションのリセット(injectResetオプションを使用する場合)はグローバルスタイルシートに配置する必要があります。

%unocss-svelte-scoped.global%プレースホルダーを<head>タグに追加します。Svelteではindex.htmlです。SvelteKitでは、%sveltekit.head%の前にapp.htmlに配置します:

index.html
<head>
  <!-- ... -->
  <title>SvelteKit using UnoCSS Svelte Scoped</title>
  %unocss-svelte-scoped.global%
  %sveltekit.head%
</head>

Svelte プリプロセッサ

ユーティリティスタイルを使用して、生成されたスタイルをビルドされたコンポーネントに直接配置するプリプロセッサを使用することで、付随する CSS ファイルに依存しないコンポーネントライブラリを構築します。Stackblitz の SvelteKit ライブラリの例をチェックしてください:

Open in StackBlitz

インストール

::: code-group

pnpm
pnpm add -D unocss @unocss/svelte-scoped
yarn
yarn add -D unocss @unocss/svelte-scoped
npm
npm install -D unocss @unocss/svelte-scoped
bun
bun add -D unocss @unocss/svelte-scoped

:::

プリプロセッサを追加

@unocss/svelte-scoped/preprocess を Svelte の設定に追加します:

svelte.config.js
import adapter from '@sveltejs/adapter-auto'
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
import UnoCSS from '@unocss/svelte-scoped/preprocess'

const config = {
  preprocess: [
    vitePreprocess(),
    UnoCSS({
      // ... プリプロセッサオプション
    }),
  ],
  // その他の Svelte 設定
}

開発中にクラス名を結合しない

通常のアプリで Svelte Scoped を使用する場合、Vite プラグインは自動的に devbuild を検出します。開発中は、クラスが区別され、ブラウザの開発者ツールでのオン/オフの切り替えを容易にするためにハッシュ化されます。class="mb-1 mr-1"class="_mb-1_9hwi32 _mr-1_84jfy4 のようになります。プロダクションでは、これらはデフォルトで uno- というプレフィックスとファイル名 + クラス名に基づくハッシュを使用して単一のクラス名にコンパイルされます。例:class="uno-84dke3

プリプロセッサを使用する際に同じ動作を望む場合は、環境に基づいて combine オプションを手動で設定する必要があります。一つの方法として、cross-env をインストールし、開発スクリプトを次のように更新します:

"dev": "cross-env NODE_ENV=development vite dev"

その後、svelte.config.js を調整します:

+const prod = process.env.NODE_ENV !== 'development'
const config = {
  preprocess: [
    vitePreprocess(),
    UnoCSS({
+      combine: prod,
    }),
  ],
}

設定ファイルを追加

uno.config.ts ファイルを以下のように設定します:下記を参照してください。

プリフライト

プリプロセッサを使用する際、必要な特定のコンポーネントに uno-preflights をスタイル属性として追加することでプリフライトを含めるオプションがあります。

<style uno-preflights></style>

.prose :where(a):not(:where(.not-prose, .not-prose *)) のようにピリオドで始まる特別なプリフライトは、Svelte コンパイラによって自動的に削除されないように :global() でラップされます。

クラスがプリフライトに依存していない場合や、ビルドされたコンポーネントが既にプリフライトを含むアプリでのみ使用される場合、個々のコンポーネントにプリフライトを追加する必要はありません。

セーフリスト

プリプロセッサを使用する際、uno-safelist をスタイル属性として追加することで、コンポーネントにセーフリストクラスを含めるオプションがあります。

<style uno-safelist></style>

セーフリストスタイルは、Svelte コンパイラによって自動的に削除されないように :global() でラップされます。

設定

UnoCSS の設定を uno.config.ts ファイルに配置します:

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

export default defineConfig({
  // ...UnoCSS オプション
})

通常の UnoCSS のグローバル使用法と Svelte Scoped 使用法の違いにより、エクストラクタはサポートされていません。プリセットとトランスフォーマーは以下のセクションで説明されているようにサポートされています。詳細は Config File および Config reference を参照してください。

プリセットのサポート

グローバルスタイルシートに必要なスタイルがいくつかあり、その他は必要に応じて各コンポーネントに含まれるという性質上、プリセットはケースバイケースで処理する必要があります:

プリセットサポート備考
@unocss/preset-uno, @unocss/preset-mini, @unocss/preset-wind3, @unocss/preset-icons, @unocss/web-fontsこれらおよびすべてのコミュニティプラグイン、例: unocss-preset-forms、はルール/バリアント/プリフライトにのみ依存するため動作します。
@unocss/preset-typographyこのプリセットがプリフライトにルールセットを追加する方法のため、このプリセットを使用する際には prose クラスをセーフリストに追加する必要があります。そうしないとプリフライトがトリガーされません。このプリセットの他のすべてのクラス、例: prose-pink はコンポーネントスコープにできます。
v66.5.0 以降、prose スタイルは rule にリファクタリングされ、クラスをセーフリストに追加する必要がなくなりました。
@unocss/preset-rem-to-pxスタイル出力を変更するだけのこのプリセットやそれに類似するすべてのプリセットは動作します。
@unocss/preset-attributify-プリセットは動作しません。代わりに unplugin-attributify-to-class Vite プラグイン (attributifyToClass({ include: [/\.svelte$/]})) を Svelte Scoped Vite プラグインの前に使用してください。
@unocss/preset-tagify-カスタムエクストラクタを追加するプリセットは動作しません。<text-red>Hi</text-red><span className="text-red">Hi</span> に変換するプリプロセッサを作成し、ここにリンクを追加する PR を作成してください。

他のプリセットについては、従来の class="..." の使用に依存しない場合、まずそれらのクラス名を class="..." 属性にプリプロセスする必要があります。タイポグラフィの .prose クラスのようなプリセットを追加する場合は、プリセットの追加をトリガーするクラスをセーフリストに配置する必要があります。

トランスフォーマーのサポート

トランスフォーマーは CSS ファイル (css|postcss|sass|scss|less|stylus|styl) に対してサポートされています。使用するには、vite.config.tscssFileTransformers オプションにトランスフォーマーを追加します:

vite.config.ts
import transformerDirectives from '@unocss/transformer-directives'

export default defineConfig({
  plugins: [
    UnoCSS({
      cssFileTransformers: [transformerDirectives()],
    }),
    sveltekit(),
  ],
})

::: info トランスフォーマーは Svelte コンポーネントでは Svelte Scoped の動作のためサポートされていません。 :::

スコープされたユーティリティクラスで創造性を解放

スコープされたスタイルを使用する際のアドバイス:大規模プロジェクトで .md<MaxW>50vw</MaxW> のようなクラスを使用するたびに、グローバルスタイルシートがどんどん大きくなるのを感じてうんざりするようになったら、このパッケージを試してみてください。必要なクラスを正確に使用することへの躊躇は創造性を妨げます。もちろん、スタイルブロックで --at-apply: md<MaxW>50vw</MaxW> を使用することもできますが、それは面倒で、コンテキスト内のスタイルは有用です。さらに、プロジェクトに多様なアイコンを含めたい場合、グローバルスタイルシートにそれらを追加する重みを感じ始めるでしょう。各コンポーネントが自身のスタイルとアイコンの重みを負うとき、各新しい追加のコストベネフィットを分析することなくプロジェクトを拡張し続けることができます。

ライセンス