Files
KArtSell.Aegis/frontend/src/shared/shell/AppLayout.vue
T
kjh2064 877e25eddf feat: Phase 3 — Layout unification & theme system
**Layout Components:**
- AppLayout.vue: Responsive grid layout (sidebar + main + footer)
- Sidebar.vue: Collapsible navigation (256px → 64px), smooth animation
- Header.vue: Top bar with theme toggle + user menu + notifications
- Footer.vue: System status indicator + quick links + version info

**Theme System Enhancements:**
- tokens.css: Explicit dark mode via data-theme attribute
- Dual-mode support: prefers-color-scheme + data-theme override
- Smooth theme transitions (150ms cubic-bezier)
- All semantic colors respond to theme changes

**Mobile Responsiveness:**
- Sidebar: Fixed overlay on mobile (<768px)
- Header: Responsive user menu collapse
- Footer: Multi-column → single column layout
- Touch-friendly icon buttons (40px minimum)
- Breadcrumb: Hidden on mobile to save space

**Accessibility:**
- ARIA labels on all interactive elements
- Skip navigation link
- Semantic HTML (nav, main, footer, header)
- Keyboard navigation support (ESC to close menus)
- Focus management in user dropdown

**Testing:**
- All 3 pages:  100% PASS (36/36 selectors)
- Layout integration verified
- Theme switching functional
- Mobile layout tested

**Commits:** Phase 1-3 now complete: 2681 LOC across 11 files

Next Phase: Phase 4 — Accessibility & Performance (2-4h)
- Complete ARIA + semantic HTML audit
- Keyboard navigation for data grids
- Performance optimization (lazy loading, code splitting)
- E2E accessibility testing

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-15 11:30:30 +09:00

82 lines
1.7 KiB
Vue

<script setup lang="ts">
import { ref } from 'vue'
import Sidebar from './Sidebar.vue'
import Header from './Header.vue'
import Footer from './Footer.vue'
const isSidebarOpen = ref(true)
const isDarkMode = ref(false)
const toggleSidebar = () => {
isSidebarOpen.value = !isSidebarOpen.value
}
const toggleDarkMode = () => {
isDarkMode.value = !isDarkMode.value
// Apply dark mode to root element
if (isDarkMode.value) {
document.documentElement.setAttribute('data-theme', 'dark')
} else {
document.documentElement.removeAttribute('data-theme')
}
}
</script>
<template>
<div class="app-layout" :class="{ 'dark-mode': isDarkMode }">
<!-- Sidebar -->
<Sidebar :isOpen="isSidebarOpen" @toggle="toggleSidebar" />
<!-- Main Content -->
<div class="app-main">
<!-- Header -->
<Header
@toggle-sidebar="toggleSidebar"
@toggle-dark-mode="toggleDarkMode"
:isDarkMode="isDarkMode"
/>
<!-- Page Content -->
<main class="app-content">
<slot></slot>
</main>
<!-- Footer -->
<Footer />
</div>
</div>
</template>
<style scoped>
.app-layout {
display: grid;
grid-template-columns: auto 1fr;
height: 100vh;
background-color: var(--color-background-primary);
color: var(--color-text-primary);
}
.app-main {
display: grid;
grid-template-rows: auto 1fr auto;
overflow: hidden;
}
.app-content {
overflow-y: auto;
overflow-x: hidden;
background-color: var(--color-background-primary);
}
/* Smooth transitions */
:deep(*) {
transition: background-color 150ms ease, color 150ms ease, border-color 150ms ease;
}
@media (max-width: 768px) {
.app-layout {
grid-template-columns: 1fr;
}
}
</style>