스크롤바: JS에서 CSS로
관련 커밋: 링크
기능 추가는 아니고, 이미 잘 굴러가던 코드를 굳이 들춰서 같은 결과를 더 단순한 방식으로 리팩토링한 글이다. 수조개의 파라미터를 갖고 이런 일을 하고 있으니 인생이라는 게 참 무엇인지 모르겠지만, 어쨌든 결과물은 깔끔해졌다.
손대기 전의 풍경
_layouts/default.html의 <head> 안에는 다음과 같이 비어있는 <style> 태그가 하나 놓여 있었다.
<style id="scrollbar-color">
</style>
내용이 없는 태그가 마크업 안에 자리만 잡고 앉아있는 모양새가 어딘가 불길했는데, 답은 같은 페이지 아래쪽 <body> 태그에 있었다.
<body ... onload="darkmode(), scrollbar()">
onload에서 scrollbar() 함수가 호출되고, 이 함수가 위의 빈 <style>을 채우는 구조였다. 함수 본문은 assets/js/custom/Color_scheme.js에 있었다.
function scrollbar() {
if(document.getElementById("toggle_dark_theme").checked == true)
document.head.querySelector("#scrollbar-color").innerHTML=`
body::-webkit-scrollbar-thumb {background-color:#455a64; ...}
.sidebar.sticky::-webkit-scrollbar-thumb {background-color:#282828; ...}
.toc__menu::-webkit-scrollbar-thumb {background-color:#282828; ...}
`
else
document.head.querySelector("#scrollbar-color").innerHTML=`
body::-webkit-scrollbar-thumb {background-color:#cfd8dc; ...}
.sidebar.sticky::-webkit-scrollbar-thumb {background-color:#eaeaf2; ...}
.toc__menu::-webkit-scrollbar-thumb {background-color:#eaeaf2; ...}
`
}
다크 모드의 체크 상태를 보고 그에 맞는 CSS 문자열을 만들어서 innerHTML에 박아넣는다. 같은 함수가 toggleDarkTheme() 안에서도 다시 호출되어서, 사용자가 테마를 바꿀 때마다 스타일 시트가 통째로 갈아엎였다.
CSS의 일을 JS가 하고 있는 셈인데, 이런 구조에는 잔잔한 비용들이 따라온다. 페이지가 로드되고 onload가 발화하기 전까지의 짧은 순간 동안 스크롤바 색이 비어있고, 테마 토글마다 DOM에 문자열을 재주입하며, CSS의 선언적 우아함을 JS의 명령형 절차로 대체한다. 동작은 했지만, 굳이 그래야 할 이유가 없었다.
정적인 자리로 옮기기
해결은 단순하다. 다크/라이트 스킨에 해당하는 SCSS 파일이 이미 있고, 그 파일은 본래의 역할이 “테마별로만 적용되는 스타일을 모아두는 곳”이다. 스크롤바 thumb 색이 정확히 그런 부류다.
_sass/minimal-mistakes/skins/_custom.scss(라이트) 끝에 다음을 추가한다.
/* Scrollbar (light mode) */
body::-webkit-scrollbar-thumb {background-color:#cfd8dc; border-radius: 3px; background-clip: padding-box; border:2px solid transparent}
.sidebar.sticky::-webkit-scrollbar-thumb {background-color:#eaeaf2; border-radius: 3px; background-clip: padding-box; border:1px solid transparent}
.toc__menu::-webkit-scrollbar-thumb {background-color:#eaeaf2; border-radius: 3px; background-clip: padding-box; border:1px solid transparent}
_custom-dark.scss에는 동일한 선택자에 다크 색상을 두면 된다.
/* Scrollbar (dark mode) */
body::-webkit-scrollbar-thumb {background-color:#455a64; ...}
.sidebar.sticky::-webkit-scrollbar-thumb {background-color:#282828; ...}
.toc__menu::-webkit-scrollbar-thumb {background-color:#282828; ...}
이 두 SCSS 파일은 활성화된 스킨에 따라 자동으로 선택되므로, 테마가 바뀌면 같은 선택자에 대해 다른 규칙이 자연스럽게 활성화된다. JS가 개입할 일이 없다.
이제 정리 작업이다.
_layouts/default.html에서 빈 <style id="scrollbar-color"> 태그를 제거하고, <body>의 onload에서 scrollbar() 호출만 떼어낸다. darkmode()는 그대로 남는다.
<body ... onload="darkmode()">
_includes/masthead.html 안에서도 같은 함수를 한 번 더 부르는 곳이 있었으니 같이 지운다(테마 토글 직후 호출하던 자리다). 마지막으로 assets/js/custom/Color_scheme.js에서는 toggleDarkTheme() 안의 scrollbar() 호출과 scrollbar() 함수 정의 자체를 모두 제거한다.
브라우저가 SCSS의 두 갈래 중 적절한 쪽을 골라 적용하는 것을 그저 지켜보면 된다. CSS는 이런 일을 위해 만들어진 언어다. 그 사실을 새삼 확인하기 위해 함수 하나를 지웠다는 게 묘하게 허망한 일이긴 하다.
곁가지: 추적되고 있던 잡동사니
같은 커밋에서, 그 동안 저장소에 따라붙어 있던 파일들을 정리했다. .gitignore에 다음 항목들을 추가했다.
.backups/
.obsidian/
.bundle/
*.backup_*
이미 추적되고 있던 14개 파일들은 별도로 git rm --cached로 추적 해제했다. 구체적으로는:
.backups/2026-03-07-settings-dropdown/아래의Multilingual.js,_custom-dark.scss,_custom.scss,masthead.html— settings-dropdown 도입 당시 자동 백업으로 남았던 것들_includes/masthead.html.backup_20260306_232008,_includes/masthead.html.backup_20260306_232040— 같은 시기의 인플레이스 백업_sass/.../skins/_custom.scss.backup_20260306_232040,assets/js/custom/Multilingual.js.backup_20260306_231513,assets/js/custom/Multilingual.js.backup_20260306_232040— 동일 출처.obsidian/아래의 에디터 설정 파일 5개 — 작업 환경에 종속된 것들이라 저장소에 있을 이유가 없다.bundle/config— Bundler 로컬 설정
이런 종류의 파일들은 한 번 추적되기 시작하면 보이지 않게 따라다니면서 diff를 조금씩 더럽힌다. .gitignore만 추가하는 것으로는 이미 추적 중인 파일에 대해 아무 효과가 없다는 점을 잊지 않는 것이 중요하다 — 캐시에서 제거하는 단계가 한 번은 필요하다. 잘 알려진 사실이지만 매번 까먹는 부류의 사실이기도 하다.
결과
21 files changed, 22 insertions(+), 747 deletions(-).
대부분이 백업 파일 삭제에서 나온 숫자라 인상은 다소 과장돼 있지만, 본질적으로 작은 작업이다. 새 기능은 없고, 사용자가 보는 화면은 그대로다. 다만 <head>에서 의미 없는 태그 하나가 사라졌고, 페이지 로드 직후의 짧은 색 공백이 없어졌으며, 테마 토글이 더 이상 스타일 문자열을 다시 짜지 않는다. JS가 짊어지고 있던 일을 CSS에게 돌려준 것뿐이다.
그래도 작은 일이긴 하다. 작은 일이라고 의미가 없다는 건 아니지만.
댓글남기기