diff --git a/src/static/header/header.module.css b/src/static/header/header.module.css index 150df10..51faefc 100644 --- a/src/static/header/header.module.css +++ b/src/static/header/header.module.css @@ -3,25 +3,33 @@ ========================= */ .header { - position: sticky; + position: fixed; top: 0; + left: 0; + right: 0; display: flex; justify-content: center; - z-index: 1000; + z-index: 1001; width: 100%; height: 90px; color: var(--color-text); - background: rgba(0, 0, 0, 0.85); + background: rgba(0, 0, 0, 0.82); - backdrop-filter: blur(14px); - -webkit-backdrop-filter: blur(14px); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); border-bottom: 1px solid var(--color-line); } +.headerSpacer { + width: 100%; + height: 90px; + flex-shrink: 0; +} + /* ========================= LOGO ========================= */ @@ -61,21 +69,21 @@ } /* ========================= - LANG TOGGLE + LANG TOGGLE (Header) ========================= */ .langToggle { display: inline-flex; align-items: center; justify-content: center; - gap: 4px; - height: 32px; - padding: 0 12px; + gap: 5px; + height: 34px; + padding: 0 14px; background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.12); border-radius: 9999px; color: var(--color-text); - font-size: 11.5px; + font-size: 12px; font-weight: 600; letter-spacing: 0.04em; text-decoration: none; @@ -92,20 +100,12 @@ } .langToggleDot { - width: 5px; - height: 5px; + width: 6px; + height: 6px; border-radius: 50%; background: #3b82f6; } -.mobileLangRow { - display: flex; - align-items: center; - margin-top: var(--space-lg); - padding-top: var(--space-md); - border-top: 1px solid var(--color-line); -} - /* ========================= DESKTOP NAV ========================= */ @@ -113,12 +113,9 @@ .desktopNav ul { display: flex; align-items: center; - gap: var(--space-lg); - margin: 0; padding: 0; - list-style: none; } @@ -129,34 +126,24 @@ .desktopNav a { position: relative; - display: inline-block; - padding: 8px 0; - color: var(--color-muted); - font-size: var(--fs-nav); font-weight: 500; letter-spacing: -0.01em; - text-decoration: none; - transition: color 0.25s ease; } .desktopNav a::after { content: ""; - position: absolute; left: 0; bottom: 2px; - width: 0; height: 1px; - background: var(--color-text); - transition: width 0.3s ease; } @@ -169,129 +156,325 @@ } /* ========================= - HAMBURGER + HAMBURGER BUTTON ========================= */ .hamburger { display: none; - - position: relative; - - width: 34px; - height: 24px; - + align-items: center; + justify-content: center; + width: 42px; + height: 42px; margin: 0; padding: 0; - - background: transparent; - border: 0; - + background: rgba(255, 255, 255, 0.05); + border: 1px solid rgba(255, 255, 255, 0.12); + border-radius: 12px; cursor: pointer; + transition: background 0.2s ease, border-color 0.2s ease; + -webkit-tap-highlight-color: transparent; } -.hamburger span { +.hamburger:hover { + background: rgba(255, 255, 255, 0.08); + border-color: rgba(255, 255, 255, 0.2); +} + +.hamburger.active { + background: rgba(255, 255, 255, 0.1); + border-color: rgba(255, 255, 255, 0.25); +} + +.hamburgerBox { + position: relative; + width: 20px; + height: 14px; +} + +.hamburgerLine { position: absolute; left: 0; - width: 100%; height: 2px; - - border-radius: 10px; - + border-radius: 2px; background: var(--color-text); - - transition: - transform 0.3s ease, - opacity 0.3s ease; + transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), + opacity 0.2s ease; } -.hamburger span:nth-child(1) { +.hamburgerLine:nth-child(1) { top: 0; } -.hamburger span:nth-child(2) { - top: 50%; - transform: translateY(-50%); +.hamburgerLine:nth-child(2) { + top: 6px; } -.hamburger span:nth-child(3) { - bottom: 0; +.hamburgerLine:nth-child(3) { + top: 12px; } -.hamburger.active span:nth-child(1) { - transform: translateY(11px) rotate(45deg); +.hamburger.active .hamburgerLine:nth-child(1) { + transform: translateY(6px) rotate(45deg); } -.hamburger.active span:nth-child(2) { +.hamburger.active .hamburgerLine:nth-child(2) { opacity: 0; - transform: translateX(10px); + transform: scaleX(0); } -.hamburger.active span:nth-child(3) { - transform: translateY(-11px) rotate(-45deg); +.hamburger.active .hamburgerLine:nth-child(3) { + transform: translateY(-6px) rotate(-45deg); } /* ========================= - MOBILE MENU + MOBILE BACKDROP +========================= */ + +.mobileBackdrop { + display: none; +} + +@media (max-width: 768px) { + .mobileBackdrop { + display: block; + position: fixed; + inset: 0; + z-index: 998; + background: rgba(0, 0, 0, 0.7); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + opacity: 0; + visibility: hidden; + pointer-events: none; + transition: opacity 0.35s ease, visibility 0.35s ease; + } + + .mobileBackdrop.active { + opacity: 1; + visibility: visible; + pointer-events: auto; + } +} + +/* ========================= + MOBILE MENU DRAWER ========================= */ .mobileMenu { - position: fixed; - top: 90px; - right: 0; - - width: 100%; - height: calc(100vh - 90px); - - padding: var(--space-2xl) var(--container-padding); - - background: var(--color-bg); - - transform: translateX(100%); - - transition: transform 0.45s cubic-bezier(0.32, 0.72, 0, 1); - - overflow-y: auto; + display: none; } -.mobileMenu.active { - transform: translateX(0); -} +@media (max-width: 768px) { + .mobileMenu { + display: flex; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100dvh; + z-index: 999; -.mobileMenu ul { - display: flex; - flex-direction: column; + background: rgba(8, 8, 8, 0.94); + backdrop-filter: blur(28px); + -webkit-backdrop-filter: blur(28px); - gap: var(--space-md); + opacity: 0; + visibility: hidden; + pointer-events: none; + transform: translateY(-10px); - margin: 0; - padding: 0; + transition: opacity 0.35s cubic-bezier(0.16, 1, 0.3, 1), + transform 0.35s cubic-bezier(0.16, 1, 0.3, 1), + visibility 0.35s ease; - list-style: none; -} + overflow-y: auto; + -webkit-overflow-scrolling: touch; + } -.mobileMenu li { - margin: 0; - padding: 0; -} + .mobileMenu.active { + opacity: 1; + visibility: visible; + pointer-events: auto; + transform: translateY(0); + } -.mobileMenu a { - display: inline-block; + .mobileMenuInner { + display: flex; + flex-direction: column; + justify-content: space-between; + width: 100%; + min-height: 100%; + padding: calc(70px + var(--space-md)) var(--container-padding) max(var(--space-lg), env(safe-area-inset-bottom, 24px)); + box-sizing: border-box; + } - color: var(--color-text); + /* Nav items */ + .mobileNavList { + display: flex; + flex-direction: column; + margin: 0; + padding: 0; + list-style: none; + } - font-size: var(--fs-display); - font-weight: 600; - line-height: 1; - letter-spacing: -0.04em; + .mobileNavItem { + margin: 0; + padding: 0; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); + opacity: 0; + transform: translateY(14px); + transition: opacity 0.4s cubic-bezier(0.16, 1, 0.3, 1), + transform 0.4s cubic-bezier(0.16, 1, 0.3, 1); + } - text-decoration: none; + .mobileMenu.active .mobileNavItem { + opacity: 1; + transform: translateY(0); + } - transition: color 0.25s ease; -} + .mobileNavLink { + display: flex; + align-items: center; + gap: 16px; + padding: 20px 4px; + text-decoration: none; + color: var(--color-text); + transition: color 0.2s ease, transform 0.2s ease; + -webkit-tap-highlight-color: transparent; + } -.mobileMenu a:hover { - color: var(--color-muted); + .mobileNavLink:active { + opacity: 0.7; + transform: translateX(4px); + } + + .mobileNavNum { + font-size: 13px; + font-weight: 600; + color: var(--color-faint); + letter-spacing: 0.05em; + } + + .mobileNavText { + font-size: clamp(24px, 6vw, 32px); + font-weight: 700; + letter-spacing: -0.04em; + line-height: 1.1; + } + + .mobileNavArrow { + margin-left: auto; + font-size: 22px; + color: var(--color-faint); + transition: transform 0.2s ease, color 0.2s ease; + } + + .mobileNavLink:hover .mobileNavArrow, + .mobileNavLink:active .mobileNavArrow { + color: #93c5fd; + transform: translate(2px, -2px); + } + + /* Extra / Action section */ + .mobileExtra { + display: flex; + flex-direction: column; + gap: var(--space-md); + margin-top: var(--space-xl); + padding-top: var(--space-md); + opacity: 0; + transform: translateY(14px); + transition: opacity 0.4s cubic-bezier(0.16, 1, 0.3, 1), + transform 0.4s cubic-bezier(0.16, 1, 0.3, 1); + } + + .mobileMenu.active .mobileExtra { + opacity: 1; + transform: translateY(0); + } + + .mobileActionGroup { + display: flex; + flex-direction: column; + gap: 10px; + } + + .mobileResumeBtn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 10px; + height: 48px; + padding: 0 20px; + background: #ffffff; + color: #000000; + border-radius: 12px; + font-size: 14px; + font-weight: 600; + text-decoration: none; + transition: transform 0.2s ease, background 0.2s ease; + -webkit-tap-highlight-color: transparent; + } + + .mobileResumeBtn:active { + transform: scale(0.98); + background: #e2e8f0; + } + + .mobileLangBtn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + height: 44px; + padding: 0 16px; + background: rgba(255, 255, 255, 0.05); + border: 1px solid rgba(255, 255, 255, 0.12); + border-radius: 12px; + color: var(--color-text); + font-size: 13px; + font-weight: 600; + text-decoration: none; + transition: background 0.2s ease, border-color 0.2s ease; + -webkit-tap-highlight-color: transparent; + } + + .mobileLangBtn:active { + background: rgba(37, 99, 235, 0.15); + border-color: rgba(59, 130, 246, 0.4); + color: #93c5fd; + } + + .mobileSocials { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding-top: 12px; + } + + .mobileSocialLink { + flex: 1; + display: inline-flex; + align-items: center; + justify-content: center; + height: 38px; + background: rgba(255, 255, 255, 0.03); + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 8px; + color: var(--color-muted); + font-size: 12px; + font-weight: 500; + text-decoration: none; + transition: all 0.2s ease; + -webkit-tap-highlight-color: transparent; + } + + .mobileSocialLink:active { + color: #fff; + background: rgba(255, 255, 255, 0.08); + } } /* ========================= @@ -299,7 +482,8 @@ ========================= */ @media (min-width: 1600px) { - .header { + .header, + .headerSpacer { height: 110px; } } @@ -309,7 +493,8 @@ ========================= */ @media (max-width: 1024px) { - .header { + .header, + .headerSpacer { height: 80px; } @@ -323,7 +508,8 @@ ========================= */ @media (max-width: 768px) { - .header { + .header, + .headerSpacer { height: 70px; } @@ -332,12 +518,7 @@ } .hamburger { - display: block; - } - - .mobileMenu { - top: 70px; - height: calc(100vh - 70px); + display: inline-flex; } } @@ -346,23 +527,16 @@ ========================= */ @media (max-width: 480px) { - .header { + .header, + .headerSpacer { height: 64px; } .logo { - font-size: 25px; + font-size: 24px; } - .hamburger { - width: 29px; - height: 21px; - } - - .mobileMenu { - top: 64px; - height: calc(100vh - 64px); - - padding-top: var(--space-lg); + .mobileMenuInner { + padding-top: calc(64px + var(--space-sm)); } } \ No newline at end of file diff --git a/src/widgets/header/ui/Header.tsx b/src/widgets/header/ui/Header.tsx index e10a0ab..1f7a65c 100644 --- a/src/widgets/header/ui/Header.tsx +++ b/src/widgets/header/ui/Header.tsx @@ -30,10 +30,16 @@ export const Header = () => { switchHref = `${switchHref}/`; } + const resumeHref = + locale === "en" + ? "/Nikita_Egorov_Resume_EN.pdf" + : "/Nikita_Egorov_Resume_RU.pdf"; + const resumeLabel = locale === "en" ? "Resume (PDF)" : "Резюме (PDF)"; + const navItems = [ - { label: dict.nav.about, href: `/${locale}/#about` }, - { label: dict.nav.projects, href: `/${locale}/projects/` }, - { label: dict.nav.contact, href: `/${locale}/#contact` }, + { num: "01", label: dict.nav.about, href: `/${locale}/#about` }, + { num: "02", label: dict.nav.projects, href: `/${locale}/projects/` }, + { num: "03", label: dict.nav.contact, href: `/${locale}/#contact` }, ]; useEffect(() => { @@ -45,13 +51,20 @@ export const Header = () => { if (window.innerWidth > 768) setOpen(false); }; - document.documentElement.classList.toggle("no-scroll", open); + if (open) { + document.documentElement.classList.add("no-scroll"); + document.body.classList.add("no-scroll"); + } else { + document.documentElement.classList.remove("no-scroll"); + document.body.classList.remove("no-scroll"); + } window.addEventListener("keydown", onKeyDown); window.addEventListener("resize", onResize); return () => { document.documentElement.classList.remove("no-scroll"); + document.body.classList.remove("no-scroll"); window.removeEventListener("keydown", onKeyDown); window.removeEventListener("resize", onResize); }; @@ -60,78 +73,161 @@ export const Header = () => { const close = () => setOpen(false); return ( -
-
-
- - Autli - + <> +
+
+
+ + Autli + +
+ +
+ + + {/* Language Switcher */} + +
+
-
- + {/* Spacer to preserve normal document flow with fixed header */} + -
+ {/* Mobile Drawer Navigation */} +
+ ); };