Skip to content

Svelte Scoped

생성된 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 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 prop<Button class="mb-1" />

Svelte Scoped는 유틸리티 스타일을 사용하는 프로젝트의 드롭인 대체품으로 설계되었습니다. 따라서 클래스 속성 내의 표현식도 지원됩니다 (예: <div class="mb-1 {foo ? 'mr-1' : 'mr-2'}" />) 하지만 앞으로는 클래스 지시문 구문을 사용하는 것을 권장합니다. 또한 <script> 블록에 배치하거나 attributify 모드를 사용하는 것과 같은 다른 방식으로 클래스 이름을 사용한 경우 Svelte Scoped를 사용하기 전에 추가 단계가 필요합니다. safelist 옵션을 활용할 수 있고 아래 presets 섹션에서 더 많은 팁을 확인할 수 있습니다.

컨텍스트 인식

스타일이 앱의 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 prop을 컴포넌트에 추가할 수 있습니다.

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}를 사용하여 요소에 배치하는 것입니다.

Apply 지시문

--at-apply 또는 @apply 또는 applyVariables 옵션을 사용하여 설정한 사용자 정의 값을 사용하여 <style> 블록 내에서 apply 지시문을 사용할 수 있습니다.

Svelte Scoped는 Svelte 스타일 블록을 위해 특별히 구축되지 않았기 때문에 일반 @unocss/transformer-directives 패키지가 제대로 처리할 수 없는 dark:text-white와 같은 컨텍스트 의존 클래스도 제대로 처리합니다. 예를 들어, 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 Plugin

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

설정 파일 추가

아래 설정에 설명된 대로 uno.config.ts 파일을 설정하세요.

전역 스타일

거의 모든 스타일이 개별 컴포넌트에 배치되지만 여전히 전역 스타일시트에 배치해야 하는 몇 가지가 있습니다: preflight, safelist, 그리고 선택적 리셋 (injectReset 옵션을 사용하는 경우).

<head> 태그에 %unocss-svelte-scoped.global% 플레이스홀더를 추가하세요. Svelte에서는 index.html입니다. SvelteKit에서는 %sveltekit.head% 앞의 app.html에 있습니다:

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
}

일반 Svelte 프로젝트에서 Vite의 transformIndexHtml 훅이 이를 자동으로 수행합니다.

Svelte Preprocessor

전처리기를 사용하여 생성된 스타일을 빌드된 컴포넌트에 직접 배치하여 동반 CSS 파일을 포함할 필요가 없는 컴포넌트 라이브러리를 구축하기 위해 유틸리티 스타일을 사용하세요. Stackblitz의 SvelteKit Library 예제를 확인하세요:

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

개발에서 클래스 이름 결합하지 않기

일반 앱에서 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를 조정하세요:

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

설정 파일 추가

아래 설정에 설명된 대로 uno.config.ts 파일을 설정하세요.

Preflight

전처리기를 사용할 때 필요한 특정 컴포넌트에 preflight을 포함할 수 있는 옵션이 있습니다. uno-preflights를 스타일 속성으로 추가하세요.

html
<style uno-preflights></style>

.prose :where(a):not(:where(.not-prose, .not-prose *))와 같이 마침표로 시작하는 특별한 preflight은 Svelte 컴파일러에 의해 자동으로 제거되지 않도록 :global()로 래핑됩니다.

클래스가 preflight에 의존하지 않거나 빌드된 컴포넌트가 이미 preflight을 포함하는 앱에서만 소비되는 경우 개별 컴포넌트에 preflight을 추가하는 것은 불필요합니다.

Safelist

전처리기를 사용할 때 uno-safelist를 스타일 속성으로 추가하여 컴포넌트에 safelist 클래스를 포함할 수 있는 옵션이 있습니다.

html
<style uno-safelist></style>

Safelist 스타일은 Svelte 컴파일러에 의해 자동으로 제거되지 않도록 :global()로 래핑됩니다.

설정

UnoCSS 설정을 uno.config.ts 파일에 배치하세요:

ts
import { defineConfig } from 'unocss'

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

일반 UnoCSS 전역 사용과 Svelte Scoped 사용의 차이로 인해 추출기는 지원되지 않습니다. Preset과 Transformer는 다음 섹션에서 설명한 대로 지원됩니다. 다른 모든 세부사항은 Config FileConfig reference를 참조하세요.

Preset 지원

전역 스타일시트에 몇 가지 필요한 스타일이 있고 나머지는 필요한 각 컴포넌트에 포함되는 특성으로 인해 preset은 사례별로 처리해야 합니다:

Preset지원됨참고사항
@unocss/preset-uno, @unocss/preset-mini, @unocss/preset-wind3, @unocss/preset-icons, @unocss/web-fonts이들과 unocss-preset-forms와 같은 모든 커뮤니티 플러그인은 규칙/variant/preflight에만 의존하는 경우 작동합니다.
@unocss/preset-typography이 preset이 preflight에 규칙 세트를 추가하는 방식으로 인해 이 preset을 사용할 때 prose 클래스를 safelist에 추가해야 합니다. 그렇지 않으면 preflight이 트리거되지 않습니다. 이 preset의 다른 모든 클래스(예: prose-pink)는 컴포넌트 스코프를 가질 수 있습니다.
@unocss/preset-rem-to-px이것과 스타일 출력만 수정하는 모든 preset이 작동합니다.
@unocss/preset-attributify-Preset이 작동하지 않습니다. 대신 Svelte Scoped Vite 플러그인보다 먼저 unplugin-attributify-to-class Vite 플러그인 (attributifyToClass({ include: [/\.svelte$/]}))을 사용하세요
@unocss/preset-tagify-사용자 정의 추출기를 추가하는 preset은 작동하지 않습니다. <text-red>Hi</text-red><span class="text-red">Hi</span>로 변환하는 전처리기를 만들고 링크를 여기에 추가하기 위해 PR을 만드세요.

다른 preset의 경우 전통적인 class="..." 사용에 의존하지 않는다면 먼저 해당 클래스 이름을 class="..." 속성으로 전처리해야 합니다. Typography의 .prose 클래스와 같은 preset을 추가한다면 preset 추가를 트리거하는 클래스를 safelist에 배치해야 합니다.

Transformer 지원

CSS 파일(css|postcss|sass|scss|less|stylus|styl)에 대한 Transformer가 지원됩니다. 사용하려면 vite.config.tscssFileTransformers 옵션에 transformer를 추가하세요:

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

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

INFO

Svelte Scoped의 작동 방식으로 인해 Svelte 컴포넌트에서는 Transformer가 지원되지 않습니다.

스코프 유틸리티 클래스가 창의성을 해방합니다

스코프 스타일을 사용하고 싶을 때에 대한 조언: 큰 프로젝트의 생명에서 .md:max-w-[50vw]와 같은 클래스를 사용할 때마다 전역 스타일시트의 크기가 점점 커지는 것을 느끼며 움찔할 정도가 되었다면 이 패키지를 시도해보세요. 정확히 필요한 클래스를 사용하는 것을 망설이는 것은 창의성을 억제합니다. 물론 스타일 블록에서 --at-apply: md:max-w-[50vw]를 사용할 수 있지만 그것은 지루해지고 컨텍스트의 스타일이 유용합니다. 또한 프로젝트에 다양한 아이콘을 포함하고 싶다면 전역 스타일시트에 추가하는 것의 무게를 느끼기 시작할 것입니다. 각 컴포넌트가 자체 스타일과 아이콘의 무게를 지게 되면 각 새로운 추가의 비용 혜택을 분석할 필요 없이 프로젝트를 계속 확장할 수 있습니다.

라이선스

Released under the MIT License.