Files
my-app-store/src/routes/+layout.svelte
2025-11-10 16:54:07 +08:00

439 lines
12 KiB
Svelte

<script lang="ts">
import { onMount } from 'svelte';
import { page } from '$app/stores';
import { goto } from '$app/navigation';
let isDarkMode = false;
let currentPath = '/';
let currentCategory: string | null = null;
const primaryNav = [
{ label: 'Today', href: '/', icon: 'today' },
{ label: 'Projects', href: '/projects', icon: 'projects' },
{ label: 'About', href: '/about', icon: 'about' },
{ label: 'Contact', href: '/contact', icon: 'contact' }
];
const categoryNav = [
{ label: 'Brand Design', href: '/projects?category=Brand Design' },
{ label: 'UI/UX Design', href: '/projects?category=UI/UX Design' },
{ label: 'Web Design', href: '/projects?category=Web Design' },
{ label: 'Graphic Design', href: '/projects?category=Graphic Design' },
{ label: 'Photography', href: '/projects?category=Photography' },
{ label: 'Print Design', href: '/projects?category=Print Design' }
];
function toggleTheme() {
isDarkMode = !isDarkMode;
document.documentElement.classList.toggle('dark', isDarkMode);
}
function navigateTo(href: string) {
goto(href);
}
$: currentPath = $page.url.pathname;
$: currentCategory = $page.url.searchParams.get('category');
onMount(() => {
// 检测系统主题偏好
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
isDarkMode = prefersDark;
document.documentElement.classList.toggle('dark', isDarkMode);
});
</script>
<svelte:head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="LiuBai Design - A modern creative studio experience built with SvelteKit" />
</svelte:head>
<div class="app-shell">
<aside class="sidebar">
<header class="sidebar__header">
<div class="sidebar__store-icon" aria-hidden="true">🎨</div>
<div class="sidebar__heading">
<span class="sidebar__title">LiuBai Design</span>
<span class="sidebar__subtitle">Creative Studio</span>
</div>
</header>
<div class="sidebar__search">
<span class="sidebar__search-icon" aria-hidden="true">🔍</span>
<input type="search" placeholder="Search" aria-label="Search LiuBai Design" />
</div>
<nav class="sidebar__nav">
<div class="sidebar__section" aria-label="Primary navigation">
{#each primaryNav as item}
<button
class:active={item.href === '/' ? currentPath === '/' : currentPath.startsWith(item.href)}
on:click={() => navigateTo(item.href)}
>
<span class="sidebar__bullet" aria-hidden="true"></span>
{item.label}
</button>
{/each}
</div>
<div class="sidebar__section" aria-label="Design categories">
<span class="sidebar__section-label">Design Categories</span>
{#each categoryNav as item}
<button
class:active={currentCategory === item.label}
on:click={() => navigateTo(item.href)}
>
{item.label}
</button>
{/each}
</div>
</nav>
<footer class="sidebar__footer">
<button on:click={toggleTheme} aria-label="Toggle theme">
<span class="sidebar__avatar" aria-hidden="true">{isDarkMode ? '☀️' : '🌙'}</span>
{isDarkMode ? 'Light' : 'Dark'}
</button>
<button aria-label="Account">
<span class="sidebar__avatar" aria-hidden="true">🧑‍💻</span>
Account
</button>
</footer>
</aside>
<main class="content">
<slot />
</main>
</div>
<style>
:global(html) {
height: 100%;
scroll-behavior: smooth;
}
:global(body) {
margin: 0;
padding: 0;
font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
transition: background-color 0.3s ease, color 0.3s ease;
}
/* 亮色模式 */
:global(body) {
background: #f5f5f7;
color: #1d1d1f;
}
/* 暗色模式 */
:global(.dark body) {
background: radial-gradient(circle at top left, rgba(46, 46, 82, 0.6), transparent 40%),
radial-gradient(circle at bottom right, rgba(36, 36, 60, 0.5), transparent 45%),
#050506;
color: #ffffff;
}
.app-shell {
display: grid;
grid-template-columns: 280px minmax(0, 1fr);
min-height: 100vh;
}
.sidebar {
display: grid;
grid-template-rows: auto auto 1fr auto;
gap: 32px;
padding: 32px 24px;
background: rgba(255, 255, 255, 0.8);
border-right: 1px solid rgba(0, 0, 0, 0.08);
backdrop-filter: blur(30px);
box-shadow: 12px 0 40px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
:global(.dark) .sidebar {
background: rgba(10, 10, 18, 0.9);
border-right: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: 12px 0 40px rgba(0, 0, 0, 0.35);
}
.sidebar__header {
display: flex;
gap: 12px;
align-items: center;
}
.sidebar__store-icon {
width: 36px;
height: 36px;
border-radius: 12px;
background: linear-gradient(135deg, #4f46e5 0%, #9333ea 100%);
display: grid;
place-items: center;
font-size: 20px;
color: white;
}
.sidebar__heading {
display: flex;
flex-direction: column;
color: rgba(0, 0, 0, 0.9);
transition: color 0.3s ease;
}
:global(.dark) .sidebar__heading {
color: rgba(255, 255, 255, 0.9);
}
.sidebar__title {
font-size: 15px;
font-weight: 600;
letter-spacing: 0.02em;
}
.sidebar__subtitle {
font-size: 13px;
color: rgba(255, 255, 255, 0.55);
}
.sidebar__search {
position: relative;
display: flex;
align-items: center;
}
.sidebar__search-icon {
position: absolute;
left: 14px;
font-size: 14px;
color: rgba(0, 0, 0, 0.4);
transition: color 0.3s ease;
}
:global(.dark) .sidebar__search-icon {
color: rgba(255, 255, 255, 0.4);
}
.sidebar__search input {
width: 100%;
padding: 10px 14px 10px 34px;
border-radius: 12px;
border: none;
background: rgba(0, 0, 0, 0.08);
color: rgba(0, 0, 0, 0.88);
font-size: 13px;
transition: all 0.3s ease;
}
:global(.dark) .sidebar__search input {
background: rgba(255, 255, 255, 0.08);
color: rgba(255, 255, 255, 0.88);
}
.sidebar__search input:focus {
outline: none;
background: rgba(0, 0, 0, 0.12);
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.35);
}
:global(.dark) .sidebar__search input:focus {
background: rgba(255, 255, 255, 0.12);
box-shadow: 0 0 0 2px rgba(147, 197, 253, 0.35);
}
.sidebar__nav {
display: flex;
flex-direction: column;
gap: 36px;
}
.sidebar__section {
display: grid;
gap: 4px;
}
.sidebar__section-label {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
color: rgba(0, 0, 0, 0.35);
margin-bottom: 8px;
transition: color 0.3s ease;
}
:global(.dark) .sidebar__section-label {
color: rgba(255, 255, 255, 0.35);
}
.sidebar__section button {
position: relative;
display: flex;
align-items: center;
gap: 12px;
padding: 10px 14px;
border-radius: 12px;
border: none;
background: transparent;
color: rgba(0, 0, 0, 0.66);
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
}
:global(.dark) .sidebar__section button {
color: rgba(255, 255, 255, 0.66);
}
.sidebar__section button:hover {
color: rgba(0, 0, 0, 0.9);
background: rgba(0, 0, 0, 0.06);
}
:global(.dark) .sidebar__section button:hover {
color: rgba(255, 255, 255, 0.9);
background: rgba(255, 255, 255, 0.06);
}
.sidebar__section button.active {
color: #ffffff;
background: linear-gradient(135deg, rgba(88, 88, 255, 0.45), rgba(138, 92, 255, 0.45));
}
.sidebar__bullet {
width: 6px;
height: 6px;
border-radius: 50%;
background: rgba(0, 0, 0, 0.4);
transition: background 0.3s ease;
}
:global(.dark) .sidebar__bullet {
background: rgba(255, 255, 255, 0.4);
}
.sidebar__section button.active .sidebar__bullet {
background: #ffffff;
box-shadow: 0 0 10px rgba(255, 255, 255, 0.8);
}
.sidebar__footer button {
width: 100%;
display: flex;
align-items: center;
gap: 10px;
padding: 12px 14px;
border-radius: 12px;
border: 1px solid rgba(0, 0, 0, 0.08);
background: rgba(0, 0, 0, 0.03);
color: rgba(0, 0, 0, 0.72);
font-size: 13px;
cursor: pointer;
transition: all 0.3s ease;
margin-bottom: 8px;
}
:global(.dark) .sidebar__footer button {
border: 1px solid rgba(255, 255, 255, 0.08);
background: rgba(255, 255, 255, 0.03);
color: rgba(255, 255, 255, 0.72);
}
.sidebar__footer button:hover {
border-color: rgba(0, 0, 0, 0.2);
background: rgba(0, 0, 0, 0.08);
color: #000000;
}
:global(.dark) .sidebar__footer button:hover {
border-color: rgba(255, 255, 255, 0.2);
background: rgba(255, 255, 255, 0.08);
color: #ffffff;
}
.sidebar__avatar {
width: 28px;
height: 28px;
border-radius: 9px;
background: rgba(0, 0, 0, 0.1);
display: grid;
place-items: center;
transition: background 0.3s ease;
}
:global(.dark) .sidebar__avatar {
background: rgba(255, 255, 255, 0.1);
}
.content {
position: relative;
overflow-y: auto;
padding: 48px 56px 64px;
background: linear-gradient(180deg, rgba(245, 245, 247, 0.9) 0%, rgba(240, 240, 242, 0.92) 55%, rgba(235, 235, 237, 0.96) 100%);
box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}
:global(.dark) .content {
background: linear-gradient(180deg, rgba(16, 16, 28, 0.9) 0%, rgba(8, 8, 18, 0.92) 55%, rgba(6, 6, 12, 0.96) 100%);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
.content::-webkit-scrollbar {
width: 10px;
}
.content::-webkit-scrollbar-track {
background: rgba(200, 200, 200, 0.7);
}
:global(.dark) .content::-webkit-scrollbar-track {
background: rgba(12, 12, 18, 0.7);
}
.content::-webkit-scrollbar-thumb {
background: rgba(150, 150, 150, 0.45);
border-radius: 6px;
}
:global(.dark) .content::-webkit-scrollbar-thumb {
background: rgba(110, 110, 145, 0.45);
}
@media (max-width: 1024px) {
.app-shell {
grid-template-columns: 240px 1fr;
}
.content {
padding: 36px 32px 48px;
}
}
@media (max-width: 860px) {
.app-shell {
grid-template-columns: 1fr;
}
.sidebar {
position: sticky;
top: 0;
z-index: 10;
grid-template-rows: auto;
grid-auto-flow: column;
overflow-x: auto;
padding: 20px 24px;
gap: 24px;
}
.sidebar__nav,
.sidebar__footer {
display: none;
}
.content {
padding: 24px;
}
}
</style>