diff --git a/src/components/common/footer/footer.module.scss b/src/components/common/footer/footer.module.scss index 9887fb0..46f7e88 100644 --- a/src/components/common/footer/footer.module.scss +++ b/src/components/common/footer/footer.module.scss @@ -30,15 +30,15 @@ top: 50%; left: 50%; width: 100%; - height: 100%; + height: 101%; content: ''; transform: translate(-50%, -50%); pointer-events: none; background: linear-gradient( 0deg, - black 15%, + var(--color-black) 15%, rgb(9 9 121 / 0) 50%, - black 100% + var(--color-black) 100% ); @media screen and (max-width: 800px) { content: normal; @@ -51,7 +51,7 @@ background: radial-gradient( ellipse farthest-corner at center center, rgb(4 4 4 / 0.05) 45%, - #000 0 + var(--color-black) 0 ); filter: blur(tovw(80px, 'default', 40px)); } diff --git a/src/components/common/header/header.module.scss b/src/components/common/header/header.module.scss index 2e5141f..94105c3 100644 --- a/src/components/common/header/header.module.scss +++ b/src/components/common/header/header.module.scss @@ -2,7 +2,7 @@ .header { position: fixed; - z-index: 10; + z-index: 20; display: flex; align-items: center; justify-content: space-between; @@ -25,10 +25,123 @@ margin: 0 0 0 tovw(122px, 'default', 90px); padding: 0; list-style-type: none; - gap: tovw(32px, 'default', 20px); + gap: tovw(32px, 'default', 16px); + } + + li:not(.item--mobile) { + position: relative; + + &::after { + position: absolute; + top: 50%; + left: tovw(-16px, 'default', -16px); + width: tovw(7px, 'default', 7px); + height: tovw(7px, 'default', 7px); + content: ''; + transition: opacity var(--normal-transition); + transform: translateY(-50%); + opacity: 0; + background: var(--color-white); + } + } + + li.active { + &::after { + opacity: 1; + } } .burger { - width: tovw(22px, 'default', 22px); + width: tovw(28px, 'default', 28px); + } + + &__mobile { + position: fixed; + z-index: 10; + top: tovw(75px, 'mobile'); + right: 0; + bottom: 0; + left: 0; + width: 100%; + height: calc(100 * var(--vh) - tovw(75px, 'mobile')); + padding: tovw(16px, 'mobile'); + background-color: var(--color-black); + background-image: linear-gradient( + 180deg, + rgb(0 0 244 / 0) 1.63%, + rgb(0 0 244 / 0.9) 81.47% + ); + + ul { + flex-direction: column; + margin: 0; + padding: 0; + } + + a { + font-size: tovw(50px, 'mobile'); + letter-spacing: tovw(-1px, 'mobile'); + opacity: 0.6; + + @media screen and (max-height: 750px) { + font-size: calc(var(--vh) * 5.2); + } + } + + li.active { + a { + opacity: 1; + } + } + + svg { + display: block; + } + + button { + width: 100%; + margin: tovw(59px, 'mobile') 0 tovw(44px, 'mobile') 0; + + @media screen and (max-height: 750px) { + margin: calc(var(--vh) * 7.2) 0 calc(var(--vh) * 5.2) 0; + } + } + + p { + text-align: center; + } + + .social { + position: relative; + display: grid; + justify-content: center; + width: 100%; + margin: tovw(32px, 'mobile') 0; + padding-top: tovw(32px, 'mobile'); + grid-template-columns: repeat(6, tovw(24px, 'mobile')); + gap: tovw(24px, 'mobile'); + + &::after { + position: absolute; + top: tovw(-32px, 'mobile'); + left: tovw(-16px, 'mobile'); + width: calc(100% + tovw(32px, 'mobile')); + height: tovw(1px, 'mobile', 1px); + margin: tovw(24px, 'mobile') 0; + content: ''; + background: white; + } + + li, + a { + display: block; + width: tovw(24px, 'default', 24px); + height: tovw(24px, 'default', 24px); + } + + a { + opacity: 1; + } + } } } diff --git a/src/components/common/header/index.tsx b/src/components/common/header/index.tsx index 317d1c5..8977e45 100644 --- a/src/components/common/header/index.tsx +++ b/src/components/common/header/index.tsx @@ -1,15 +1,103 @@ import clsx from 'clsx' import NextLink from 'next/link' +import { useRouter } from 'next/router' +import * as React from 'react' import Burger from '~/components/icons/burguer' import { Logo } from '~/components/icons/logo' import { Button } from '~/components/primitives/button' import Link from '~/components/primitives/link' +import { useIsomorphicLayoutEffect } from '~/hooks/use-isomorphic-layout-effect' +import { DURATION, gsap } from '~/lib/gsap' +import { ConnectLinks } from '../footer/footer' import { defaultHeaderLinks } from './header' import s from './header.module.scss' +const HeaderMobile = React.forwardRef< + HTMLDivElement, + JSX.IntrinsicElements['div'] +>(({ className, ...props }, ref) => { + const router = useRouter() + + const isActive = (href: string) => router.pathname === href + return ( +
+ + + +

Laconic, The Source of Proof

+
+ ) +}) + export const Header = () => { + const [isOpen, setIsOpen] = React.useState(false) + const headerMobileRef = React.useRef(null) + const timelineRef = React.useRef() + const router = useRouter() + + const isActive = (href: string) => router.pathname === href + + useIsomorphicLayoutEffect(() => { + if (!headerMobileRef.current) return + + const navigationItems = + headerMobileRef.current.querySelectorAll('.items li') + const socialItems = headerMobileRef.current.querySelectorAll('.social li') + const button = headerMobileRef.current.querySelector('button') + const text = headerMobileRef.current.querySelector('p') + + timelineRef.current = gsap.timeline({ + paused: true, + smoothChildTiming: true + }) + timelineRef.current.fromTo( + headerMobileRef.current, + { + xPercent: 100 + }, + { xPercent: 0, duration: DURATION } + ) + timelineRef.current.fadeIn([navigationItems, button, socialItems, text]), + '>-40%' + }, []) + + useIsomorphicLayoutEffect(() => { + if (isOpen) { + gsap.set('body, html', { overflowY: 'hidden' }) + timelineRef.current?.timeScale(1).play() + } else { + gsap.set('body, html', { overflowY: 'auto' }) + timelineRef.current?.timeScale(1.5).reverse() + } + }, [isOpen]) + return (
) } diff --git a/src/components/icons/burguer.tsx b/src/components/icons/burguer.tsx index f955798..ba06cf5 100644 --- a/src/components/icons/burguer.tsx +++ b/src/components/icons/burguer.tsx @@ -1,16 +1,95 @@ -const Burger = ({ className, fill }: { className?: string; fill?: string }) => { +import * as React from 'react' + +import { useIsomorphicLayoutEffect } from '~/hooks/use-isomorphic-layout-effect' +import { gsap } from '~/lib/gsap' + +const Burger = ({ + className, + fill, + isOpen +}: { + className?: string + fill?: string + isOpen: boolean +}) => { + const timelineRef = React.useRef() + const topRef = React.useRef(null) + const middleRef = React.useRef(null) + const bottomRef = React.useRef(null) + + useIsomorphicLayoutEffect(() => { + timelineRef.current = gsap.timeline({ + paused: true, + reversed: true, + smoothChildTiming: true + }) + + timelineRef.current.to(topRef.current, { + y: -4, + transformOrigin: '50% 50%' + }) + timelineRef.current.to( + bottomRef.current, + { + y: 4, + transformOrigin: '50% 50%' + }, + '<' + ) + timelineRef.current.to( + middleRef.current, + { + scale: 0.1, + transformOrigin: '50% 50%' + }, + '<' + ) + timelineRef.current.add('rotate') + timelineRef.current.to(topRef.current, { y: 6 }, 'rotate') + timelineRef.current.to(bottomRef.current, { y: -6 }, 'rotate') + timelineRef.current.to( + topRef.current, + { rotationZ: 45, transformOrigin: '50% 50%' }, + 'rotate' + ) + timelineRef.current.to( + bottomRef.current, + { rotationZ: -45, transformOrigin: '50% 50%' }, + 'rotate' + ) + + timelineRef.current.timeScale(2.23) + + return () => { + timelineRef.current?.kill() + } + }, []) + + useIsomorphicLayoutEffect(() => { + if (isOpen) { + timelineRef.current?.play() + } else { + timelineRef.current?.reverse() + } + }, [isOpen]) + return ( - + + ) } diff --git a/src/components/icons/socials.tsx b/src/components/icons/socials.tsx index 447bbc3..f193386 100644 --- a/src/components/icons/socials.tsx +++ b/src/components/icons/socials.tsx @@ -1,3 +1,5 @@ +import { useReactId } from '~/hooks/use-react-id' + const Telegram = ({ className, fill @@ -5,6 +7,7 @@ const Telegram = ({ className?: string fill?: string }) => { + const id = useReactId() return ( - + @@ -42,6 +45,7 @@ const Twitter = ({ className?: string fill?: string }) => { + const id = useReactId() return ( - + @@ -71,6 +75,7 @@ const Twitter = ({ } const Reddit = ({ className, fill }: { className?: string; fill?: string }) => { + const id = useReactId() return ( { fillRule="evenodd" clipRule="evenodd" d="M24 12c0 6.6274-5.3726 12-12 12-6.62742 0-12-5.3726-12-12C0 5.37258 5.37258 0 12 0c6.6274 0 12 5.37258 12 12Zm-5.7544-1.7544C19.214 10.2456 20 11.0316 20 12c0 .7158-.4351 1.3333-1.0105 1.614.0281.1684.0421.3369.0421.5193 0 2.6947-3.1298 4.8702-7.0035 4.8702-3.87371 0-7.00353-2.1755-7.00353-4.8702 0-.1824.01403-.3649.0421-.5333-.61754-.2807-1.03859-.8842-1.03859-1.6 0-.9684.78596-1.7544 1.75438-1.7544.46316 0 .89825.1965 1.20702.4912 1.20702-.88419 2.87719-1.43156 4.74382-1.4877l.8843-4.18246c.028-.08421.0701-.15438.1403-.19649.0702-.0421.1544-.05614.2386-.0421l2.9053.61754c.1965-.42105.6175-.70175 1.1087-.70175.6878 0 1.2492.5614 1.2492 1.24912s-.5614 1.24912-1.2492 1.24912c-.6736 0-1.221-.53333-1.2491-1.19298l-2.5965-.54737-.8 3.74737c1.8246.07018 3.4807.63158 4.6737 1.4877.3088-.3088.7298-.4912 1.207-.4912ZM9.24913 12c-.68772 0-1.24912.5614-1.24912 1.2491 0 .6877.5614 1.2491 1.24912 1.2491s1.24917-.5614 1.24917-1.2491c0-.6877-.56145-1.2491-1.24917-1.2491Zm2.76487 5.4596c.4772 0 2.1053-.0561 2.9614-.9123.1264-.1263.1264-.3228.0281-.4631-.1263-.1263-.3368-.1263-.4631 0-.5474.5333-1.6843.7298-2.5123.7298-.8281 0-1.979-.1965-2.5123-.7298-.12632-.1263-.33685-.1263-.46316 0-.12632.1263-.12632.3368 0 .4631.8421.8422 2.48416.9123 2.96136.9123Zm1.4878-4.2105c0 .6877.5614 1.2491 1.2491 1.2491.6877 0 1.2491-.5614 1.2491-1.2491C16 12.5614 15.4386 12 14.7509 12c-.6877 0-1.2491.5614-1.2491 1.2491Z" - fill="url(#reddit)" + fill={`url(#${id})`} /> { gradientUnits="userSpaceOnUse" > - + @@ -132,6 +137,8 @@ const Facebook = ({ className?: string fill?: string }) => { + const id = useReactId() + return ( - + @@ -167,6 +174,7 @@ const Instagram = ({ className?: string fill?: string }) => { + const id = useReactId() return ( - + - + - + diff --git a/src/components/primitives/button/button.module.scss b/src/components/primitives/button/button.module.scss index 78bf377..2320477 100644 --- a/src/components/primitives/button/button.module.scss +++ b/src/components/primitives/button/button.module.scss @@ -39,4 +39,15 @@ &--small { padding: tovw(11.5px, 'default', 11.5px) tovw(24px, 'default', 10px); } + + &--unstyled { + border: none; + padding: tovw(12px, 'default', 12px); + background: none; + + &:hover { + background: none; + color: currentcolor; + } + } } diff --git a/src/components/primitives/button/index.tsx b/src/components/primitives/button/index.tsx index 198d9f9..dc4a8c5 100644 --- a/src/components/primitives/button/index.tsx +++ b/src/components/primitives/button/index.tsx @@ -9,7 +9,7 @@ import s from './button.module.scss' export type ButtonProps = JSX.IntrinsicElements['button'] & { size?: 'small' | 'medium' | 'large' - variant?: 'default' | 'primary' + variant?: 'default' | 'primary' | 'unstyled' } export const Button = React.forwardRef( diff --git a/src/components/sections/homepage/hero/hero.module.scss b/src/components/sections/homepage/hero/hero.module.scss index 72e90a4..6665359 100644 --- a/src/components/sections/homepage/hero/hero.module.scss +++ b/src/components/sections/homepage/hero/hero.module.scss @@ -27,15 +27,15 @@ top: 50%; left: 50%; width: 100%; - height: 100%; + height: 101%; content: ''; transform: translate(-50%, -50%); pointer-events: none; background: linear-gradient( 0deg, - black 15%, + var(--color-black) 15%, rgb(9 9 121 / 0) 50%, - black 100% + var(--color-black) 100% ); @supports (aspect-ratio: 16 / 9) { aspect-ratio: 16 / 9; @@ -51,7 +51,7 @@ background: radial-gradient( ellipse farthest-corner at center center, rgb(4 4 4 / 0.25) 45%, - #000 0 + var(--color-black) 0 ); filter: blur(tovw(80px, 'default', 40px)); } diff --git a/src/css/global.scss b/src/css/global.scss index 7a3e145..b6b2d63 100644 --- a/src/css/global.scss +++ b/src/css/global.scss @@ -18,6 +18,7 @@ --color-black: #040404; --color-white: #fbfbfb; --color-grey-light: #8e8e8e; + --color-grey-lightness: #dedede; // Duration --duration-normal: 0.525s;