다크모드 팔레트

Dark mode palette

단순히 색을 반전하는 게 아니라, 표면(surface)이 위로 떠 있을수록 오히려 더 밝아지게 설계하는 것이 다크모드 팔레트의 핵심입니다.

다른 이름: Dark theme다크 테마
···
html
<div class="wrap">
  <div class="stage" id="stage">
    <div class="row">
      <div class="lvl" id="l0">Surface</div>
      <div class="lvl" id="l1">+1</div>
      <div class="lvl" id="l2">+2</div>
    </div>
  </div>
  <button class="toggle" id="toggle"><span id="tglText">Dark</span></button>
</div>
css
.wrap{width:min(440px,92%);display:grid;gap:clamp(10px,2.6vmin,16px);justify-items:center}
.stage{width:100%;border-radius:18px;padding:clamp(12px,3.4vmin,20px);transition:background .5s}
.row{display:flex;gap:clamp(8px,2.6vmin,16px);justify-content:center}
.lvl{flex:1;max-width:120px;aspect-ratio:1;border-radius:12px;display:grid;place-items:center;text-align:center;
  font:700 clamp(9px,2.6vmin,12px)/1 ui-monospace,monospace;border:1px solid transparent;
  transition:background .5s,box-shadow .5s,color .5s,border-color .5s}
.toggle{border:1px solid var(--line);background:var(--surface);color:var(--fg);border-radius:999px;padding:8px 18px;font:700 clamp(10px,2.6vmin,12px)/1 system-ui;cursor:pointer}
js
let isDark = false;
const stage = document.getElementById('stage'), l0 = document.getElementById('l0'), l1 = document.getElementById('l1'), l2 = document.getElementById('l2');
const toggle = document.getElementById('toggle'), tglText = document.getElementById('tglText');
const HUE = 230;
function hsl(h, s, l) { return 'hsl(' + h + ', ' + s + '%, ' + l + '%)'; }

function render() {
  if (isDark) {
    stage.style.background = hsl(HUE, 22, 9);
    l0.style.background = hsl(HUE, 14, 14); l0.style.borderColor = 'transparent'; l0.style.boxShadow = 'none'; l0.style.color = '#f4f4f6';
    l1.style.background = hsl(HUE, 14, 20); l1.style.borderColor = 'transparent'; l1.style.boxShadow = '0 6px 16px rgba(0,0,0,.35)'; l1.style.color = '#f4f4f6';
    l2.style.background = hsl(HUE, 14, 27); l2.style.borderColor = 'transparent'; l2.style.boxShadow = '0 10px 24px rgba(0,0,0,.45)'; l2.style.color = '#f4f4f6';
  } else {
    stage.style.background = hsl(HUE, 30, 94);
    l0.style.background = '#ffffff'; l0.style.borderColor = 'var(--line)'; l0.style.boxShadow = 'none'; l0.style.color = '#15151a';
    l1.style.background = '#ffffff'; l1.style.borderColor = 'var(--line)'; l1.style.boxShadow = '0 6px 16px rgba(0,0,0,.10)'; l1.style.color = '#15151a';
    l2.style.background = '#ffffff'; l2.style.borderColor = 'var(--line)'; l2.style.boxShadow = '0 10px 24px rgba(0,0,0,.16)'; l2.style.color = '#15151a';
  }
  tglText.textContent = isDark ? 'Dark' : 'Light';
}
function flip() { isDark = !isDark; render(); }
render();
toggle.addEventListener('click', e => { e.stopPropagation(); flip(); });
setInterval(flip, 2600);

라이트모드에서는 위로 뜬 요소(카드, 모달)를 그림자로 표현합니다. 하지만 어두운 배경 위에서는 그림자가 잘 안 보입니다. 그래서 Material Design 같은 시스템은 다크모드에서 "고도(elevation)가 높을수록 표면에 흰색을 살짝 더 섞어 밝게" 만드는 방식으로 위계를 표현합니다 — 고도 0은 가장 어둡고, 고도가 올라갈수록 조금씩 밝아집니다.

또한 다크모드는 순수 검정(#000)과 순수 흰색 텍스트를 피하는 게 좋습니다. 대비가 너무 강하면(특히 얇은 폰트에서) 눈부심과 헤일로 현상이 생깁니다 — 배경은 짙은 회색(#121212 근방), 텍스트는 완전한 흰색보다 약간 낮춘 명도를 씁니다.

브랜드 컬러도 다크 배경에서는 채도를 살짝 낮추고 명도를 올려야 튀지 않으면서도 잘 보입니다 — 라이트모드 색을 그대로 가져오면 대개 너무 쨍하거나 묻힙니다.

언제 쓰나

다크모드를 "색 반전"으로 자동 생성하지 말고, 표면 단계마다 명도를 따로 설계하세요.