banner and intro fix
This commit is contained in:
@@ -109,6 +109,10 @@
|
||||
3. Base styles — applied to plain HTML elements before any classes hit.
|
||||
--------------------------------------------------------------------------- */
|
||||
@layer base {
|
||||
html {
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
@@ -135,6 +139,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
html[data-ui-ready="0"] body {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
4. Site shell + ambient effects
|
||||
The .site-shell class is on the <main> element in page.tsx. The two
|
||||
|
||||
+27
-2
@@ -62,11 +62,36 @@ export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{ children: React.ReactNode }>) {
|
||||
return (
|
||||
<html lang="en-GB" suppressHydrationWarning>
|
||||
<html lang="en-GB" suppressHydrationWarning data-ui-ready="0">
|
||||
<head>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
(function () {
|
||||
var root = document.documentElement;
|
||||
root.dataset.uiReady = "0";
|
||||
try {
|
||||
var consent = window.localStorage.getItem("novarix-cookie-consent");
|
||||
var introSeen = window.sessionStorage.getItem("novarix-intro-seen");
|
||||
var shouldShowBanner = consent !== "ack" && consent !== "rst";
|
||||
var shouldPlayIntro = !shouldShowBanner && !introSeen;
|
||||
|
||||
root.dataset.showCookieBanner = shouldShowBanner ? "1" : "0";
|
||||
root.dataset.showIntro = shouldPlayIntro ? "1" : "0";
|
||||
} catch (error) {
|
||||
root.dataset.showCookieBanner = "1";
|
||||
root.dataset.showIntro = "0";
|
||||
}
|
||||
root.dataset.uiReady = "1";
|
||||
})();
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
{children}
|
||||
<CookieBanner />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+54
-23
@@ -31,6 +31,8 @@ import { openCookieBanner } from "@/components/CookieBanner";
|
||||
|
||||
// Type for the mouse-pointer position used to move the background glow.
|
||||
type PointerState = { x: number; y: number };
|
||||
const INTRO_SEEN_KEY = "novarix-intro-seen";
|
||||
const INTRO_EVENT = "novarix:start-intro";
|
||||
|
||||
export default function HomePage() {
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -40,43 +42,72 @@ export default function HomePage() {
|
||||
// pointer — the mouse position (in % of page width/height). Used by the
|
||||
// soft glow that follows the cursor in the background.
|
||||
// ---------------------------------------------------------------------------
|
||||
const [showIntro, setShowIntro] = useState(false);
|
||||
const [showIntro, setShowIntro] = useState(() => {
|
||||
if (typeof document === "undefined") return false;
|
||||
return document.documentElement.dataset.showIntro === "1";
|
||||
});
|
||||
const [pointer, setPointer] = useState<PointerState>({ x: 50, y: 22 });
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// INTRO OVERLAY EFFECT
|
||||
// Plays the animated logo + wordmark the first time someone visits the
|
||||
// site in this browser tab. We remember they've seen it using
|
||||
// sessionStorage so it doesn't replay on every navigation. The intro lasts
|
||||
// a little under 4 seconds, with a softer fade into the main page.
|
||||
// site in this browser tab. We decide before hydration whether to play it,
|
||||
// which avoids the homepage flashing briefly before the intro appears.
|
||||
// After the user accepts cookies on their first visit, we trigger the intro
|
||||
// immediately from the banner close action.
|
||||
// ---------------------------------------------------------------------------
|
||||
useEffect(() => {
|
||||
function playIntro() {
|
||||
document.documentElement.dataset.showIntro = "1";
|
||||
setShowIntro(true);
|
||||
try {
|
||||
window.sessionStorage.setItem(INTRO_SEEN_KEY, "true");
|
||||
} catch {
|
||||
/* storage unavailable — silently ignore */
|
||||
}
|
||||
|
||||
const timer = window.setTimeout(() => {
|
||||
document.documentElement.dataset.showIntro = "0";
|
||||
setShowIntro(false);
|
||||
}, 3950);
|
||||
|
||||
return timer;
|
||||
}
|
||||
|
||||
let timer: number | null = null;
|
||||
|
||||
try {
|
||||
const introSeen = window.sessionStorage.getItem("novarix-intro-seen");
|
||||
// Only set the "seen" flag if the user has accepted cookies. Without
|
||||
// consent we still play the intro — we just don't remember it played.
|
||||
const introSeen = window.sessionStorage.getItem(INTRO_SEEN_KEY);
|
||||
const consent = window.localStorage.getItem("novarix-cookie-consent");
|
||||
const shouldPlayNow =
|
||||
document.documentElement.dataset.showIntro === "1" &&
|
||||
consent === "ack" &&
|
||||
!introSeen;
|
||||
|
||||
if (!introSeen) {
|
||||
// Sync once with sessionStorage on first client mount.
|
||||
if (shouldPlayNow) {
|
||||
timer = playIntro();
|
||||
} else {
|
||||
document.documentElement.dataset.showIntro = "0";
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||
setShowIntro(true);
|
||||
if (consent === "ack") {
|
||||
window.sessionStorage.setItem("novarix-intro-seen", "true");
|
||||
}
|
||||
|
||||
// The SVG's built-in stroke + fill animation lasts ~3s.
|
||||
// We hold a little longer so the page underneath can ease in while
|
||||
// the overlay fades away.
|
||||
const timer = window.setTimeout(() => {
|
||||
setShowIntro(false);
|
||||
}, 3950);
|
||||
|
||||
return () => window.clearTimeout(timer);
|
||||
setShowIntro(false);
|
||||
}
|
||||
} catch {
|
||||
/* storage unavailable — silently skip the intro */
|
||||
document.documentElement.dataset.showIntro = "0";
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||
setShowIntro(false);
|
||||
}
|
||||
|
||||
function handleStartIntro() {
|
||||
if (timer !== null) window.clearTimeout(timer);
|
||||
timer = playIntro();
|
||||
}
|
||||
|
||||
window.addEventListener(INTRO_EVENT, handleStartIntro);
|
||||
|
||||
return () => {
|
||||
if (timer !== null) window.clearTimeout(timer);
|
||||
window.removeEventListener(INTRO_EVENT, handleStartIntro);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user