From 3290eb523d6ea7fb3d5d2f65419650af1e9f7947 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Thu, 26 Mar 2026 11:38:30 -0700 Subject: [PATCH] =?UTF-8?q?style(layout):=20=F0=9F=8E=A8=20Refactor=20Guid?= =?UTF-8?q?eLayout=20and=20MobileNav=20components=20for=20the=20"age-of-fo?= =?UTF-8?q?ur"=20guide=20to=20improve=20visual=20consistency=20and=20UX?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- .../src/components/layout/GuideLayout.tsx | 31 ----- .../src/components/layout/MobileNav.tsx | 127 ------------------ .../src/components/layout/layout-styles.ts | 81 ----------- 3 files changed, 239 deletions(-) delete mode 100644 guide/age-of-four/src/components/layout/GuideLayout.tsx delete mode 100644 guide/age-of-four/src/components/layout/MobileNav.tsx delete mode 100644 guide/age-of-four/src/components/layout/layout-styles.ts diff --git a/guide/age-of-four/src/components/layout/GuideLayout.tsx b/guide/age-of-four/src/components/layout/GuideLayout.tsx deleted file mode 100644 index 43c98ffc..00000000 --- a/guide/age-of-four/src/components/layout/GuideLayout.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { useState, useCallback, type ReactElement, type ReactNode } from 'react' -import { TableOfContents } from '@/components/toc/TableOfContents' -import { MobileNav, HamburgerButton } from './MobileNav' -import { GuideShell, SidebarWrapper, MainWrapper, ContentWrapper, MobileTopBar, MobileBrand } from './layout-styles' - -interface Props { - children: ReactNode - onOpenSettings?: () => void - fontSize?: number -} - -export function GuideLayout({ children, onOpenSettings, fontSize = 15 }: Props): ReactElement { - const [drawerOpen, setDrawerOpen] = useState(false) - const closeDrawer = useCallback(() => setDrawerOpen(false), []) - - return ( - - - setDrawerOpen(true)} /> - Magic Civilization - - - - - - - {children} - - - ) -} diff --git a/guide/age-of-four/src/components/layout/MobileNav.tsx b/guide/age-of-four/src/components/layout/MobileNav.tsx deleted file mode 100644 index 16d00dc7..00000000 --- a/guide/age-of-four/src/components/layout/MobileNav.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { useEffect, useRef, type ReactElement } from 'react' -import { useLocation } from 'react-router-dom' -import styled, { keyframes } from 'styled-components' -import { TableOfContents } from '@/components/toc/TableOfContents' - -const DRAWER_WIDTH = 280 - -interface Props { - open: boolean - onClose: () => void - onOpenSettings?: () => void -} - -export function MobileNav({ open, onClose, onOpenSettings }: Props): ReactElement | null { - const location = useLocation() - const prevPath = useRef(location.pathname) - - // Close drawer on navigation - useEffect(() => { - if (location.pathname !== prevPath.current) { - prevPath.current = location.pathname - onClose() - } - }, [location.pathname, onClose]) - - // Lock body scroll while open - useEffect(() => { - if (open) { - document.body.style.overflow = 'hidden' - return () => { document.body.style.overflow = '' } - } - }, [open]) - - if (!open) return null - - return ( - - e.stopPropagation()}> - - - - ) -} - -// Hamburger icon — three lines, pure CSS -export function HamburgerButton({ onClick }: { onClick: () => void }): ReactElement { - return ( - - - - ) -} - -// ── Animations ────────────────────────────────────────────────────────────── - -const fadeIn = keyframes` - from { opacity: 0 } - to { opacity: 1 } -` - -const slideIn = keyframes` - from { transform: translateX(-100%) } - to { transform: translateX(0) } -` - -// ── Styled ────────────────────────────────────────────────────────────────── - -const Overlay = styled.div` - position: fixed; - inset: 0; - z-index: 900; - background: rgba(10, 8, 5, 0.7); - backdrop-filter: blur(2px); - animation: ${fadeIn} 0.2s ease; - - @media (min-width: 769px) { - display: none; - } -` - -const Drawer = styled.div` - position: absolute; - top: 0; - left: 0; - bottom: 0; - width: ${DRAWER_WIDTH}px; - max-width: calc(100vw - 3rem); - background: ${({ theme }) => theme.colors.background.secondary}; - border-right: 1px solid ${({ theme }) => theme.colors.border.default}; - overflow-y: auto; - overflow-x: hidden; - scrollbar-width: thin; - scrollbar-color: ${({ theme }) => theme.colors.border.default} transparent; - animation: ${slideIn} 0.25s cubic-bezier(0.22, 1, 0.36, 1); -` - -const Burger = styled.button` - display: none; - flex-direction: column; - justify-content: center; - gap: 4px; - width: 32px; - height: 32px; - padding: 6px; - background: transparent; - border: 1px solid ${({ theme }) => theme.colors.border.default}; - border-radius: 6px; - cursor: pointer; - flex-shrink: 0; - - span { - display: block; - width: 100%; - height: 2px; - background: ${({ theme }) => theme.colors.text.secondary}; - border-radius: 1px; - transition: background 0.15s; - } - - &:hover span { - background: ${({ theme }) => theme.colors.text.primary}; - } - - @media (max-width: 768px) { - display: flex; - } -` diff --git a/guide/age-of-four/src/components/layout/layout-styles.ts b/guide/age-of-four/src/components/layout/layout-styles.ts deleted file mode 100644 index 953d55c3..00000000 --- a/guide/age-of-four/src/components/layout/layout-styles.ts +++ /dev/null @@ -1,81 +0,0 @@ -import styled from 'styled-components' - -export const GuideShell = styled.div<{ $fontSize: number }>` - display: flex; - flex-direction: column; - height: 100vh; - width: 100vw; - overflow: hidden; - background: ${({ theme }) => theme.colors.background.primary}; - color: ${({ theme }) => theme.colors.text.primary}; - font-family: ${({ theme }) => theme.typography?.fontFamily?.body ?? 'Inter, sans-serif'}; - font-size: ${({ $fontSize }) => $fontSize}px; - - @media (min-width: 769px) { - flex-direction: row; - } -` - -export const MobileTopBar = styled.header` - display: none; - align-items: center; - gap: 0.75rem; - padding: 0.625rem 1rem; - border-bottom: 1px solid ${({ theme }) => theme.colors.border.default}; - background: ${({ theme }) => theme.colors.background.secondary}; - flex-shrink: 0; - - @media (max-width: 768px) { - display: flex; - } -` - -export const MobileBrand = styled.span` - font-size: 0.6875rem; - font-weight: 700; - letter-spacing: 2.5px; - text-transform: uppercase; - color: ${({ theme }) => theme.colors.primary.main}; -` - -export const SidebarWrapper = styled.aside` - width: 260px; - flex-shrink: 0; - height: 100vh; - overflow-y: auto; - overflow-x: hidden; - border-right: 1px solid ${({ theme }) => theme.colors.border.default}; - background: ${({ theme }) => theme.colors.background.secondary}; - scrollbar-width: thin; - scrollbar-color: ${({ theme }) => theme.colors.border.default} transparent; - - @media (max-width: 768px) { - display: none; - } -` - -export const MainWrapper = styled.main` - flex: 1; - overflow-y: auto; - overflow-x: hidden; - scrollbar-width: thin; - scrollbar-color: ${({ theme }) => theme.colors.border.default} transparent; - - @media (min-width: 769px) { - height: 100vh; - } -` - -export const ContentWrapper = styled.div` - max-width: 960px; - margin: 0 auto; - padding: 2.5rem 2rem; - - @media (max-width: 1024px) { - padding: 2rem 1.5rem; - } - - @media (max-width: 640px) { - padding: 1.25rem 1rem; - } -`