From 68735ea2995a65b2988eac4ad00190a40bf42ac6 Mon Sep 17 00:00:00 2001 From: Satya Date: Thu, 15 Oct 2026 11:43:18 +0800 Subject: [PATCH] Docs: landing, show content before the scripts load The head script set data-dk-js on first paint, which hid every .dk-reveal element (the hero included) until React hydrated and the reveal hook ran. On a slow phone that left the page blank under the header for a few seconds. Content now stays visible until the reveal hook runs. The hook shows what's already on screen and the hero, then turns on the hidden state for the rest. The hero animates in with CSS alone, so it plays on first paint. Co-Authored-By: Claude Opus 5.5 --- docs/components/landing/hooks.js | 26 ++++++++++++++++++-------- docs/components/landing/landing.css | 13 +++++++++++++ docs/pages/_document.tsx | 4 ++-- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/docs/components/landing/hooks.js b/docs/components/landing/hooks.js index febd96f..7570ec3 100644 --- a/docs/components/landing/hooks.js +++ b/docs/components/landing/hooks.js @@ -99,14 +99,21 @@ export function useGithubStars(repo) { return stars; } -/** Fades sections in as they scroll into view. Without JS, everything stays visible. */ +/** + * Fades sections in as they scroll into view. Content stays visible until this runs, so a page whose scripts are + * slow to load is never blank: what's already on screen (and the hero, which animates in with CSS) stays shown, and + * only what's further down is hidden until it scrolls in. + */ export function useReveal() { useEffect(() => { - const els = document.querySelectorAll(".dk-reveal"); - if (typeof IntersectionObserver === "undefined") { - els.forEach((el) => el.classList.add("in")); - return; - } + if (typeof IntersectionObserver === "undefined") return; + const root = document.documentElement; + const later = []; + document.querySelectorAll(".dk-reveal").forEach((el) => { + if (el.closest(".dk-hero") || el.getBoundingClientRect().top < window.innerHeight) el.classList.add("in"); + else later.push(el); + }); + root.setAttribute("data-dk-js", ""); const io = new IntersectionObserver( (entries) => { entries.forEach((e) => { @@ -118,8 +125,11 @@ export function useReveal() { }, { rootMargin: "0px 0px -8% 0px", threshold: 0.08 } ); - els.forEach((el) => io.observe(el)); - return () => io.disconnect(); + later.forEach((el) => io.observe(el)); + return () => { + io.disconnect(); + root.removeAttribute("data-dk-js"); + }; }, []); } diff --git a/docs/components/landing/landing.css b/docs/components/landing/landing.css index 4d08472..edfee6c 100644 --- a/docs/components/landing/landing.css +++ b/docs/components/landing/landing.css @@ -2420,6 +2420,19 @@ html[data-dk-js] .dk-reveal.in { transform: none; } +/* The hero animates in with CSS alone, so it plays on first paint instead of waiting for the scripts */ +@keyframes dk-rise { + from { + opacity: 0; + transform: translateY(24px); + } +} + +.dk-hero .dk-reveal { + animation: dk-rise 0.8s cubic-bezier(0.2, 0.7, 0.2, 1) both; + animation-delay: var(--delay, 0s); +} + /* ---------- Responsive ---------- */ @media (max-width: 1024px) { .dk-hero-grid { diff --git a/docs/pages/_document.tsx b/docs/pages/_document.tsx index 9fc5aad..d5c1f33 100644 --- a/docs/pages/_document.tsx +++ b/docs/pages/_document.tsx @@ -1,8 +1,8 @@ import { Head, Html, Main, NextScript } from 'next/document' // Landing page theme, applied before first paint: the saved theme (next-themes' "theme" key, shared with the -// docs) or the OS preference. data-dk-js lets the landing page's scroll-reveal styles apply. -const landingThemeScript = `(function(){try{var d=document.documentElement;d.setAttribute('data-dk-js','');var t=localStorage.getItem('theme');if(t!=='light'&&t!=='dark'){t=window.matchMedia('(prefers-color-scheme: light)').matches?'light':'dark'}d.setAttribute('data-dk-theme',t)}catch(e){}})();` +// docs) or the OS preference. +const landingThemeScript = `(function(){try{var d=document.documentElement;var t=localStorage.getItem('theme');if(t!=='light'&&t!=='dark'){t=window.matchMedia('(prefers-color-scheme: light)').matches?'light':'dark'}d.setAttribute('data-dk-theme',t)}catch(e){}})();` export default function Document() { return (