polishing ui

This commit is contained in:
FernandoJVideira committed 2026-09-12 13:31:50 +01:00
1 parent e5820a4890
commit ec0f73f560
4 files changed
+68 -17

No files matched your search

+8 -3
View File
@@ -5,6 +5,11 @@ import { motion } from "framer-motion";
const Footer = () => { const Footer = () => {
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
// Browsers report region variants (e.g. "en-US", "pt-BR"), not just the
// bare "en"/"pt" i18next is configured with - matching on that exactly
// would pick the wrong sized text class for most real visitors.
const currentLang = i18n.language?.startsWith("pt") ? "pt" : "en";
return ( return (
<footer className="flex flex-col space-y-8 justify-center items-center mt-10 mb-8 px-6 pt-10"> <footer className="flex flex-col space-y-8 justify-center items-center mt-10 mb-8 px-6 pt-10">
<div className="w-full max-w-xs h-px bg-gradient-to-r from-transparent via-white/10 to-transparent" /> <div className="w-full max-w-xs h-px bg-gradient-to-r from-transparent via-white/10 to-transparent" />
@@ -38,7 +43,7 @@ const Footer = () => {
<a <a
href="/tos" href="/tos"
className={ className={
i18n.language === "en" currentLang === "en"
? "footer-text hover:text-gold-light transition-colors" ? "footer-text hover:text-gold-light transition-colors"
: "footer-text-pt hover:text-gold-light transition-colors" : "footer-text-pt hover:text-gold-light transition-colors"
} }
@@ -48,7 +53,7 @@ const Footer = () => {
<a <a
href="/privacy" href="/privacy"
className={ className={
i18n.language === "en" currentLang === "en"
? "footer-text hover:text-gold-light transition-colors" ? "footer-text hover:text-gold-light transition-colors"
: "footer-text-pt hover:text-gold-light transition-colors" : "footer-text-pt hover:text-gold-light transition-colors"
} }
@@ -59,7 +64,7 @@ const Footer = () => {
<p <p
className={ className={
i18n.language === "en" currentLang === "en"
? "footer-text text-center" ? "footer-text text-center"
: "footer-text-pt text-center" : "footer-text-pt text-center"
} }
+50 -9
View File
@@ -31,26 +31,52 @@ const Navbar = () => {
]; ];
useEffect(() => { useEffect(() => {
const handleScroll = () => { // Each section's offset, measured once (and on resize) instead of on
// every scroll tick - getBoundingClientRect() forces a synchronous
// layout recalc, and calling it on every native scroll event (which
// fires far more often than the display's refresh rate during a fast
// swipe or a smooth-scroll animation) is what made scrolling feel
// sluggish: layout was being thrashed dozens of times a second.
let sectionTops: { path: string; top: number }[] = [];
const measureSections = () => {
sectionTops = navigation.map((item) => {
const el = document.getElementById(item.path);
const top = el
? el.getBoundingClientRect().top + window.scrollY
: Infinity;
return { path: item.path, top };
});
};
let ticking = false;
const updateFromScroll = () => {
setColorChange(window.scrollY >= 100); setColorChange(window.scrollY >= 100);
// The active section is whichever nav target's start we've scrolled // The active section is whichever nav target's start we've scrolled
// past most recently - not just "is it in the viewport", since a // past most recently - not just "is it in the viewport", since a
// short section could otherwise never register as active at all. // short section could otherwise never register as active at all.
let current = navigation[0].path; let current = navigation[0].path;
for (const item of navigation) { for (const { path, top } of sectionTops) {
const el = document.getElementById(item.path);
if (!el) continue;
const top = el.getBoundingClientRect().top + window.scrollY;
if (window.scrollY + NAVBAR_HEIGHT + 40 >= top) { if (window.scrollY + NAVBAR_HEIGHT + 40 >= top) {
current = item.path; current = path;
} }
} }
setActiveSection(current); setActiveSection(current);
ticking = false;
}; };
handleScroll(); const handleScroll = () => {
if (ticking) return;
ticking = true;
requestAnimationFrame(updateFromScroll);
};
measureSections();
updateFromScroll();
window.addEventListener("scroll", handleScroll, { passive: true }); window.addEventListener("scroll", handleScroll, { passive: true });
window.addEventListener("resize", measureSections);
const scrollPath = sessionStorage.getItem("scrollPath"); const scrollPath = sessionStorage.getItem("scrollPath");
if (scrollPath) { if (scrollPath) {
@@ -60,6 +86,7 @@ const Navbar = () => {
return () => { return () => {
window.removeEventListener("scroll", handleScroll); window.removeEventListener("scroll", handleScroll);
window.removeEventListener("resize", measureSections);
}; };
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
@@ -109,12 +136,26 @@ const Navbar = () => {
> >
<div className={colorChange ? "px-4" : ""}> <div className={colorChange ? "px-4" : ""}>
<div <div
className={`relative mx-auto flex items-center justify-between transition-all duration-500 ${ className={`relative mx-auto flex items-center justify-between transition-[max-width,margin,padding] duration-500 ${
colorChange colorChange
? "mt-3 max-w-3xl rounded-full glass-nav shadow-glass-lg px-5 py-2.5" ? "mt-3 max-w-3xl px-5 py-2.5"
: "max-w-screen-xl px-4 py-3 md:px-8 md:py-5" : "max-w-screen-xl px-4 py-3 md:px-8 md:py-5"
}`} }`}
> >
{/*
The glass material is its own layer, separate from the box
above whose size/margin is animating. backdrop-filter can't be
smoothly interpolated between "none" and "blur(20px)" - toggling
it on the same element that's also resizing is what produced a
seam along the bottom edge mid-transition. Here it's always
present and only crossfades via opacity, which is safe.
*/}
<div
className={`absolute inset-0 -z-10 rounded-full glass-nav shadow-glass-lg transition-opacity duration-500 ${
colorChange ? "opacity-100" : "opacity-0"
}`}
/>
<motion.a <motion.a
href="Top" href="Top"
whileTap={{ scale: 0.92 }} whileTap={{ scale: 0.92 }}
+9 -4
View File
@@ -7,6 +7,11 @@ const Hero = () => {
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
const prefersReducedMotion = useReducedMotion(); const prefersReducedMotion = useReducedMotion();
// Browsers report region variants (e.g. "en-US", "pt-BR"), not just the
// bare "en"/"pt" i18next is configured with - matching on that exactly
// would pick the wrong sized text class for most real visitors.
const currentLang = i18n.language?.startsWith("pt") ? "pt" : "en";
const handleNavigation = (path: string) => { const handleNavigation = (path: string) => {
const element = document.getElementById(path); const element = document.getElementById(path);
const navbarHeight = 110; const navbarHeight = 110;
@@ -51,13 +56,13 @@ const Hero = () => {
/> />
<div> <div>
<motion.h1 <motion.h1
className={i18n.language === "en" ? "hero-text" : "hero-text-pt"} className={currentLang === "en" ? "hero-text" : "hero-text-pt"}
{...rise(0.05)} {...rise(0.05)}
> >
{t("hero.heading.part1")} {t("hero.heading.part1")}
</motion.h1> </motion.h1>
<motion.h1 <motion.h1
className={i18n.language === "en" ? "hero-text" : "hero-text-pt"} className={currentLang === "en" ? "hero-text" : "hero-text-pt"}
{...rise(0.1)} {...rise(0.1)}
> >
<span className="gold-text">{t("hero.heading.part2")}</span> <span className="gold-text">{t("hero.heading.part2")}</span>
@@ -69,7 +74,7 @@ const Hero = () => {
<motion.div className="mx-5 lg:mx-0" {...rise(0.15)}> <motion.div className="mx-5 lg:mx-0" {...rise(0.15)}>
<h2 <h2
className={ className={
i18n.language === "en" currentLang === "en"
? "hero-sub-text mt-4 md:mt-10" ? "hero-sub-text mt-4 md:mt-10"
: "hero-sub-text-pt mt-4 md:mt-10" : "hero-sub-text-pt mt-4 md:mt-10"
} }
@@ -78,7 +83,7 @@ const Hero = () => {
</h2> </h2>
<h2 <h2
className={ className={
i18n.language === "en" ? "hero-sub-text" : "hero-sub-text-pt" currentLang === "en" ? "hero-sub-text" : "hero-sub-text-pt"
} }
> >
{t("hero.subheading.part2")} {t("hero.subheading.part2")}
+1 -1
View File
@@ -121,7 +121,7 @@ body {
background: rgba(10, 10, 13, 0.55); background: rgba(10, 10, 13, 0.55);
backdrop-filter: blur(20px) saturate(180%); backdrop-filter: blur(20px) saturate(180%);
-webkit-backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%);
border-bottom: 1px solid rgba(250, 204, 151, 0.1); border: 1px solid rgba(250, 204, 151, 0.1);
} }
/* Heavier material for full takeover surfaces (mobile menu sheet) — a /* Heavier material for full takeover surfaces (mobile menu sheet) — a