Skip to content

Svelte Scoped

Помещает сгенерированный CSS для служебных стилей каждого компонента Svelte непосредственно в блок <style> этого компонента, вместо глобального файла CSS.

Этот компонент:

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

преобразуется в:

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

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

Когда использовать

Сценарий использованияОписаниеКакой пакет использовать
Небольшие приложенияУдобнее иметь 1 глобальный CSS-файл. Используйте обычный плагин Vite для Svelte/SvelteKit.unocss/vite
Крупные приложенияSvelte Scoped поможет вам избежать постоянно растущего глобального CSS-файла.@unocss/svelte-scoped/vite
Библиотека компонентовСгенерированные стили помещаются непосредственно в собранные компоненты, без необходимости использования UnoCSS в конвейере сборки использующего их приложения.@unocss/svelte-scoped/preprocess

Как это работает

Обычная настройка UnoCSS/Tailwind CSS помещает служебные стили в глобальный CSS-файл с правильным порядком. В отличие от этого, Svelte Scoped распределяет ваши стили по множеству произвольно упорядоченных CSS-файлов компонентов Svelte. Однако он должен сохранять служебные стили глобальными, чтобы они могли учитывать контекст, что необходимо для таких задач, как поддержка RTL и других сценариев использования, перечисленных ниже. Это создает проблему, которая решается с помощью использования обёртки :global() в Svelte, чтобы отказаться от стандартного метода хеширования CSS в Svelte и вместо этого использовать хеш, основанный на имени файла + имени класса (классов), для компиляции уникальных имён классов, которые могут быть сделаны глобальными без конфликтов стилей.

Использование

Поскольку Svelte Scoped переписывает имена ваших служебных классов, вы ограничены в том, где можете их писать:

Поддерживаемый синтаксисПример
Атрибут class<div class="mb-1" />
Директива class<div class:mb-1={condition} />
Сокращение директивы class<div class:logo />
Проп class<Button class="mb-1" />
Аналог clsx<div class={["mb-1", { logo, 'font-bold': isBold() }, isUnderlined() && 'underline' ]} />

Svelte Scoped разработан как прямая замена для проекта, использующего служебные стили. В связи с этим, выражения внутри атрибутов class также поддерживаются (например, <div class="mb-1 {foo ? 'mr-1' : 'mr-2'}" />), но мы рекомендуем в дальнейшем использовать синтаксис clsx. Обратите внимание, что если вы использовали имена классов другими способами, например, помещая их в блок <script> или используя режим атрибутов, вам потребуется предпринять дополнительные шаги перед использованием Svelte Scoped. Вы можете использовать опцию 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>Статус: онлайн</div>
  <Button>FAQ</Button>
  <Button>Войти</Button>
</div>

преобразуется так:

svelte
<div class="uno-7haszz">
  <div>Статус: онлайн</div>
  <Button>FAQ</Button>
  <Button>Войти</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">Войти</Button>

преобразуется так:

svelte
<Button class="uno-4hshza">Войти</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

Вы можете использовать директивы apply внутри блоков <style> с помощью --at-apply, @apply или пользовательского значения, заданного с помощью опции applyVariables.

Svelte Scoped даже корректно обрабатывает классы, зависящие от контекста, такие как dark:text-white, с которыми обычный пакет @unocss/transformer-directives не может справиться должным образом, так как он не был создан специально для блоков стилей Svelte. Например, с Svelte Scoped этот компонент:

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, помещая минимально необходимые стили в глобальную таблицу стилей. Ознакомьтесь с примером SvelteKit в Stackblitz:

Открыть в 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

Добавление плагина

Добавьте @unocss/svelte-scoped/vite в конфигурацию 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', // см. определение типов для всех включенных вариантов сброса или узнайте, как передать собственный
      // ...другие опции Svelte Scoped
    }),
    sveltekit(),
  ],
})

Добавление файла конфигурации

Настройте файл uno.config.ts, как описано ниже.

Глобальные стили

Хотя почти все стили размещаются в отдельных компонентах, есть несколько, которые должны быть помещены в глобальную таблицу стилей: префлайты, safelist (белый список) и опциональный сброс (если вы используете опцию injectReset).

Добавьте плейсхолдер %unocss-svelte-scoped.global% в тег <head>. В Svelte это index.html. В SvelteKit это будет в app.html перед %sveltekit.head%:

index.html
html
<head>
  <!-- ... -->
  <title>SvelteKit с использованием UnoCSS Svelte Scoped</title>
  %unocss-svelte-scoped.global%
  %sveltekit.head%
</head>

Препроцессор Svelte

Используйте служебные стили для создания библиотеки компонентов, которая не зависит от включения сопутствующего CSS-файла, используя препроцессор для размещения сгенерированных стилей непосредственно в собранных компонентах. Ознакомьтесь с примером библиотеки SvelteKit в Stackblitz:

Открыть в 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

Добавление препроцессора

Добавьте @unocss/svelte-scoped/preprocess в конфигурацию Svelte:

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({
      // ... параметры препроцессора
    }),
  ],
  // другая конфигурация Svelte
}

Не объединяйте имена классов в разработке

При использовании Svelte Scoped в обычном приложении плагин Vite автоматически определяет режим dev (разработка) или build (сборка). В режиме разработки классы остаются раздельными и хешируются на месте для удобства включения/выключения в инструментах разработчика вашего браузера. class="mb-1 mr-1" превратится во что-то вроде class="_mb-1_9hwi32 _mr-1_84jfy4. В продакшене они будут скомпилированы в одно имя класса с использованием желаемого префикса (по умолчанию uno-) и хеша, основанного на имени файла + именах классов, например class="uno-84dke3.

Если вы хотите получить такое же поведение при использовании препроцессора, вы должны вручную установить опцию combine в зависимости от окружения. Один из способов сделать это — установить cross-env и обновить ваш dev-скрипт следующим образом:

json
{
  "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, как описано ниже.

Префлайты

При использовании препроцессора у вас есть возможность включить префлайты в конкретные компоненты, где они необходимы, добавив uno-preflights в качестве атрибута стиля.

html
<style uno-preflights></style>

Любые специальные префлайты, начинающиеся с точки, такие как .prose :where(a):not(:where(.not-prose, .not-prose *)), будут обёрнуты в :global(), чтобы избежать автоматического удаления компилятором Svelte.

Добавление префлайтов в отдельные компоненты необязательно, если ваши классы не зависят от них, или если ваши собранные компоненты используются только в приложениях, которые уже включают префлайты.

Белый список

При использовании препроцессора у вас есть возможность включить классы из белого списка в компонент, добавив uno-safelist в качестве атрибута стиля.

html
<style uno-safelist></style>

Стили из белого списка будут обёрнуты в :global(), чтобы избежать их автоматического удаления компилятором Svelte.

Конфигурация

Поместите настройки UnoCSS в файл uno.config.ts:

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

export default defineConfig({
  // параметры ...UnoCSS
})

Экстракторы не поддерживаются из-за различий между обычным глобальным использованием UnoCSS и использованием в Svelte Scoped. Пресеты и трансформеры поддерживаются, как описано в следующих разделах. Все остальные подробности см. в Файле конфигурации и Справочнике по конфигурации.

Поддержка пресетов

Из-за того, что несколько необходимых стилей находятся в глобальной таблице стилей, а всё остальное содержится в каждом компоненте по мере необходимости, пресеты нужно рассматривать в индивидуальном порядке:

ПресетПоддержкаПримечания
@unocss/preset-uno, @unocss/preset-mini, @unocss/preset-wind3, @unocss/preset-icons, @unocss/web-fontsЭти и все плагины сообщества (например, unocss-preset-forms), которые полагаются только на правила/варианты/префлайты, будут работать.
@unocss/preset-typographyИз-за того, как этот пресет добавляет наборы правил в ваши префлайты, вы должны добавить класс prose в белый список (safelist) при использовании этого пресета, иначе префлайты никогда не сработают. Все остальные классы из этого пресета, например prose-pink, могут быть изолированы в компоненте.
После v66.5.0 стили prose были переработаны в rule (правило), что означает, что вам больше не нужно добавлять этот класс в белый список.
@unocss/preset-rem-to-pxЭтот и все подобные пресеты, которые только модифицируют вывод стилей, будут работать.
@unocss/preset-attributify-Пресет работать не будет. Вместо этого используйте плагин Vite unplugin-attributify-to-class (attributifyToClass({ include: [/\.svelte$/]})) перед плагином Svelte Scoped для Vite.
@unocss/preset-tagify-Пресеты, добавляющие пользовательские экстракторы, работать не будут. Создайте препроцессор для конвертации <text-red>Hi</text-red> в <span class="text-red">Hi</span>, затем создайте PR, чтобы добавить ссылку сюда.

Для других пресетов: если они не полагаются на традиционное использование class="...", вам нужно сначала преобразовать эти имена классов в атрибут class="...". Если они добавляют пресеты, подобные классу .prose в типографике, вам нужно поместить классы, вызывающие добавление пресета, в ваш белый список.

Поддержка трансформеров

Трансформеры поддерживаются для ваших CSS-файлов (css|postcss|sass|scss|less|stylus|styl). Чтобы использовать их, добавьте трансформер в опцию cssFileTransformers в вашем vite.config.ts:

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

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

ИНФОРМАЦИЯ

Трансформеры не поддерживаются в компонентах Svelte из-за особенностей работы Svelte Scoped.

Изолированные служебные классы раскрывают творческий потенциал

Несколько советов о том, когда стоит использовать изолированные стили: если вы дошли до той стадии разработки крупного проекта, когда каждый раз, добавляя класс вроде .md:max-w-[50vw] (который, как вы знаете, используется всего один раз), вы внутренне сжимаетесь, чувствуя, как раздувается ваша глобальная таблица стилей — попробуйте этот пакет. Страх использовать именно тот класс, который нужен, ограничивает творчество. Конечно, можно использовать --at-apply: md:max-w-[50vw] в блоке стилей, но это утомительно, да и видеть стили в контексте (в HTML) бывает полезно. Кроме того, если вы захотите использовать в проекте большое разнообразие иконок, вы начнете ощущать тяжесть их добавления в глобальную таблицу стилей. Когда каждый компонент сам несет нагрузку своих стилей и иконок, вы можете продолжать расширять проект, не задумываясь о целесообразности каждого нового добавления с точки зрения производительности.

Лицензия

  • Лицензия MIT © 2022-настоящее время Jacob Bowdoin

Распространяется по лицензии MIT.