コンテンツへスキップ

Svelte スコープ

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

このコンポーネント

svelte
<div class="mb-1" />

は、以下のように変換されます

svelte
<div class="uno-ei382o" />

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

いつ使うか

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

仕組み

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

使い方

Svelte スコープはユーティリティクラス名を書き換えるため、それらを記述できる場所が制限されます

サポートされる構文
class属性<div class="mb-1" />
classディレクティブ<div class:mb-1={condition} />
classディレクティブの短縮形<div class:logo />
classプロパティ<Button class="mb-1" />

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

コンテキストを認識

スタイルはアプリのSvelteコンポーネントに分散されますが、それらは依然としてグローバルクラスであり、特定のコンポーネント外にある要素との関係で機能します。いくつかの例を示します

親依存

親コンポーネントにある属性に依存するクラス

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

は、次のように変化します

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

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

子への影響

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

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

は、次のように変化します

svelte
<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プロパティを追加して、そのコンポーネントが消費される場所でカスタムクラスを渡せるようにできます。

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

は、次のように変化します

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

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

適用ディレクティブ

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

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

svelte
<div />

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

は、以下のように変換されます

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

インストール

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

プラグインの追加

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

vite.config.ts
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', // see type definition for all included reset options or how to pass in your own
      // ...other Svelte Scoped options
    }),
    sveltekit(),
  ],
})

設定ファイルの追加

下記の説明に従って、uno.config.tsファイルを設定します。

グローバルスタイル

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

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

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

SvelteKitを使用している場合は、src/hooks.server.jsファイルのtransformPageChunkフックに以下も追加する必要があります

src/hooks.server.js
js
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
  const response = await resolve(event, {
    transformPageChunk: ({ html }) =>
      html.replace(
        '%unocss-svelte-scoped.global%',
        'unocss_svelte_scoped_global_styles'
      ),
  })
  return response
}

この変換は、パスにhooksserverを含むファイル(例:src/hooks.server.jssrc/hooks.server.ts)にある必要があります。svelte-scopedは、サーバーフックファイルでunocss_svelte_scoped_global_stylesをグローバルスタイルに置き換えるためです。@sveltejs/kit/hookssequenceを使用する場合など、別のファイルからこの変換をインポートしないようにしてください。

通常のSvelteプロジェクトでは、ViteのtransformIndexHtmlフックがこれを自動的に実行します。

Svelteプリプロセッサ

生成されたスタイルをビルドされたコンポーネントに直接配置するプリプロセッサを使用することにより、コンパニオンCSSファイルを含めることに依存しないコンポーネントライブラリを構築するために、ユーティリティスタイルを使用します。StackblitzのSvelteKitライブラリの例をご覧ください

Open in StackBlitz

インストール

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

プリプロセッサの追加

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

svelte.config.js
ts
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({
      // ... preprocessor options
    }),
  ],
  // other Svelte config
}

開発環境ではクラス名を結合しない

通常のアプリで Svelte Scoped を使用する場合、Vite プラグインは自動的に devbuild を検出します。開発時には、クラスは区別され、ブラウザの開発者ツールでオン/オフを切り替えやすくするために、その場でハッシュ化されます。class="mb-1 mr-1"class="_mb-1_9hwi32 _mr-1_84jfy4 のようになります。本番環境では、これらは、デフォルトでは uno- のような、指定したプレフィックスと、ファイル名 + クラス名に基づいてハッシュ化された1つのクラス名にコンパイルされます。例: 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 属性として追加することで、プリフライトが必要な特定のコンポーネントに含めるオプションがあります。

html
<style uno-preflights></style>

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

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

セーフリスト

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

html
<style uno-safelist></style>

セーフリストのスタイルは、Svelte コンパイラによって自動的に削除されるのを避けるために、:global() でラップされます。

設定

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

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

export default defineConfig({
  // ...UnoCSS options
})

通常の UnoCSS のグローバルな使用と Svelte Scoped の使用の違いにより、エクストラクターはサポートされていません。プリセットとトランスフォーマーは、次のセクションで説明するようにサポートされています。その他のすべての詳細については、設定ファイルおよび設定リファレンスを参照してください。

プリセットのサポート

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

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

他のプリセットの場合、従来の class="..." の使用に依存していない場合は、最初にそれらのクラス名を class="..." 属性に前処理する必要があります。typography の .prose クラスのようにプリセットを追加する場合は、プリセットの追加をトリガーするクラスをセーフリストに配置する必要があります。

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

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

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

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

情報

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

スコープ付きユーティリティクラスが創造性を解き放つ

スコープ付きスタイルを使用する際に役立つアドバイスをいくつか紹介します。大規模なプロジェクトのライフサイクルにおいて、.md:max-w-[50vw] のようなクラスを一度しか使用しないとわかっている場合に、グローバルスタイルシートのサイズがどんどん大きくなっていると感じてうんざりするような状況に陥った場合は、このパッケージを試してみてください。必要なクラスを正確に使用することへのためらいは、創造性を阻害します。確かに、スタイルブロックで --at-apply: md:max-w-[50vw] を使用することもできますが、それは面倒であり、コンテキスト内のスタイルは便利です。さらに、プロジェクトに多種多様なアイコンを含めたい場合、それらをグローバルスタイルシートに追加することの重さを感じ始めるでしょう。各コンポーネントが独自のスタイルとアイコンの重みを担うとき、新しい追加ごとに費用対効果を分析することなく、プロジェクトを拡張し続けることができます。

ライセンス

MIT License の下でリリースされました。