Theme:

블로그: 테마 5개 만들기에 대해 "왜?"라고 물으면 답할 수 있으신가요?

\n\n> 블로그: 테마 5개 만들기에 대해 "왜?"라고 물으면 답할 수 있으신가요?\n블로그에 다크 모드 하나쯤은 있어야 하지 않나 싶어서 시작했는데, 만들다 보니 욕심이 생겨서 5개까지 늘어났습니다. Dracula, GitHub Dark, Monokai, Solarized, One Dark — 전부 개발자들이 좋아하는 에디터 테마에서 따왔습니다.

근데 5개 테마를 관리하는 게 생각보다 어렵지 않았습니다. CSS Variables 하나로 끝났거든요.


핵심 아이디어

테마가 바뀌면 뭐가 달라져야 할까요? 결국 색상 입니다. 레이아웃이 바뀌는 건 아니고, 배경색·글자색·강조색만 달라지면 됩니다.

그러면 CSS에서 색상을 직접 쓰는 대신 변수로 빼고, 테마마다 변수 값만 바꿔주면 되지 않을까?

CSS
/* 컴포넌트에서는 변수만 참조 */
.header {
    background: var(--bg);
    color: var(--fg);
    border-bottom: 1px solid var(--border);
}
CSS
/* 테마별로 변수 값을 다르게 정의 */
[data-theme="dracula"] {
    --bg: #282a36;
    --fg: #f8f8f2;
    --border: #44475a;
    --accent: #bd93f9;
}

[data-theme="github-dark"] {
    --bg: #0d1117;
    --fg: #c9d1d9;
    --border: #30363d;
    --accent: #58a6ff;
}

이렇게 하면 테마 전환은 <html> 태그의 data-theme 속성만 바꾸면 됩니다. 자바스크립트로 할 일이 거의 없습니다.


변수 설계

테마에서 관리하는 변수를 정리하면 대략 이런 구조입니다.

CSS
:root {
    /* 기본 색상 */
    --bg: ...;           /* 배경 */
    --fg: ...;           /* 기본 텍스트 */
    --fg-muted: ...;     /* 흐린 텍스트 (날짜, 부가 정보) */

    /* 강조 색상 */
    --accent: ...;       /* 링크, 버튼, 활성 상태 */
    --accent-hover: ...; /* 호버 시 */

    /* 구분선 */
    --border: ...;
    --border-light: ...;

    /* 코드 블록 */
    --code-bg: ...;
    --code-fg: ...;

    /* 터미널 */
    --terminal-bg: ...;
    --terminal-fg: ...;
    --terminal-prompt: ...;
}

변수 이름을 지을 때 --dracula-purple 같은 테마 종속적인 이름 대신, --accent 같은 역할 기반 이름을 쓰는 게 포인트입니다. 컴포넌트에서 var(--accent)를 쓰면 어떤 테마든 자연스럽게 적용되니까요.


5가지 테마

Dracula

CSS
[data-theme="dracula"] {
    --bg: #282a36;
    --fg: #f8f8f2;
    --accent: #bd93f9;
    --border: #44475a;
    --code-bg: #1e1f29;
}

보라색 강조가 특징입니다. 기본 테마로 쓰고 있습니다.

GitHub Dark

CSS
[data-theme="github-dark"] {
    --bg: #0d1117;
    --fg: #c9d1d9;
    --accent: #58a6ff;
    --border: #30363d;
}

GitHub 다크 모드 그대로입니다. 파란색 계열이라 깔끔합니다.

Monokai

CSS
[data-theme="monokai"] {
    --bg: #272822;
    --fg: #f8f8f2;
    --accent: #a6e22e;
    --border: #3e3d32;
}

초록색 강조. 서브라임 텍스트 쓰던 사람한테 익숙한 조합입니다.

Solarized Dark

CSS
[data-theme="solarized"] {
    --bg: #002b36;
    --fg: #839496;
    --accent: #268bd2;
    --border: #073642;
}

눈이 편한 파란 계열. 채도가 낮아서 오래 봐도 피로감이 적습니다.

One Dark

CSS
[data-theme="one-dark"] {
    --bg: #282c34;
    --fg: #abb2bf;
    --accent: #61afef;
    --border: #3e4452;
}

VS Code 기본 테마에 가까운 느낌입니다.


테마 전환 로직

JavaScript에서 해야 할 일은 딱 두 가지입니다.

  1. <html> 태그의 data-theme 속성 변경
  2. 선택한 테마를 localStorage에 저장 (새로고침해도 유지)
JAVASCRIPT
function setTheme(theme) {
    document.documentElement.setAttribute('data-theme', theme);
    localStorage.setItem('theme', theme);
}

// 초기 로드 시
const saved = localStorage.getItem('theme') || 'dracula';
setTheme(saved);

이게 전부입니다. CSS Variables가 알아서 나머지를 처리합니다.


테마 선택 UI

헤더에 테마 선택 드롭다운을 넣었습니다. 클릭하면 5개 테마가 나오고, 선택하면 즉시 적용됩니다.

구현은 간단한데, 한 가지 신경 써야 할 점이 있습니다. 초기 로딩 시 깜빡임(FOUC) 문제입니다.

React가 마운트되기 전에 기본 테마가 잠깐 보이다가 저장된 테마로 바뀌면 화면이 번쩍합니다. 이걸 막으려면 index.html<head>에서 React보다 먼저 테마를 설정해야 합니다.

HTML
<script>
    const t = localStorage.getItem('theme') || 'dracula';
    document.documentElement.setAttribute('data-theme', t);
</script>

이 스크립트가 CSS보다 먼저 실행되면 깜빡임이 없습니다.


왜 CSS-in-JS 안 쓰고 CSS Variables를 선택했나

styled-components나 Emotion으로도 테마를 구현할 수 있는데, CSS Variables를 선택한 이유가 있습니다.

  1. ** 런타임 비용이 없음** — CSS Variables는 브라우저가 네이티브로 처리합니다. JS 런타임이 개입하지 않아서 빠릅니다.
  2. ** 번들 크기** — CSS-in-JS 라이브러리를 추가할 필요가 없습니다.
  3. ** 단순함** — 테마 전환이 setAttribute 한 줄입니다. Provider 감싸고, Theme 객체 만들고 하는 보일러플레이트가 없습니다.

개인 블로그 수준에서는 CSS Variables가 가성비 최고입니다.

댓글 로딩 중...