404 Page (#19)
This commit is contained in:
parent
6e5279a3c0
commit
3d7434f9cf
@ -21,12 +21,14 @@ import s from './footer.module.scss'
|
||||
|
||||
export const Footer = () => {
|
||||
const [show, setShow] = useState(true)
|
||||
const [is404, setIs404] = useState(false)
|
||||
const router = useRouter()
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
router?.pathname === '/terms-of-use' ||
|
||||
router?.pathname === '/privacy-policy'
|
||||
router?.pathname === '/privacy-policy' ||
|
||||
router?.pathname === '/404'
|
||||
) {
|
||||
setShow(false)
|
||||
} else {
|
||||
@ -34,6 +36,14 @@ export const Footer = () => {
|
||||
}
|
||||
}, [router?.pathname])
|
||||
|
||||
useEffect(() => {
|
||||
if (router?.pathname === '/404') {
|
||||
setIs404(true)
|
||||
} else {
|
||||
setIs404(false)
|
||||
}
|
||||
}, [router?.pathname])
|
||||
|
||||
return (
|
||||
<>
|
||||
{show && (
|
||||
@ -73,113 +83,116 @@ export const Footer = () => {
|
||||
</Container>
|
||||
</Section>
|
||||
)}
|
||||
<footer className={s.footer}>
|
||||
<Container className={s['container']}>
|
||||
<div className={s['logo']}>
|
||||
<Link variant="unstyled" href="/">
|
||||
<LogoFooter />
|
||||
<span className="sr-only">Laconic</span>
|
||||
</Link>
|
||||
</div>
|
||||
<nav>
|
||||
<ul>
|
||||
{DevelopersLinks.map((link) => {
|
||||
return (
|
||||
<li key={link.title}>
|
||||
<Link href={link.href} variant="nav">
|
||||
{link.title}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
<ul>
|
||||
{ProductsLinks.map((link) => {
|
||||
return (
|
||||
<li key={link.title}>
|
||||
<Link href={link.href} variant="nav">
|
||||
{link.title}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
<ul>
|
||||
{AboutLinks.map((link) => {
|
||||
return (
|
||||
<li key={link.title}>
|
||||
<Link href={link.href} variant="nav">
|
||||
{link.title}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
<ul>
|
||||
{CommunityLinks.map((link) => {
|
||||
return (
|
||||
<li key={link.title}>
|
||||
<Link href={link.href} variant="nav">
|
||||
{link.title}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
<ul className={s['connect__links']}>
|
||||
<li>
|
||||
<Link href="/connect" variant="nav">
|
||||
Connect
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
{ConnectLinks.map((link, index) => {
|
||||
return (
|
||||
<div key={`${link.title}-${index}`}>
|
||||
<Link href={link.href} variant="unstyled">
|
||||
<span className="sr-only">{link.title}</span>
|
||||
{link.logo && link.logo}
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</Container>
|
||||
<Container>
|
||||
<nav className={s['sub__footer']}>
|
||||
<ul className={s['connect__links__mobile']}>
|
||||
<li>
|
||||
{ConnectLinks.map((link, index) => {
|
||||
return (
|
||||
<div key={`${link.title}-${index}`}>
|
||||
<Link href={link.href} variant="unstyled">
|
||||
<span className="sr-only">{link.title}</span>
|
||||
{link.logo && link.logo}
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</li>
|
||||
</ul>
|
||||
<ul className={s['sub__footer__links']}>
|
||||
<li>
|
||||
<Link href="/privacy-policy" variant="nav">
|
||||
Privacy Policy
|
||||
</Link>
|
||||
|
||||
<Link href="/terms-of-use" variant="nav">
|
||||
Terms of Use
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<p>Laconic, The Source of Proof</p>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</Container>
|
||||
</footer>
|
||||
{!is404 && (
|
||||
<footer className={s.footer}>
|
||||
<Container className={s['container']}>
|
||||
<div className={s['logo']}>
|
||||
<Link variant="unstyled" href="/">
|
||||
<LogoFooter />
|
||||
<span className="sr-only">Laconic</span>
|
||||
</Link>
|
||||
</div>
|
||||
<nav>
|
||||
<ul>
|
||||
{DevelopersLinks.map((link) => {
|
||||
return (
|
||||
<li key={link.title}>
|
||||
<Link href={link.href} variant="nav">
|
||||
{link.title}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
<ul>
|
||||
{ProductsLinks.map((link) => {
|
||||
return (
|
||||
<li key={link.title}>
|
||||
<Link href={link.href} variant="nav">
|
||||
{link.title}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
<ul>
|
||||
{AboutLinks.map((link) => {
|
||||
return (
|
||||
<li key={link.title}>
|
||||
<Link href={link.href} variant="nav">
|
||||
{link.title}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
<ul>
|
||||
{CommunityLinks.map((link) => {
|
||||
return (
|
||||
<li key={link.title}>
|
||||
<Link href={link.href} variant="nav">
|
||||
{link.title}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
<ul className={s['connect__links']}>
|
||||
<li>
|
||||
<Link href="/connect" variant="nav">
|
||||
Connect
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
{ConnectLinks.map((link, index) => {
|
||||
return (
|
||||
<div key={`${link.title}-${index}`}>
|
||||
<Link href={link.href} variant="unstyled">
|
||||
<span className="sr-only">{link.title}</span>
|
||||
{link.logo && link.logo}
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</Container>
|
||||
<Container>
|
||||
<nav className={s['sub__footer']}>
|
||||
<ul className={s['connect__links__mobile']}>
|
||||
<li>
|
||||
{ConnectLinks.map((link, index) => {
|
||||
return (
|
||||
<div key={`${link.title}-${index}`}>
|
||||
<Link href={link.href} variant="unstyled">
|
||||
<span className="sr-only">{link.title}</span>
|
||||
{link.logo && link.logo}
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</li>
|
||||
</ul>
|
||||
<ul className={s['sub__footer__links']}>
|
||||
<li>
|
||||
<Link href="/privacy-policy" variant="nav">
|
||||
Privacy Policy
|
||||
</Link>
|
||||
|
||||
<Link href="/terms-of-use" variant="nav">
|
||||
Terms of Use
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<p>Laconic, The Source of Proof</p>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</Container>
|
||||
</footer>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
184
src/components/sections/404/hero/hero.module.scss
Normal file
184
src/components/sections/404/hero/hero.module.scss
Normal file
@ -0,0 +1,184 @@
|
||||
@import '~/css/helpers';
|
||||
|
||||
.section {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: calc(var(--vh) * 100);
|
||||
text-align: center;
|
||||
|
||||
*:not(.gradient) {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
background-image: url('/images/hero-mobile.png');
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50% 15%;
|
||||
background-size: 100%;
|
||||
}
|
||||
|
||||
@supports (aspect-ratio: 16 / 9) {
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
|
||||
&::after,
|
||||
&::before {
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 100%;
|
||||
height: 101%;
|
||||
content: '';
|
||||
transform: translate(-50%, -50%);
|
||||
pointer-events: none;
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
var(--color-black) 15%,
|
||||
rgb(9 9 121 / 0) 50%,
|
||||
var(--color-black) 100%
|
||||
);
|
||||
@supports (aspect-ratio: 16 / 9) {
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
@media screen and (max-width: 800px) {
|
||||
content: initial;
|
||||
}
|
||||
}
|
||||
|
||||
&::before {
|
||||
width: 120%;
|
||||
height: 115%;
|
||||
background: radial-gradient(
|
||||
ellipse farthest-corner at center center,
|
||||
rgb(4 4 4 / 0.25) 55%,
|
||||
var(--color-black) 0
|
||||
);
|
||||
filter: blur(tovw(80px, 'default', 40px));
|
||||
}
|
||||
|
||||
.line {
|
||||
width: tovw(3px, 'default', 2px);
|
||||
height: tovw(72px, 'default', 56px);
|
||||
margin: tovw(37px, 'default', 20px) auto tovw(23px, 'default', 20px) auto;
|
||||
}
|
||||
|
||||
.flag {
|
||||
width: tovw(36px, 'default', 22px);
|
||||
height: tovw(20px, 'default', 20px);
|
||||
margin: 0 auto 0 tovw(8px, 'default', 6px);
|
||||
}
|
||||
|
||||
h1 {
|
||||
@media screen and (max-width: 800px) {
|
||||
font-size: tovw(50px, 'mobile', 71px);
|
||||
margin-top: auto;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: var(--font-dm-mono), sans-serif;
|
||||
font-size: tovw(18px, 'default', 18px);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
a {
|
||||
margin-top: tovw(32px, 'default', 32px);
|
||||
text-decoration: none;
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
width: 100%;
|
||||
margin-top: auto;
|
||||
margin-bottom: tovw(25px, 'mobile');
|
||||
}
|
||||
}
|
||||
|
||||
.scroll {
|
||||
position: absolute;
|
||||
bottom: 5%;
|
||||
left: tovw(56px, 'default', 16px);
|
||||
display: grid;
|
||||
align-items: flex-end;
|
||||
width: calc(100% - (tovw(56px, 'default', 16px) * 2));
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-template-rows: 1fr;
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
p,
|
||||
li {
|
||||
font-family: var(--font-dm-mono);
|
||||
font-size: tovw(12px, 'default', 11px);
|
||||
line-height: 1.7;
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
text-transform: uppercase;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
> div {
|
||||
&:first-of-type {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-self: flex-start;
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
text-align: right;
|
||||
justify-self: flex-end;
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
width: tovw(60px, 'default', 55px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gradient {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: tovw(740px, 'default', 740px);
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgb(0 0 244 / 0) 1.63%,
|
||||
rgb(0 0 244 / 0.9) 63.96%
|
||||
);
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
height: 70%;
|
||||
}
|
||||
}
|
||||
|
||||
.video {
|
||||
position: absolute;
|
||||
z-index: -2;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
69
src/components/sections/404/hero/index.tsx
Normal file
69
src/components/sections/404/hero/index.tsx
Normal file
@ -0,0 +1,69 @@
|
||||
import clsx from 'clsx'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import Flag from '~/components/icons/flag'
|
||||
import Line from '~/components/icons/line'
|
||||
import Section from '~/components/layout/section'
|
||||
import { ButtonLink } from '~/components/primitives/button'
|
||||
import Heading from '~/components/primitives/heading'
|
||||
|
||||
import s from './hero.module.scss'
|
||||
|
||||
const Hero = () => {
|
||||
const [position, setPosition] = useState({ x: 0, y: 0 })
|
||||
|
||||
useEffect(() => {
|
||||
const setFromEvent = (e: { clientX: any; clientY: any }) =>
|
||||
setPosition({ x: e.clientX, y: e.clientY })
|
||||
window.addEventListener('mousemove', setFromEvent)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('mousemove', setFromEvent)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Section className={s['section']}>
|
||||
<video
|
||||
autoPlay
|
||||
className={clsx('hide-on-mobile', s['video'])}
|
||||
controls={false}
|
||||
loop
|
||||
muted
|
||||
preload="true"
|
||||
>
|
||||
<source src="/videos/hero-grid.mp4" type="video/mp4" />
|
||||
</video>
|
||||
<Heading as="h1" variant="xl" centered>
|
||||
Page not found
|
||||
</Heading>
|
||||
<Line className={s['line']} />
|
||||
<p>ERROR 404</p>
|
||||
<ButtonLink href="/" variant="default" size="large">
|
||||
Bring me back home
|
||||
</ButtonLink>
|
||||
<div className={s['scroll']}>
|
||||
<div>
|
||||
<p>C:</p>
|
||||
<Flag className={s['flag']} />
|
||||
</div>
|
||||
<div className="hide-on-mobile">
|
||||
<p>404</p>
|
||||
<ul>
|
||||
<li>
|
||||
H:
|
||||
<span>{position.x} PX</span>
|
||||
</li>
|
||||
<li>
|
||||
V:
|
||||
<span>{position.y} PX</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className={s.gradient} />
|
||||
</Section>
|
||||
)
|
||||
}
|
||||
|
||||
export default Hero
|
||||
16
src/pages/404.tsx
Normal file
16
src/pages/404.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import { Meta } from '~/components/common/meta'
|
||||
import { PageLayout } from '~/components/layout/page'
|
||||
import Hero from '~/components/sections/404/hero'
|
||||
|
||||
import { Page } from './_app'
|
||||
|
||||
const NotFoundPage: Page = () => {
|
||||
return (
|
||||
<PageLayout>
|
||||
<Meta />
|
||||
<Hero />
|
||||
</PageLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export default NotFoundPage
|
||||
Loading…
Reference in New Issue
Block a user