181 lines
6.1 KiB
Plaintext
181 lines
6.1 KiB
Plaintext
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
|
|
<jsp:include page="/WEB-INF/views/theme-init.jsp"/>
|
|
<%
|
|
jakarta.servlet.http.HttpSession headerSession = request.getSession(false);
|
|
boolean headerLoggedIn = headerSession != null && headerSession.getAttribute("userId") != null;
|
|
String headerProfileHref = request.getContextPath() + (headerLoggedIn ? "/profile" : "/login");
|
|
String headerProfileLabel = headerLoggedIn ? "프로필" : "로그인";
|
|
%>
|
|
<style>
|
|
/* 기본(라이트) 헤더 — data-theme 없을 때도 동일 톤 */
|
|
.site-header {
|
|
--header-bg: #ffffff;
|
|
--header-text: #1a1a1a;
|
|
--header-muted: rgba(26, 26, 26, 0.55);
|
|
--header-border: rgba(0, 0, 0, 0.08);
|
|
--header-btn-hover: rgba(0, 0, 0, 0.06);
|
|
box-shadow: 0 1px 0 var(--header-border), 0 4px 16px rgba(0, 0, 0, 0.06);
|
|
}
|
|
html[data-theme="dark"] .site-header {
|
|
--header-bg: #1a1a1a;
|
|
--header-text: #f5f0e8;
|
|
--header-muted: rgba(245, 240, 232, 0.65);
|
|
--header-border: rgba(255, 255, 255, 0.06);
|
|
--header-btn-hover: rgba(255, 255, 255, 0.08);
|
|
box-shadow: 0 1px 0 var(--header-border), 0 8px 24px rgba(0, 0, 0, 0.35);
|
|
}
|
|
.site-header {
|
|
--accent: #e8a54b;
|
|
--header-h: 4rem;
|
|
background: var(--header-bg);
|
|
color: var(--header-text);
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 100;
|
|
}
|
|
.site-header__inner {
|
|
max-width: 72rem;
|
|
margin: 0 auto;
|
|
padding: 0 max(1rem, env(safe-area-inset-left)) 0 max(1rem, env(safe-area-inset-right));
|
|
height: var(--header-h);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 1rem;
|
|
}
|
|
.site-header__brand {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
text-decoration: none;
|
|
color: inherit;
|
|
font-weight: 600;
|
|
letter-spacing: -0.02em;
|
|
font-size: 1.125rem;
|
|
min-width: 0;
|
|
}
|
|
.site-header__brand:hover {
|
|
color: var(--accent);
|
|
}
|
|
.site-header__logo {
|
|
height: 2.25rem;
|
|
width: auto;
|
|
display: block;
|
|
border-radius: 6px;
|
|
object-fit: contain;
|
|
flex-shrink: 0;
|
|
}
|
|
.site-header__actions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.375rem;
|
|
flex-shrink: 0;
|
|
}
|
|
.site-header__icon-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 2.75rem;
|
|
height: 2.75rem;
|
|
padding: 0;
|
|
border: none;
|
|
border-radius: 10px;
|
|
background: transparent;
|
|
color: inherit;
|
|
cursor: pointer;
|
|
-webkit-tap-highlight-color: transparent;
|
|
transition: background 0.15s ease;
|
|
}
|
|
.site-header__icon-btn:hover {
|
|
background: var(--header-btn-hover);
|
|
}
|
|
.site-header__icon-btn:active {
|
|
transform: scale(0.96);
|
|
}
|
|
.site-header__icon-btn svg {
|
|
width: 1.375rem;
|
|
height: 1.375rem;
|
|
}
|
|
.site-header__icon-btn .icon-sun {
|
|
display: none;
|
|
}
|
|
.site-header__icon-btn .icon-moon {
|
|
display: block;
|
|
}
|
|
html[data-theme="dark"] .site-header__icon-btn .icon-moon {
|
|
display: none;
|
|
}
|
|
html[data-theme="dark"] .site-header__icon-btn .icon-sun {
|
|
display: block;
|
|
}
|
|
.site-header__profile {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 2.75rem;
|
|
height: 2.75rem;
|
|
border-radius: 50%;
|
|
background: var(--header-btn-hover);
|
|
color: var(--header-text);
|
|
text-decoration: none;
|
|
transition: background 0.15s ease, transform 0.15s ease;
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
.site-header__profile:hover {
|
|
background: var(--accent);
|
|
color: #1a1a1a;
|
|
}
|
|
.site-header__profile:active {
|
|
transform: scale(0.96);
|
|
}
|
|
.site-header__profile svg {
|
|
width: 1.35rem;
|
|
height: 1.35rem;
|
|
}
|
|
</style>
|
|
<header class="site-header" role="banner">
|
|
<div class="site-header__inner">
|
|
<a class="site-header__brand" href="${pageContext.request.contextPath}/">
|
|
<img class="site-header__logo" src="${pageContext.request.contextPath}/images/logo.png" alt="" width="120" height="36" />
|
|
<span>bibimbap</span>
|
|
</a>
|
|
<div class="site-header__actions">
|
|
<button type="button" class="site-header__icon-btn" id="theme-toggle" aria-label="다크 모드로 전환" title="테마 전환">
|
|
<%-- 라이트 모드일 때 달(다크로), 다크 모드일 때 해(라이트로) --%>
|
|
<svg class="icon-moon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
|
|
</svg>
|
|
<svg class="icon-sun" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
<circle cx="12" cy="12" r="4"/>
|
|
<path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/>
|
|
</svg>
|
|
</button>
|
|
<a class="site-header__profile" href="<%= headerProfileHref %>" aria-label="<%= headerProfileLabel %>" title="<%= headerProfileLabel %>">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
|
|
<circle cx="12" cy="7" r="4"/>
|
|
</svg>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
<jsp:include page="/WEB-INF/views/modal.jsp"/>
|
|
<script>
|
|
(function () {
|
|
var btn = document.getElementById('theme-toggle');
|
|
if (!btn) return;
|
|
function label() {
|
|
var dark = document.documentElement.getAttribute('data-theme') === 'dark';
|
|
btn.setAttribute('aria-label', dark ? '라이트 모드로 전환' : '다크 모드로 전환');
|
|
btn.setAttribute('title', dark ? '라이트 모드' : '다크 모드');
|
|
}
|
|
label();
|
|
btn.addEventListener('click', function () {
|
|
var next = document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
|
|
document.documentElement.setAttribute('data-theme', next);
|
|
try { localStorage.setItem('bibimbap-theme', next); } catch (e) {}
|
|
label();
|
|
});
|
|
})();
|
|
</script>
|