RRS First version
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Link, NavLink, Outlet, useLocation } from "react-router-dom";
|
||||
import { company, navigation } from "../data";
|
||||
import { Arrow, WhatsAppAction } from "./Primitives";
|
||||
import "./Layout.css";
|
||||
|
||||
function Brand() {
|
||||
return (
|
||||
<Link className="brand" to="/" aria-label="RRS home">
|
||||
<span className="brand__mark">
|
||||
RRS<span className="brand__period">.</span>
|
||||
</span>
|
||||
<span className="brand__name">
|
||||
Radhe Radhe
|
||||
<br />
|
||||
Synthetics
|
||||
</span>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
function Header() {
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const closeMenu = () => setMenuOpen(false);
|
||||
return (
|
||||
<header
|
||||
className="site-header"
|
||||
onKeyDown={(event) => {
|
||||
if (menuOpen && event.key === "Escape") {
|
||||
closeMenu();
|
||||
document.getElementById("menu-toggle")?.focus();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="site-header__inner">
|
||||
<Brand />
|
||||
<button
|
||||
id="menu-toggle"
|
||||
className="site-header__toggle"
|
||||
type="button"
|
||||
aria-controls="primary-navigation"
|
||||
aria-expanded={menuOpen}
|
||||
onClick={() => setMenuOpen((open) => !open)}
|
||||
>
|
||||
{menuOpen ? "Close" : "Menu"}
|
||||
<span aria-hidden="true">{menuOpen ? "−" : "+"}</span>
|
||||
</button>
|
||||
<nav
|
||||
id="primary-navigation"
|
||||
aria-label="Main navigation"
|
||||
className={`site-nav${menuOpen ? " site-nav--open" : ""}`}
|
||||
>
|
||||
{navigation.map(({ to, label }) => (
|
||||
<NavLink
|
||||
key={to}
|
||||
to={to}
|
||||
end={to === "/"}
|
||||
onClick={closeMenu}
|
||||
className={({ isActive }) =>
|
||||
`site-nav__link${isActive ? " site-nav__link--active" : ""}`
|
||||
}
|
||||
>
|
||||
{label}
|
||||
</NavLink>
|
||||
))}
|
||||
<Link
|
||||
className="site-nav__contact"
|
||||
to="/contact#whatsapp"
|
||||
onClick={closeMenu}
|
||||
>
|
||||
Let's talk <Arrow diagonal />
|
||||
</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
function Footer() {
|
||||
return (
|
||||
<footer className="site-footer">
|
||||
<div className="site-footer__top">
|
||||
<div className="site-footer__intro">
|
||||
<p className="site-footer__name">{company.name}</p>
|
||||
<p className="site-footer__description">{company.description}</p>
|
||||
</div>
|
||||
<nav aria-label="Footer navigation" className="site-footer__nav">
|
||||
<span className="site-footer__label">Explore</span>
|
||||
{navigation.map(({ to, label }) => (
|
||||
<Link className="site-footer__link" to={to} key={to}>
|
||||
{label}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
<div className="site-footer__contact">
|
||||
<span className="site-footer__label">Start a conversation</span>
|
||||
<Link className="site-footer__link" to="/contact#whatsapp">
|
||||
WhatsApp <Arrow diagonal />
|
||||
</Link>
|
||||
<Link className="site-footer__link" to="/contact#direct-contact">
|
||||
Phone · Email · Address <Arrow diagonal />
|
||||
</Link>
|
||||
<p className="site-footer__pending">
|
||||
Contact details to be supplied.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="site-footer__bottom">
|
||||
<span className="site-footer__wordmark" aria-hidden="true">
|
||||
RRS.
|
||||
</span>
|
||||
<p className="site-footer__signature">
|
||||
Built to develop.
|
||||
<br />
|
||||
<em>Built to repeat.</em>
|
||||
</p>
|
||||
</div>
|
||||
<div className="site-footer__legal">
|
||||
<span>
|
||||
© {new Date().getFullYear()} Radhe Radhe Synthetics Pvt. Ltd.
|
||||
</span>
|
||||
<span>Commercial supply: greige fabric only.</span>
|
||||
<a className="site-footer__top-link" href="#top">
|
||||
Back to top ↑
|
||||
</a>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
||||
export function ContactBanner({
|
||||
title = (
|
||||
<>
|
||||
Developing your
|
||||
<br />
|
||||
<em>next construction?</em>
|
||||
</>
|
||||
),
|
||||
text = "Talk to RRS about fabric structure, yarn combinations, bulk requirements or new developments.",
|
||||
}) {
|
||||
return (
|
||||
<section className="contact-banner">
|
||||
<div className="contact-banner__copy">
|
||||
<p className="contact-banner__label">
|
||||
A conversation is where it begins
|
||||
</p>
|
||||
<h2 className="contact-banner__title">{title}</h2>
|
||||
<p className="contact-banner__text">{text}</p>
|
||||
<WhatsAppAction variant="light" />
|
||||
</div>
|
||||
<div className="contact-banner__motif" aria-hidden="true">
|
||||
<span className="contact-banner__loop" />
|
||||
<span className="contact-banner__loop" />
|
||||
<span className="contact-banner__loop" />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Layout() {
|
||||
const { pathname, hash, key } = useLocation();
|
||||
useEffect(() => {
|
||||
const title =
|
||||
navigation.find(({ to }) => to === pathname)?.label ?? "Page not found";
|
||||
document.title = `${title === "Home" ? "RRS" : `${title} — RRS`} | Radhe Radhe Synthetics`;
|
||||
const target = hash
|
||||
? document.getElementById(hash.slice(1))
|
||||
: document.getElementById("main-content");
|
||||
if (hash && target) target.scrollIntoView({ behavior: "instant" });
|
||||
else window.scrollTo({ top: 0, behavior: "instant" });
|
||||
target?.focus({ preventScroll: true });
|
||||
}, [pathname, hash, key]);
|
||||
|
||||
useEffect(() => {
|
||||
const sections = [...document.querySelectorAll("#main-content > section")];
|
||||
const reducedMotion = window.matchMedia(
|
||||
"(prefers-reduced-motion: reduce)",
|
||||
).matches;
|
||||
|
||||
if (reducedMotion || !("IntersectionObserver" in window)) {
|
||||
sections.forEach((section) => section.classList.add("reveal--visible"));
|
||||
return undefined;
|
||||
}
|
||||
|
||||
sections.forEach((section) => section.classList.add("reveal"));
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (!entry.isIntersecting) return;
|
||||
entry.target.classList.add("reveal--visible");
|
||||
observer.unobserve(entry.target);
|
||||
});
|
||||
},
|
||||
{ threshold: 0.08, rootMargin: "0px 0px -8% 0px" },
|
||||
);
|
||||
sections.forEach((section) => observer.observe(section));
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [pathname, key]);
|
||||
|
||||
return (
|
||||
<div className="site-shell" id="top">
|
||||
<a className="skip-link" href="#main-content">
|
||||
Skip to content
|
||||
</a>
|
||||
<Header key={key} />
|
||||
<main className="site-main" id="main-content" tabIndex={-1}>
|
||||
<Outlet />
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user