// 테마 토글 버튼 스타일. // // 아이콘 3종을 항상 DOM에 두고 CSS만으로 하나를 고른다 — 이 전환을 React state로 하면 // 서버·클라이언트 출력이 달라져 hydration이 깨진다(선택값이 localStorage에 있기 때문). // // 버튼 모양은 @fox 아이콘 버튼(ghost·md)이 갖는다. 헤더의 다른 버튼과 나란히 서므로 // header-icon-button.module.scss와 같은 근거로 시안의 radius만 덮는다. @use "@fox/styles/abstracts" as fox; // @fox 전역 스타일과 CSS 모듈은 명시도가 같아 로드 순서에 좌우된다 — `:global()`로 한 단계 // 얹어 순서와 무관하게 이기게 한다(header-icon-button.module.scss와 같은 방식). .button:global(.fox-icon-button) { position: relative; border-radius: fox.form(radius-sm); } // 현재 테마에 해당하는 것만 보인다. 아이콘과 스크린리더 문구가 같은 규칙을 공유해야 // 숨겨진 쪽의 글자까지 함께 사라진다 — 그래야 리더가 현재 선택 하나만 읽는다. .themeItem { display: none; } // "system"은 ``에 속성이 **없는** 상태로 표현된다(lib/constants/theme). :global(:root:not([data-theme])) .system, :global(:root[data-theme="light"]) .light, :global(:root[data-theme="dark"]) .dark { display: block; } .icon { inline-size: fox.icon(5); font-size: fox.icon(5); block-size: fox.icon(5); } // 보이는 글자가 없으므로 이 텍스트가 버튼의 접근성 이름이 된다. .srOnly { position: absolute; inline-size: 1px; block-size: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }