헤더: 코드로 참여 입력칸 빼고 테마 버튼(🌙/☀️) 추가 — 누르면 밝게↔어둡게

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
EJClaw
2026-10-07 14:40:26 +09:00
parent 843b18aea1
commit 53fbbc1c9d
3 changed files with 40 additions and 46 deletions

View File

@@ -1,28 +1,12 @@
/** PC top menu with an always-available room-code box (docs/07-ui-ux.md §3.2). */
/** PC top menu (docs/07-ui-ux.md §3.2): 로고·메뉴, 오른쪽에 테마 버튼과 로그인/계정 메뉴. */
import { useEffect, useRef, useState } from 'react';
import { Link, NavLink, useNavigate } from 'react-router';
import { normalizeRoomCode } from '@bg/shared/lite';
import { api } from '../net/api';
import { useSession } from '../store/session';
import { toast } from '../store/toast';
import { Avatar, ConfirmSheet } from './ui';
export function SiteHeader() {
const { me, siteName, role } = useSession();
const [code, setCode] = useState('');
const nav = useNavigate();
const join = async (e: React.FormEvent) => {
e.preventDefault();
const c = normalizeRoomCode(code);
if (!c) return toast('방 코드 6자리를 넣어 주세요.', 'error');
try {
await api.room(c);
setCode('');
nav(`/r/${c}`);
} catch (ex) {
toast((ex as Error).message, 'error');
}
};
return (
<header className="site-header">
<div className="site-header-inner">
@@ -37,10 +21,7 @@ export function SiteHeader() {
<NavLink to="/games">게임 목록</NavLink>
{role !== 'user' && <NavLink to="/admin">관리자</NavLink>}
</nav>
<form className="header-join" onSubmit={join} aria-label="코드로 참여">
<input value={code} onChange={(e) => setCode(e.target.value)} placeholder="방 코드 6자리" inputMode="numeric" maxLength={40} aria-label="방 코드" />
<button className="btn btn-primary btn-sm">참여</button>
</form>
<ThemeToggle />
{me ? (
<AccountMenu />
) : (
@@ -53,6 +34,28 @@ export function SiteHeader() {
);
}
const darkQuery = () => matchMedia('(prefers-color-scheme: dark)');
/** 누를 때마다 밝게 ↔ 어둡게. '자동'이면 지금 보이는 색의 반대로 바꿔요. 설정 페이지의 '화면 색'과 같은 값이에요. */
function ThemeToggle() {
const theme = useSession((s) => s.settings.theme);
const updateSettings = useSession((s) => s.updateSettings);
const [systemDark, setSystemDark] = useState(() => darkQuery().matches);
useEffect(() => {
const q = darkQuery();
const on = () => setSystemDark(q.matches);
q.addEventListener('change', on);
return () => q.removeEventListener('change', on);
}, []);
const dark = theme === 'dark' || (theme === 'auto' && systemDark);
const label = dark ? '밝은 화면으로 바꾸기' : '어두운 화면으로 바꾸기';
return (
<button type="button" className="theme-toggle" aria-label={label} title={label} onClick={() => updateSettings({ theme: dark ? 'light' : 'dark' })}>
<span aria-hidden>{dark ? '☀️' : '🌙'}</span>
</button>
);
}
/** 로그인한 뒤: 닉네임(디스코드 연결 시 디스코드 이름) → 누르면 [설정] [로그아웃] 메뉴. */
function AccountMenu() {
const me = useSession((s) => s.me)!;

View File

@@ -589,29 +589,23 @@ label.field {
color: var(--primary);
background: var(--primary-soft);
}
.header-join {
display: flex;
gap: 6px;
.theme-toggle {
margin-left: auto;
}
.header-join input {
width: 150px;
width: 42px;
height: 42px;
border-radius: 10px;
flex: none;
border: 2px solid var(--border);
background: var(--bg);
padding: 0 12px;
font-size: 1.05rem;
font-weight: 700;
letter-spacing: 0.08em;
font-variant-numeric: tabular-nums;
border-radius: 999px;
background: transparent;
font-size: 1.2rem;
line-height: 1;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
}
.header-join input:focus {
outline: none;
border-color: var(--primary);
}
.header-join .btn {
min-height: 42px;
.theme-toggle:hover {
background: var(--surface-2);
}
.header-user {
display: flex;
@@ -693,9 +687,6 @@ button.header-user {
padding: 0 12px;
gap: 8px;
}
.header-join {
display: none;
}
.site-logo .full {
display: none;
}