InfraGuard

라이트 / 다크 테마

콘솔의 밝은·어두운 테마 전환 — 루트의 data-theme 속성, localStorage(infr-theme) 영속, 헤더 토글, 모든 색은 --infr-* CSS 변수 토큰.

개요 — 무엇을 하나

InfraGuard 콘솔은 다크(기본) 와 라이트 두 가지 테마를 제공합니다. 테마는 문서 루트(<html>) 요소의 data-theme 속성 하나로 전환되며, 이 속성에 따라 SCSS 가 정의한 --infr-* CSS 변수 토큰의 값 세트가 통째로 바뀝니다. 모든 컴포넌트는 하드코딩된 hex 색 대신 var(--infr-*) 토큰만 사용하므로, 속성 한 글자(dark↔light)만 바꾸면 화면 전체가 즉시 재도색됩니다.

기본값은 다크다 — NOC(상황실) 콘솔로서 어두운 배경에서 상태 색(위험/경고/정상)이 또렷하게 읽히도록 딥 blue-black 바탕 + teal 시그널을 채택했습니다.

<html data-theme="dark">   ← 기본, NOC 다크
<html data-theme="light">  ← 라이트(밝은 사무실 환경)

테마는 순수 프론트엔드 프레퍼런스입니다. 서버에 저장되지 않고, 사용자 계정·DB 와 무관하게 브라우저별 localStorage 에만 남습니다. 로그아웃해도 유지되고, 다른 사람이 같은 계정으로 다른 기기에서 접속하면 그 기기의 설정을 따릅니다.


언제 쓰나

한 번 고르면 그 브라우저에서 계속 유지되므로, 보통 최초 1회만 설정하면 됩니다.


화면 구성

헤더 토글 버튼

콘솔 셸(layouts/app.blade.php)과 로그인 화면(layouts/simple.blade.php) 상단바 우측에 테마 토글 버튼이 있습니다.

항목 값
마크업 훅 <button data-theme-toggle>
아이콘 mdi-theme-light-dark(반달)
aria-label msg.action.theme(다국어)
동작 클릭 시 dark ↔ light 순환 토글
위치 상단바 액션 영역(언어 스위처 옆, 사용자 메뉴 왼쪽)

로그인 전(게스트) 화면에도 동일 토글이 있어, 인증 없이도 테마를 바꿀 수 있습니다.

색 토큰 그룹

resources/scss/_variables.scss 가 :root[data-theme="dark"] 와 :root[data-theme="light"] 두 블록에 동일한 토큰 이름을 서로 다른 값으로 정의합니다. 대표 토큰:

토큰 용도 dark light
--infr-bg 페이지 배경 #0b1220 #f6f8fb
--infr-surface 패널·카드 표면 #111b2c #ffffff
--infr-fg 본문 텍스트 #e9eff8 #111a28
--infr-fg-muted 보조 텍스트 #8493ac #63728b
--infr-border 테두리 #1e2c43 #dbe2ec
--infr-accent 강조(teal) #2dd4bf #0d9488
--infr-crit 위험 심각도 #fb7185 #e11d48
--infr-warn 경고 심각도 #fbbf24 #b45309
--infr-good 정상 심각도 #34d399 #0f9d6a
--infr-info 정보 #38bdf8 #0369a1

이 밖에도 --infr-surface-2/hi, --infr-border-2, --infr-accent-soft/ink, --infr-on-accent(버튼 텍스트), TCP 상태색(--infr-state-*), 차트색(--infr-chart-2), 그림자·글로우(--infr-shadow/--infr-glow), 상단바 글래스(--infr-topbar-*)가 테마별로 정의됩니다. 각 블록은 color-scheme: dark|light 도 함께 선언해 네이티브 폼 컨트롤·스크롤바까지 테마에 맞춥니다.

SSH 터미널 팔레트는 예외. --infr-term-* 토큰은 data-theme 와 무관하게 항상 딥 다크(몰입형)로 고정됩니다. 라이트 테마에서도 SSH 콘솔은 어두운 터미널을 유지합니다.


사용법

전환하기

  1. 상단바 우측의 반달 아이콘(테마 토글) 을 클릭합니다.
  2. 즉시 dark ↔ light 로 전환되며, 선택은 자동 저장됩니다.
  3. 다음 접속·새로고침에도 마지막 선택이 그대로 복원됩니다.

내부 동작 (개발자 참고)

프론트엔드는 resources/js/app.js 가 담당합니다. 핵심 로직:

const THEME_KEY = 'infr-theme';                 // localStorage 키
const get_theme = () => localStorage.getItem(THEME_KEY) || 'dark';   // 기본 dark
const set_theme = (t) => {
    document.documentElement.setAttribute('data-theme', t);   // 루트 속성
    localStorage.setItem(THEME_KEY, t);                        // 영속
    window.dispatchEvent(new CustomEvent('infr:theme', { detail: t })); // 차트 재도색
};
// 토글 버튼 배선
document.querySelectorAll('[data-theme-toggle]')
    .forEach(btn => btn.addEventListener('click',
        () => set_theme(get_theme() === 'dark' ? 'light' : 'dark')));

동작 요약:

단계 무엇
저장 키 localStorage['infr-theme'] = "dark" | "light"
적용 대상 document.documentElement(=<html>)의 data-theme 속성
기본값 dark(localStorage 비어있거나 접근 실패 시)
이벤트 전환마다 window 에 infr:theme CustomEvent(detail=현재 테마) 발행
탭 동기화 storage 이벤트 수신 → 같은 브라우저의 다른 탭도 자동 동기화

플래시(FOUC) 방지 — pre-paint 스크립트

<head> 최상단(app.blade.php·simple.blade.php 공통, CSS @vite 로드 이전)에 아주 작은 인라인 스크립트가 있어, 스타일시트가 적용되기 전에 data-theme 를 먼저 세팅합니다. 덕분에 새로고침 시 "다크→라이트 깜빡임"이 없습니다.

<script>
  (function () {
    try { document.documentElement.setAttribute('data-theme',
            localStorage.getItem('infr-theme') || 'dark'); }
    catch (e) { document.documentElement.setAttribute('data-theme', 'dark'); }
  })();
</script>

이 스크립트는 localStorage 접근이 막힌 환경(시크릿 모드·서드파티 쿠키 차단 등)에서도 catch 로 안전하게 dark 로 폴백합니다.

차트·에디터의 테마 연동

<cm-editor v-model="script.body" lang="bash" :dark="is_dark_mode"></cm-editor>

데이터 소스

테마는 DB·서버 저장이 전혀 없습니다. 순수하게 다음만 사용합니다:

위치 내용
localStorage['infr-theme'] 브라우저별 사용자 선택(dark/light)
<html data-theme> 런타임 적용 상태
resources/scss/_variables.scss --infr-* 토큰의 테마별 실제 색 정의(빌드 산출)
resources/js/app.js 토글·영속·이벤트 배선

CLAUDE.md 부록 C(Light/Dark 테마 토큰) 가 팔레트의 설계 기준(SOT)입니다.


권한

테마는 인증·권한과 무관한 UI 프레퍼런스입니다.


문제해결 · FAQ

Q. 새로고침하면 테마가 초기화됩니다. 브라우저가 localStorage 를 차단(시크릿 모드·강력한 프라이버시 설정·서드파티 쿠키/스토리지 차단)하면 저장이 안 돼 매번 기본 dark 로 돌아갑니다. 사이트에 스토리지 허용을 부여하세요.

Q. 한 탭에서 바꿨는데 다른 탭은 그대로입니다. 전환은 storage 이벤트로 같은 브라우저의 다른 탭에 전파됩니다. 즉시 반영되지 않으면 해당 탭이 이벤트 수신 배선(app.js) 로드 전일 수 있으니 새로고침합니다. 다른 기기/브라우저는 스토리지가 분리되어 동기화되지 않습니다(설계상 정상).

Q. 라이트로 바꿨는데 SSH 터미널만 계속 어둡습니다. 정상입니다. 터미널은 --infr-term-* 전용 팔레트로 앱 테마와 무관하게 항상 딥 다크를 유지합니다(가독성·몰입).

Q. 특정 컴포넌트만 색이 어색합니다(테마가 안 먹습니다). 해당 컴포넌트가 토큰 대신 하드코딩 hex 를 쓴 것입니다. 규칙상 컴포넌트는 var(--infr-*) 토큰만 사용해야 합니다. resources/scss/pages 의 hex 직접값을 찾는 회귀 grep 으로 점검합니다:

rg -n "#[0-9a-fA-F]{3,6}" resources/scss/pages

Q. OS 다크모드를 따라가게 하고 싶습니다. 현재 구현은 OS 프레퍼런스(prefers-color-scheme)를 따르지 않습니다. 초기값은 항상 dark 이며, 사용자가 토글로 명시 선택한 값만 기억합니다. OS 연동은 로드맵 항목입니다.


관련 항목

상단바 오른쪽의 테마 · 언어 전환
상단바 오른쪽의 테마 · 언어 전환