Skip to content

Svelte Scoped

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

このコンポーネント:

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

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

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

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

どんな時に使うか

ユースケース説明使用パッケージ
小規模アプリグローバルCSSファイル1つの方が便利です。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はユーティリティクラス名を書き換えるため、書ける場所が制限されます:

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

Svelte Scopedはユーティリティスタイルを使うプロジェクトのドロップイン置換を目指しています。そのため、class属性内の式(例:<div class="mb-1 {foo ? 'mr-1' : 'mr-2'}" />)もサポートしますが、今後はclassディレクティブ構文の利用を推奨します。<script>ブロック内でクラス名を使ったり、attributifyモードを使っている場合は追加の対応が必要です。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>

受け取り側コンポーネントでは、{$$props.class}を使って要素にクラスを付与できます(例:div class="{$$props.class} foo bar" />)。

Applyディレクティブ

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

Svelte Scopedは、dark:text-whiteのようなコンテキスト依存クラスも正しく処理します(通常の@unocss/transformer-directivesパッケージは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()でラップされます。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
bash
bun add -D unocss @unocss/svelte-scoped

プラグイン追加

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

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ファイルを下記の通りにセットアップします。

グローバルスタイル

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

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

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

SvelteKitを使う場合は、src/hooks.server.jsファイルのtransformPageChunkフックにも以下を追加してください:

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/hooks]のsequenceなどで他ファイルからインポートしないよう注意してください。

通常の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
bash
bun add -D unocss @unocss/svelte-scoped

プリプロセッサ追加

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

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({
      // ... プリプロセッサオプション
    }),
  ],
  // その他のSvelte設定
}

開発時はクラス名を結合しない

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スクリプトを次のようにします:

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

そしてsvelte.config.jsを次のように調整します:

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

設定ファイル追加

uno.config.tsファイルを下記の通りにセットアップします。

Preflights

プリプロセッサ利用時は、必要なコンポーネントでuno-preflights属性付きのstyleタグを追加してpreflightsを含めることができます。

html
<style uno-preflights></style>

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

クラスがpreflightsに依存しない場合や、ビルド済みコンポーネントがpreflightsを含むアプリでのみ使われる場合は、個別コンポーネントへのpreflights追加は不要です。

Safelist

プリプロセッサ利用時は、uno-safelist属性付きのstyleタグを追加してsafelistクラスを含めることができます。

html
<style uno-safelist></style>

safelistスタイルはSvelteコンパイラで自動的に除去されないよう:global()でラップされます。

設定

UnoCSSの設定はuno.config.tsファイルに記述します:

ts
import { defineConfig } from 'unocss'

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

extractorsは通常のUnoCSSグローバル利用とSvelte Scoped利用の違いによりサポートされていません。プリセットとトランスフォーマーは下記の通りサポートされています。他の詳細はConfig FileConfig referenceを参照してください。

プリセットサポート

グローバルスタイルシートに必要なスタイルが少しだけあり、それ以外は各コンポーネントに含めるという性質上、プリセットは個別に対応が必要です:

プリセットサポート備考
@unocss/preset-uno, @unocss/preset-mini, @unocss/preset-wind3, @unocss/preset-icons, @unocss/web-fontsこれらや、rules/variants/preflightsのみに依存するコミュニティプラグイン(例:unocss-preset-forms)は動作します。
@unocss/preset-typographyこのプリセットはpreflightsにルールセットを追加するため、使用時はsafelistにproseクラスを追加してください。その他のクラス(例:prose-pink)はコンポーネントスコープで利用可能です。
@unocss/preset-rem-to-pxスタイル出力のみを変更するプリセットは動作します。
@unocss/preset-attributify-このプリセットは動作しません。代わりにunplugin-attributify-to-class Viteプラグイン(attributifyToClass({ include: [/\.svelte$/]}))をSvelte Scoped Viteプラグインの前に使ってください。
@unocss/preset-tagify-カスタムextractorを追加するプリセットは動作しません。<text-red>Hi</text-red><span class="text-red">Hi</span>に変換するプリプロセッサを作成し、ここにリンクを追加してください。

他のプリセットについては、従来のclass="..."利用に依存しない場合、まずクラス名をclass="..."属性にプリプロセスする必要があります。typographyの.proseクラスのようにプリセット追加がある場合は、safelistにトリガークラスを追加してください。

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

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

ts
import transformerDirectives from '@unocss/transformer-directives'

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

INFO

Svelteコンポーネント内ではSvelte Scopedの仕組み上、トランスフォーマーはサポートされていません。

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

グローバルスタイルシートの肥大化が気になり、例えば.md:max-w-[50vw]のような一度しか使わないクラスを使うたびにためらいを感じるようになったら、このパッケージを試してみてください。まさに必要なクラスを使うことをためらうのは創造性の妨げです。もちろん、スタイルブロックで--at-apply: md:max-w-[50vw]を使うこともできますが、手間がかかりますし、文脈に応じたスタイルは便利です。また、多様なアイコンをプロジェクトに含めたい場合、グローバルスタイルシートに追加する重みを感じるようになります。各コンポーネントが自身のスタイルやアイコンを持つことで、追加ごとにコストを気にせずプロジェクトを拡張できます。

ライセンス

Released under the MIT License.