Skip to content

Svelte Scoped

Place le CSS généré pour chaque composant Svelte directement dans le bloc <style> du composant au lieu d'un fichier CSS global.

Ce composant :

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

est transformé en :

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

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

Quand l'utiliser

Cas d'utilisationDescriptionPackage à utiliser
Petites applicationsAvoir un seul fichier CSS global est plus pratique. Utilisez le plugin Vite classique pour Svelte/SvelteKit.unocss/vite
Grandes applicationsSvelte Scoped peut vous aider à éviter un fichier CSS global qui grossit sans cesse.@unocss/svelte-scoped/vite
Librairie de composantsLes styles générés sont placés directement dans les composants construits sans avoir besoin d'utiliser UnoCSS dans le pipeline de build de l'app consommatrice.@unocss/svelte-scoped/preprocess

Comment ça marche

Une configuration UnoCSS/Tailwind CSS classique place les utilitaires dans un fichier CSS global avec un ordre précis. Svelte Scoped, au contraire, répartit vos styles dans de nombreux fichiers CSS de composants Svelte, dans un ordre arbitraire. Cependant, il doit garder les utilitaires globaux pour permettre la prise en compte du contexte (RTL, etc.). Cela est résolu en utilisant le wrapper :global() de Svelte pour sortir du hachage CSS par défaut de Svelte et utiliser un hash basé sur le nom de fichier + nom(s) de classe pour compiler des classes uniques globales sans conflit de style.

Utilisation

Comme Svelte Scoped réécrit vos noms de classes utilitaires, vous êtes limité sur les endroits où vous pouvez les écrire :

Syntaxe supportéeExemple
Attribut class<div class="mb-1" />
Directive class<div class:mb-1={condition} />
Raccourci directive class<div class:logo />
Prop class<Button class="mb-1" />

Svelte Scoped est conçu pour être un remplacement direct pour un projet utilisant les utilitaires. Les expressions dans les attributs class sont aussi supportées (ex : <div class="mb-1 {foo ? 'mr-1' : 'mr-2'}" />), mais il est recommandé d'utiliser la syntaxe directive. Si vous utilisez les noms de classes dans d'autres contextes (dans <script>, mode attributify, etc.), il faudra des ajustements (voir option safelist et section presets).

Prise en compte du contexte

Même si les styles sont répartis dans vos composants Svelte, ils restent des classes globales et fonctionnent en relation avec des éléments extérieurs au composant. Exemples :

Dépendant du parent

Classes dépendant d'attributs du parent :

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

devient :

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

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

Influence sur les enfants

Vous pouvez ajouter de l'espace entre 3 enfants, même s'ils sont dans des composants séparés :

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

devient :

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>

Passer des classes à des composants enfants

Vous pouvez ajouter une prop class à un composant pour transmettre des classes personnalisées où il est utilisé.

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

devient :

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>

Pour appliquer la classe dans le composant receveur, placez-la sur un élément avec {$$props.class} comme dans div class="{$$props.class} foo bar" />.

Directives apply

Vous pouvez utiliser les directives apply dans vos blocs <style> avec --at-apply, @apply ou une valeur personnalisée via l'option applyVariables.

Svelte Scoped gère même correctement les classes dépendantes du contexte comme dark:text-white que le package @unocss/transformer-directives ne gère pas dans les blocs style Svelte. Exemple :

svelte
<div />

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

devient :

svelte
<div />

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

Pour que rtl:ml-2 fonctionne, le sélecteur [dir="rtl"] est enveloppé dans :global() pour éviter que le compilateur Svelte ne le supprime. Mais div ne peut pas être inclus dans :global() sinon le style affecterait tous les div de l'app.

Autres directives dans les blocs style

L'utilisation de theme() est aussi supportée, mais @screen ne l'est pas.

Plugin Vite

Dans les apps Svelte ou SvelteKit, injectez les styles générés directement dans vos composants Svelte, tout en plaçant le minimum nécessaire dans une feuille de style globale. Voir l'exemple SvelteKit sur Stackblitz :

Ouvrir dans StackBlitz

Installation

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

Ajouter le plugin

Ajoutez @unocss/svelte-scoped/vite à votre configuration 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', // voir la définition de type pour toutes les options de reset incluses ou comment passer le vôtre
      // ...autres options Svelte Scoped
    }),
    sveltekit(),
  ],
})

Ajouter le fichier de configuration

Configurez votre fichier uno.config.ts comme décrit ci-dessous.

Styles globaux

Bien que presque tous les styles soient placés dans les composants individuels, certains doivent encore être placés dans une feuille de style globale : preflights, safelist, et un reset optionnel (si vous utilisez l'option injectReset).

Ajoutez le placeholder %unocss-svelte-scoped.global% dans votre balise <head>. En Svelte, c'est dans index.html. En SvelteKit, ce sera dans app.html avant %sveltekit.head% :

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

Si vous utilisez SvelteKit, vous devez aussi ajouter ce qui suit au hook transformPageChunk dans votre fichier 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
}

Cette transformation doit être dans un fichier dont le chemin contient hooks et server (ex : src/hooks.server.js, src/hooks.server.ts) car svelte-scoped recherchera dans votre fichier de hooks serveur pour remplacer unocss_svelte_scoped_global_styles par vos styles globaux. N'importez pas cette transformation depuis un autre fichier, comme lors de l'utilisation de sequence de @sveltejs/kit/hooks.

Dans un projet Svelte classique, le hook transformIndexHtml de Vite le fera automatiquement.

Svelte Preprocessor

Utilisez les utilitaires pour construire une librairie de composants indépendante d'un fichier CSS compagnon en utilisant un préprocesseur pour placer les styles générés directement dans les composants construits. Voir l'exemple SvelteKit Library sur Stackblitz :

Ouvrir dans StackBlitz

Installation

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

Ajouter le préprocesseur

Ajoutez @unocss/svelte-scoped/preprocess à votre config Svelte :

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({
      // ... options du préprocesseur
    }),
  ],
  // autres options Svelte
}

Ne pas combiner les noms de classes en développement

Quand vous utilisez Svelte Scoped dans une app normale, le plugin Vite détecte automatiquement dev vs build. En développement, les classes sont gardées distinctes et hashées pour faciliter le debug dans les outils du navigateur. class="mb-1 mr-1" devient par exemple class="_mb-1_9hwi32 _mr-1_84jfy4". En production, elles sont compilées en un seul nom de classe avec le préfixe désiré, uno- par défaut, et un hash basé sur le nom de fichier + classes, ex : class="uno-84dke3".

Pour ce comportement avec le préprocesseur, vous devez définir manuellement l'option combine selon l'environnement. Une façon est d'installer cross-env et de mettre à jour votre script dev :

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

Puis ajustez votre svelte.config.js :

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

Ajouter le fichier de configuration

Configurez votre fichier uno.config.ts comme décrit ci-dessous.

Preflights

Avec le préprocesseur, vous pouvez inclure les preflights dans les composants où c'est nécessaire en ajoutant l'attribut uno-preflights au style.

html
<style uno-preflights></style>

Tout preflight spécial commençant par un point, comme .prose :where(a):not(:where(.not-prose, .not-prose *)), sera enveloppé dans :global() pour éviter d'être supprimé par le compilateur Svelte.

Ajouter les preflights dans les composants est inutile si vos classes n'en dépendent pas ou si vos composants sont consommés dans des apps qui incluent déjà les preflights.

Safelist

Avec le préprocesseur, vous pouvez inclure les classes safelist dans un composant en ajoutant l'attribut uno-safelist au style.

html
<style uno-safelist></style>

Vos styles safelist seront enveloppés dans :global() pour éviter d'être supprimés par le compilateur Svelte.

Configuration

Placez vos réglages UnoCSS dans un fichier uno.config.ts :

ts
import { defineConfig } from 'unocss'

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

Les extracteurs ne sont pas supportés à cause des différences entre l'usage global UnoCSS classique et Svelte Scoped. Les presets et transformers sont supportés comme décrit ci-dessous. Voir Fichier de config et Référence config pour plus de détails.

Support des presets

En raison de la présence de quelques styles globaux nécessaires et du reste contenu dans chaque composant, les presets doivent être gérés au cas par cas :

PresetSupportéRemarques
@unocss/preset-uno, @unocss/preset-mini, @unocss/preset-wind3, @unocss/preset-icons, @unocss/web-fontsCeux-ci et tous les plugins communautaires, ex. unocss-preset-forms, qui ne dépendent que des rules/variants/preflights fonctionneront.
@unocss/preset-typographyComme ce preset ajoute des rulesets aux preflights, il faut ajouter la classe prose à votre safelist, sinon les preflights ne seront jamais déclenchés. Les autres classes de ce preset, ex. prose-pink, peuvent être scoped par composant.
@unocss/preset-rem-to-pxCe preset et tous ceux qui ne font que modifier le style fonctionneront.
@unocss/preset-attributify-Ce preset ne fonctionne pas. Utilisez plutôt le plugin Vite unplugin-attributify-to-class (attributifyToClass({ include: [/\.svelte$/]})) avant le plugin Svelte Scoped.
@unocss/preset-tagify-Les presets qui ajoutent des extracteurs personnalisés ne fonctionneront pas. Créez un préprocesseur pour convertir <text-red>Hi</text-red> en <span class="text-red">Hi</span>, puis proposez un PR pour ajouter le lien ici.

Pour les autres presets, s'ils ne reposent pas sur l'usage traditionnel de class="...", il faudra d'abord prétraiter ces noms de classes dans l'attribut class="...". S'ils ajoutent des presets comme la classe .prose de typography, il faudra placer les classes qui déclenchent ces ajouts dans votre safelist.

Support des transformers

Les transformers sont supportés pour vos fichiers CSS (css|postcss|sass|scss|less|stylus|styl). Pour les utiliser, ajoutez le transformer dans l'option cssFileTransformers de votre vite.config.ts :

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

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

INFO

Les transformers ne sont pas supportés dans les composants Svelte à cause du fonctionnement de Svelte Scoped.

Les utilitaires scoped libèrent la créativité

Quelques conseils sur l'intérêt d'utiliser les styles scoped : si dans un gros projet, chaque fois que vous utilisez une classe comme .md:max-w-[50vw] (utilisée une seule fois), vous hésitez à cause de la taille du CSS global, essayez ce package. Hésiter à utiliser exactement la classe dont vous avez besoin bride la créativité. Oui, vous pourriez utiliser --at-apply: md:max-w-[50vw] dans le bloc style, mais cela devient fastidieux et le style en contexte est utile. De plus, si vous souhaitez inclure beaucoup d'icônes, vous sentirez le poids de les ajouter au CSS global. Quand chaque composant porte ses propres styles et icônes, vous pouvez continuer à étendre votre projet sans analyser le coût de chaque ajout.

Licence

Released under the MIT License.