Vite에서 사용하기
Vite 환경에서 @vapor-ui/core 테마 시스템을 설정하고, 화면 깜빡임(FOUC)을 방지하기 위해 스크립트를 수동으로 설정하는 방법을 안내합니다.
Vite에서 사용하기
Vite는 클라이언트 사이드 렌더링(CSR)을 기본으로 동작합니다. 이 때문에 동적 테마를 적용할 때 화면 깜빡임(FOUC, Flash of Unstyled Content) 현상이 발생할 수 있습니다.
이 문서는 Vite 환경에서 @vapor-ui/core
를 설정하고, FOUC 현상을 방지하는 방법을 안내합니다.
왜 깜빡임(FOUC)이 발생할까요?
Vite는 최소한의 index.html
을 브라우저에 먼저 보냅니다. 브라우저는 이 HTML과 기본 CSS로 첫 화면(주로 라이트 모드)을 그립니다. 그 후 React 앱의 자바스크립트가 로드되고 실행되면서 ThemeProvider
가 사용자의 저장된 테마(예: 다크 모드)를 뒤늦게 DOM에 적용합니다. 이 시차 때문에 "라이트 모드 → 다크 모드로 화면이 바뀌는" 깜빡임이 발생합니다.
설정 방법
FOUC를 방지하려면, React가 실행되기 전에 localStorage
의 테마 설정을 읽어 <html>
태그에 적용하는 스크립트를 index.html
에 직접 추가해야 합니다.
향후 지원 안내
현재는 스크립트를 수동으로 추가해야 하지만, 추후 이 과정을 자동화하는
@vapor-ui/vite-plugin
을 제공할 예정입니다.
1단계: 패키지 설치
먼저 @vapor-ui/core
를 설치합니다.
npm install @vapor-ui/core
2단계: index.html
에 FOUC 방지 스크립트 추가
public/index.html
또는 프로젝트 루트의 index.html
파일을 열고, <head>
태그 안에 아래의 <script>
코드를 복사하여 붙여넣으세요. 이 스크립트는 React보다 먼저 실행되어 깜빡임을 방지합니다.
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vapor UI with Vite</title>
<!-- 💡 @vapor-ui/core FOUC 방지 스크립트 시작 -->
<script>
(function () {
// 이 스크립트는 React 실행 전에 동작하여 FOUC를 방지합니다.
// 아래 설정을 자신의 프로젝트에 맞게 수정하세요.
const defaultConfig = {
appearance: 'light',
radius: 'md',
scaling: 1.0,
};
const storageKey = 'my-vite-app-theme'; // ThemeProvider와 동일한 storageKey를 사용해야 합니다.
// --- 내부 로직 (수정 불필요) ---
const root = document.documentElement;
let currentThemes = defaultConfig;
try {
const storedItem = localStorage.getItem(storageKey);
if (storedItem) {
const storedSettings = JSON.parse(storedItem);
currentThemes = Object.assign({}, defaultConfig, storedSettings);
}
} catch (e) {
// localStorage가 비활성화된 경우 기본값을 사용합니다.
}
// 1. Color Theme 적용
if (currentThemes.appearance === 'dark') {
root.classList.add('vapor-dark-theme');
} else {
root.classList.add('vapor-light-theme');
}
// 2. Radius Theme 적용
const radiusMap = { none: 0, sm: 0.5, md: 1, lg: 1.5, xl: 2, full: 3 };
const radiusFactor = radiusMap[currentThemes.radius] ?? 1;
root.style.setProperty('--vapor-radius-factor', radiusFactor.toString());
// 3. Scaling Theme 적용
const scaleFactor = currentThemes.scaling ?? 1;
root.style.setProperty('--vapor-scale-factor', scaleFactor.toString());
})();
</script>
<!-- 스크립트 끝 -->
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
중요: 위 스크립트의
storageKey
값은 3단계에서ThemeProvider
에 전달할storageKey
와 반드시 일치해야 합니다.
3단계: ThemeProvider
설정
애플리케이션의 진입점(src/main.tsx
)에서 ThemeProvider
로 앱 전체를 감싸고, 스타일 시트를 임포트합니다.
// src/main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { ThemeProvider, createThemeConfig } from '@vapor-ui/core';
// 필수: 스타일 시트를 임포트합니다.
import '@vapor-ui/core/styles.css';
import App from './App.tsx';
// index.html의 스크립트와 동일한 설정을 사용합니다.
const themeConfig = createThemeConfig({
appearance: 'light',
radius: 'md',
scaling: 1.0,
storageKey: 'my-vite-app-theme', // index.html의 storageKey와 일치해야 합니다.
});
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<ThemeProvider config={themeConfig}>
<App />
</ThemeProvider>
</React.StrictMode>,
);
4단계: 컴포넌트에서 테마 사용하기
설정이 완료되면 useTheme
훅을 사용하여 어떤 컴포넌트에서든 현재 테마 값을 읽거나 변경할 수 있습니다.
// src/components/ThemeToggleButton.tsx
import { useTheme } from '@vapor-ui/core';
export function ThemeToggleButton() {
const { appearance, setTheme } = useTheme();
const toggleTheme = () => {
setTheme({
appearance: appearance === 'light' ? 'dark' : 'light',
});
};
return <button onClick={toggleTheme}>Toggle Theme (Current: {appearance})</button>;
}
이제 Vite 프로젝트에서도 화면 깜빡임 없이 동적 테마를 사용할 수 있습니다.