diff --git a/app/components/Sidebar.module.css b/app/components/Sidebar.module.css new file mode 100644 index 0000000..b395428 --- /dev/null +++ b/app/components/Sidebar.module.css @@ -0,0 +1,142 @@ +/* Hamburger button */ +.hamburger { + position: fixed; + top: 24px; + right: 24px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 5px; + width: 40px; + height: 40px; + padding: 0; + background: rgba(0, 0, 0, 0.9); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 50%; + cursor: pointer; + z-index: 1100; + transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); +} + +.hamburger:hover { + background: rgba(0, 0, 0, 1); + border-color: rgba(255, 255, 255, 0.4); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); +} + +.hamburgerLine { + width: 16px; + height: 1.5px; + background: #ffffff; + border-radius: 1px; + transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1); + transform-origin: center; +} + +.hamburgerLineTop { + transform: translateY(6.5px) rotate(45deg); +} + +.hamburgerLineMid { + opacity: 0; +} + +.hamburgerLineBot { + transform: translateY(-6.5px) rotate(-45deg); +} + +/* Overlay backdrop */ +.overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.5); + backdrop-filter: blur(4px); + opacity: 0; + pointer-events: none; + transition: opacity 0.3s cubic-bezier(0.16, 1, 0.3, 1); + z-index: 1050; +} + +.overlayVisible { + opacity: 1; + pointer-events: auto; +} + +/* Sidebar panel */ +.sidebar { + position: fixed; + top: 0; + right: 0; + width: 320px; + max-width: 85vw; + height: 100dvh; + background: rgba(10, 10, 10, 0.95); + backdrop-filter: blur(20px); + border-left: 1px solid rgba(255, 255, 255, 0.1); + transform: translateX(100%); + transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1); + z-index: 1075; + display: flex; + flex-direction: column; + justify-content: center; + padding: 80px 40px; +} + +.sidebarOpen { + transform: translateX(0); +} + +/* Nav list */ +.navList { + list-style: none; + display: flex; + flex-direction: column; + gap: 8px; +} + +.navLink { + display: block; + padding: 14px 20px; + color: rgba(255, 255, 255, 0.6); + text-decoration: none; + font-size: 18px; + font-weight: 600; + letter-spacing: -0.01em; + border-radius: 12px; + transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); +} + +.navLink:hover { + color: #ffffff; + background: rgba(255, 255, 255, 0.08); +} + +.navLinkActive { + color: #ffffff; + background: rgba(255, 255, 255, 0.1); +} + +@media (max-width: 768px) { + .hamburger { + top: 16px; + right: 16px; + width: 36px; + height: 36px; + } + + .hamburgerLine { + width: 14px; + } + + .sidebar { + padding: 60px 24px; + } + + .navLink { + font-size: 16px; + padding: 12px 16px; + } +} diff --git a/app/components/Sidebar.tsx b/app/components/Sidebar.tsx new file mode 100644 index 0000000..951ac78 --- /dev/null +++ b/app/components/Sidebar.tsx @@ -0,0 +1,68 @@ +'use client' + +import { useState, useEffect } from 'react' +import { usePathname } from 'next/navigation' +import Link from 'next/link' +import styles from './Sidebar.module.css' + +const navLinks = [ + { href: '/', label: 'Home' }, + { href: '/design-experiments', label: 'Design Experiments' }, + { href: '/docs', label: 'Docs' }, + { href: '/blog', label: 'Blog' }, +] + +export default function Sidebar() { + const [open, setOpen] = useState(false) + const pathname = usePathname() + + useEffect(() => { + setOpen(false) + }, [pathname]) + + useEffect(() => { + if (open) { + document.body.style.overflow = 'hidden' + } else { + document.body.style.overflow = '' + } + return () => { + document.body.style.overflow = '' + } + }, [open]) + + return ( + <> + + +
setOpen(false)} + /> + + + + ) +} diff --git a/app/layout.tsx b/app/layout.tsx index 44e7b85..b3e0ecf 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,7 @@ import { Inter } from 'next/font/google' import './globals.css' import BackButton from './components/BackButton' +import Sidebar from './components/Sidebar' const inter = Inter({ subsets: ['latin'], @@ -22,6 +23,7 @@ export default function RootLayout({ + {children} diff --git a/package-lock.json b/package-lock.json index 826b87f..22b6950 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "gray-matter": "^4.0.3", "lucide-react": "^0.563.0", "next": "^16.1.6", - "next-mdx-remote": "^5.0.0", + "next-mdx-remote": "^6.0.0", "react": "^19.2.4", "react-dom": "^19.2.4", "react-syntax-highlighter": "^16.1.0", @@ -3924,15 +3924,16 @@ } }, "node_modules/next-mdx-remote": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/next-mdx-remote/-/next-mdx-remote-5.0.0.tgz", - "integrity": "sha512-RNNbqRpK9/dcIFZs/esQhuLA8jANqlH694yqoDBK8hkVdJUndzzGmnPHa2nyi90N4Z9VmzuSWNRpr5ItT3M7xQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/next-mdx-remote/-/next-mdx-remote-6.0.0.tgz", + "integrity": "sha512-cJEpEZlgD6xGjB4jL8BnI8FaYdN9BzZM4NwadPe1YQr7pqoWjg9EBCMv3nXBkuHqMRfv2y33SzUsuyNh9LFAQQ==", "license": "MPL-2.0", "dependencies": { "@babel/code-frame": "^7.23.5", "@mdx-js/mdx": "^3.0.1", "@mdx-js/react": "^3.0.1", - "unist-util-remove": "^3.1.0", + "unist-util-remove": "^4.0.0", + "unist-util-visit": "^5.1.0", "vfile": "^6.0.1", "vfile-matter": "^5.0.0" }, @@ -5258,47 +5259,14 @@ } }, "node_modules/unist-util-remove": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-3.1.1.tgz", - "integrity": "sha512-kfCqZK5YVY5yEa89tvpl7KnBBHu2c6CzMkqHUrlOqaRgGOMp0sMvwWOVrbAtj03KhovQB7i96Gda72v/EFE0vw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0", - "unist-util-visit-parents": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/unist-util-remove/node_modules/unist-util-is": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", - "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove/node_modules/unist-util-visit-parents": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", - "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-4.0.0.tgz", + "integrity": "sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg==", "license": "MIT", "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" }, "funding": { "type": "opencollective",