Shadow DOM에서 Chakra UI 사용하기

Shadow DOM에서 Chakra UI 사용하기 (Using Chakra UI in Shadow DOM)

브라우저 확장을 개발하거나 대규모 프로젝트의 일부로 Chakra를 사용할 때, Shadow DOM을 활용하면 스타일과 로직을 격리하는 데 유용해요. Shadow DOM과 함께 Chakra UI를 설치하는 방법을 안내합니다.

출처: 문서

본문

브라우저 확장을 개발하거나 대규모 프로젝트의 일부로 Chakra를 사용할 때, Shadow DOM을 활용하면 스타일과 로직을 캡슐화하는 데 유용해요.

템플릿 (Template)

빠르게 시작하려면 다음 템플릿을 사용하세요.

설치 (Installation)

최소 Node 버전은 Node.20.x입니다.

의존성 설치 (Install dependencies)

npm i @chakra-ui/react @emotion/react @emotion/cache react-shadow

추가로 사용되는 패키지들은 다음과 같아요.

  • react-shadow — Shadow DOM을 쉽게 생성하는 데 사용
  • @emotion/cache — 스타일을 위한 커스텀 삽입 지점을 생성하는 데 사용

스니펫 추가 (Add snippets)

스니펫은 UI를 더 빨리 구축하는 데 사용할 수 있는 사전 빌드된 컴포넌트예요. @chakra-ui/cli를 사용해 스니펫을 프로젝트에 추가할 수 있습니다.

npx @chakra-ui/cli snippet add

tsconfig 업데이트 (Update tsconfig)

TypeScript를 사용한다면 tsconfig 파일의 compilerOptions에 다음 옵션을 포함하도록 업데이트해야 해요.

{
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "skipLibCheck": true,
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

스타일 엔진 구성 (Configure style engine)

프로젝트 루트에 system.ts 파일을 만들고 스타일 엔진을 구성하세요.

import { createSystem, defaultConfig, defineConfig } from "@chakra-ui/react"

const varRoot = ":host"

const config = defineConfig({
  cssVarsRoot: varRoot,
  conditions: {
    light: `${varRoot} &, .light &`,
  },
  preflight: { scope: varRoot },
  globalCss: {
    [varRoot]: defaultConfig.globalCss?.html ?? {},
  },
})

export const system = createSystem(defaultConfig, config)

알아두면 좋아요: system.ts 파일의 주요 목적은 스타일 엔진이 Shadow DOM을 대상으로 하도록 구성하는 것입니다.

provider 설정 (Setup provider)

생성된 components/ui/provider 컴포넌트를 Provider 컴포넌트로 업데이트하세요.

이 provider는 다음을 조합해요.

  • 스타일링 시스템을 위한 @chakra-ui/react의 ChakraProvider
  • Chakra 컴포넌트가 DOM을 올바르게 조회하도록 하는 react-shadow의 EnvironmentProvider
  • 커스텀 삽입 지점을 제공하는 @emotion/react의 CacheProvider
  • 컬러 모드를 위한 next-themes의 ThemeProvider
"use client"

import { ChakraProvider, EnvironmentProvider } from "@chakra-ui/react"
import createCache from "@emotion/cache"
import { CacheProvider } from "@emotion/react"
import { ThemeProvider, type ThemeProviderProps } from "next-themes"
import { useEffect, useState } from "react"
import root from "react-shadow/emotion"
import { system } from "./system"

export function Provider(props: ThemeProviderProps) {
  const [shadow, setShadow] = useState<HTMLElement | null>(null)
  const [cache, setCache] = useState<ReturnType<typeof createCache> | null>(
    null,
  )

  useEffect(() => {
    if (!shadow?.shadowRoot || cache) return
    const emotionCache = createCache({
      key: "root",
      container: shadow.shadowRoot,
    })
    setCache(emotionCache)
  }, [shadow, cache])

  return (
    <root.div ref={setShadow}>
      {shadow && cache && (
        <EnvironmentProvider value={() => shadow.shadowRoot ?? document}>
          <CacheProvider value={cache}>
            <ChakraProvider value={system}>
              <ThemeProvider {...props} />
            </ChakraProvider>
          </CacheProvider>
        </EnvironmentProvider>
      )}
    </root.div>
  )
}

provider 사용하기 (Use the provider)

애플리케이션 루트에서 components/ui/provider 컴포넌트에 생성된 Provider 컴포넌트로 애플리케이션을 감싸세요.

import { Provider } from "@/components/ui/provider"
import { StrictMode } from "react"
import { createRoot } from "react-dom/client"
import App from "./App.tsx"

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <Provider>
      <App />
    </Provider>
  </StrictMode>,
)

즐거운 개발 되세요! (Enjoy!)

Chakra UI의 스니펫과 원시 컴포넌트의 힘으로 더 빠르게 UI를 구축할 수 있어요.

import { Button, HStack } from "@chakra-ui/react"

export default function App() {
  return (
    <HStack>
      <Button>Click me</Button>
      <Button>Click me</Button>
    </HStack>
  )
}

더 알아보기 (Learn more)