문제

"디자인은 깔끔한데 죽어 보여." 사이트를 열어 본 첫 반응이 그랬다. 진입 페이지는 멀끔한데, 다음 페이지로 넘어갈 이유가 없었다. 받은 주문은 한 줄, "톤은 인터랙티브 좋아"였다.

문제는 그 한 줄이 가리키는 범위가 너무 넓다는 거였다. 3D나 게임화까지 가면 인터랙티브하긴 한데 애써 잡은 미니멀 톤을 무너뜨린다. 호버 효과만 얹으면 톤은 지키지만 여전히 죽어 보인다. 그래서 "어디까지 할 것인가"부터 내가 직접 선을 그어야 했다.

의사결정

선을 이렇게 그었다. 화려한 모션으로 살아 보이게 하는 길은 버렸다 — 미니멀 톤과 정면충돌이라. 대신 정보 구조와 반응 속도로 살아 있게 만들기로 했다. Cmd+K 한 번에 모든 글에 닿고, 키보드만으로 사이트 전체를 도는 것. 베이스 디자인은 손대지 않고 그 위에 한 겹을 얹는 방향이다.

기준은 단순했다. "이 인터랙션이 미니멀 톤을 깨는가, 아니면 정보에 더 빨리 닿게 하는가." 깨는 건 전부 잘랐고, 빨라지는 것만 남겼다.

만든 것

방향을 정한 뒤로는 혼자 끝까지 밀어붙였다. 검색·단축키·테마·접근성을 차례로 붙였고, 각각이 미니멀 톤을 깨지 않는지 매번 되물으며 작업했다.

글로벌 검색 (Cmd+K)

  • lib/search.js — Fuse.js 인덱스 (cases·notes·essays·logs 통합)
  • CommandPalette.jsx — ↑↓ Enter 키보드 네비, 결과 그룹별 표시
  • 모바일 input: inputMode="search" autoCorrect="off" autoCapitalize="off" enterKeyHint="search"

키보드 단축키 (GlobalShortcuts.jsx)

  • ? — 도움말 모달
  • / 또는 ⌘K — 검색
  • T — 테마 토글 (라이트 ↔ 다크)
  • g h/c/n/e/l/a/u/w/r/v — 페이지 점프 시퀀스 (1200ms idle timeout)

다크 테마 시스템

  • UIProvider (lib/ui-context.jsx) — localStorage 영속 + prefers-color-scheme 추정
  • data-theme HTML attribute로 토큰 swap
  • color-scheme: dark로 form·scrollbar 자동 적응

모달 접근성 (inert 패턴)

  • 3 모달(Palette/Help/MobileMenu) 모두 previouslyFocused 캐시 + 닫힘 시 trigger focus 복귀
  • inert 속성으로 background subtree 비활성화 (focus trap 라이브러리 대체)
  • :focus-visible 글로벌 outline + prefers-reduced-motion 가드

결과

처음의 "죽어 보임"은 사라졌다. 이제 정적 사이트인데 SaaS처럼 움직인다 — 그것도 검색 7KB(Fuse.js) + 키보드 단축키 ~3KB라는 가벼운 예산 안에서. 키보드만으로 사이트 전체를 탐색할 수 있고, 720px 아래에서는 햄버거 메뉴로 알아서 갈아탄다. 접근성은 구색이 아니라 통과 기준으로 두고 WCAG 2.2 1.4.13/2.4.7/2.4.11과 APG dialog 패턴까지 맞췄다.

회고

작업하며 가장 흔히 터지는 사고를 직접 밟았다. 모달이 background focus를 trap하지 않으면 Tab이 모달 밖으로 새어 나간다. focus trap 라이브러리를 들이는 대신 표준 inert 속성 하나로 끝냈다 — 의존성 없이, 브라우저가 보장하는 방식으로.

배운 건 분명하다. 사이트를 살아 있게 만드는 건 화려한 애니메이션이 아니라 반응 속도와 키보드 우선이다. 모호한 한 줄 주문에서 스스로 범위를 긋고, 끝까지 혼자 만들어 굴러가게 했다 — 나는 그렇게 일한다.