[ Vue ] 다크모드 화이트모드 시스템 테마 설정하기
이전에 react에서 했던 다크모드와 화이트모드 설정은 직접 class를 하나씩 주는 방식으로 했었는데
이 부분에서 많은 노가다가 필요했었기에 보통 페이지들에서 사용하는 방식인
data-theme을 주는 방식으로 진행
Style
style 폴더에서 variable.css파일을 생성하여 스타일 변수를 저장하는 파일로 관리
이후 main에 해당 css파일을 import해주면 된다.
data-theme이 white는 화이트 모드
data-theme이 dark는 다크모드
이때 변수명은 둘다 동일하게 가져가야한다.
:root {
--app-root-padding: 10px;
}
:root[data-theme='white'] {
--app-theme-side-background: #f3f3f3;
--app-theme-background: #FFFFFF;
--app-theme-text-color: #373737;
}
:root[data-theme='dark'] {
--app-theme-side-background: #212121;
--app-theme-background: #303030;
--app-theme-text-color: #FFFFFF;
}
이후 vue파일에서 각 theme에 선언한 css 변수를 넣어주면 된다.
그럼 이제 dom에서 data-theme이 바뀜에 따라서 해당 변수의 색을 가져오므로
해당 변수만 적절한 위치에 선언해주면 된다.
<template>
<div class="app-layout-wrapper">
<div class="layout-sidebar">
<side/>
</div>
<div class="layout-main">
<app-main/>
</div>
</div>
</template>
<style scoped lang="scss">
.app-layout-wrapper {
width: 100vw;
height: 100vh;
display: flex;
.layout-sidebar {
flex: 20;
background: var(--app-theme-side-background);
color: var(--app-theme-text-color);
}
.layout-main {
flex: 80;
background: var(--app-theme-background);
color: var(--app-theme-text-color)
}
}
</style>
Script
이제 data-theme을 변경해주는 함수를 만들어야한다.
나중에 오타를 방지하기 위해서 whte와 dark값을 변수로 미리 만들어서 사용
/* app theme relation */
export const APP_THEME_KEY = 'app-theme'
export const APP_THEME_WHITE = 'white'
export const APP_THEME_DARK = 'dark'
export type appThemeWhtie = typeof APP_THEME_WHITE
export type appThemeDark = typeof APP_THEME_DARK
appTheme함수를 만들어 초기 세팅용 init과 테마 적용용 get/set 함수를 생성
document.documentElement.setAttribute('data-theme', theme)
window.matchMedia('(prefers-color-scheme: dark)').matches;
window.matchMedia를 활용하면 시스템 테마를 가져 올 수 있다.
초기에 설정된 테마가 없다면 시스템 테마를 가져온다.
이때 시스템 테마는 true, false로 return되면 prefers-color-scheme가 dark인지 아닌지 비교해서 가져온다.
import type {appThemeDark, appThemeWhite} from "@/constant/local-storage.ts";
import {APP_THEME_KEY, APP_THEME_WHITE} from "@/constant/local-storage.ts";
export function appTheme() {
const initTheme = () => {
const theme = getTheme()
if(!theme) {
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
systemPrefersDark ? setTheme(APP_THEME_DARK) : setTheme(APP_THEME_WHITE)
} else {
setTheme(theme)
}
}
const setTheme = (theme: appThemeWhite | appThemeDark) => {
localStorage.setItem(APP_THEME_KEY,theme)
document.documentElement.setAttribute('data-theme', theme);
}
const getTheme = (): appThemeWhite | appThemeDark | null => {
const theme = localStorage.getItem(APP_THEME_KEY);
return theme as appThemeWhite | appThemeDark | null;
}
return {
initTheme,
setTheme,
getTheme
}
}
Vue
그럼 이제 처음 실행시에는 app.vue가 먼저 실행되므로 해당 부분에서 getTheme을 호출해 테마를 적용시켜준다.
import { createApp } from 'vue'
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import './style/index.scss'
import App from './App.vue'
import router from "@/router";
import {appTheme} from "@/service";
const {initTheme} = appTheme()
initTheme()
const app = createApp(App)
app.use(ElementPlus)
app.use(router)
app.mount('#app')
이제 스위치를 통해서 값이 변경되면 setTheme을 호출해서 테마를 바꿔준다.
element-plus를 사용하므로 el-switch를 사용
import {appTheme} from "@/service";
const {setTheme,getTheme} = appTheme()
const themeMode = ref(getTheme())
const appThemeChange = (theme: appThemeWhtie | appThemeDark) => {
setTheme(theme)
}
</script>
<template>
<div class="app-side-wrapper">
<div class="side-mode-change">
<div class="flex-center">다크모드</div>
<el-switch v-model="themeMode"
:active-value="APP_THEME_DARK"
:inactive-value="APP_THEME_WHITE"
@change="appThemeChange"
/>
</div>
</div>
</template>
watch를 활용해서 할까 하다가 그냥 @change로 사용