Skip to content

Commit de3517e

Browse files
xabierlameiroByteEVMcolinhacks
authored
fix(docs): prevent FOUC on website homepage logo (#4716)
* fix(docs): prevent FOUC on website homepage logo - Add HeroLogo component with loading states and skeleton placeholder - Implement image preloading in layout for critical resources - Add CSS optimizations to prevent flash of unstyled content - Replace direct img tags with optimized Next.js Image component - Ensures consistent 170px height during logo loading Fixes #4606 * style: fix formatting in hero-logo component * fix: remove unused ReactNode import to resolve lint error * fix: apply Biome formatting to hero-logo component - Fixed import order in hero-logo.tsx to comply with project linting rules - Ran 'pnpm format' to auto-fix formatting issues * refactor: use size-20 instead of w-20 h-20 for better Tailwind CSS optimization - Apply code review feedback to minify sizing classes - Use semantic size-* utility instead of separate width/height classes * fix(docs): resolve FOUC issue with Zod logo - Optimize HeroLogo component with smooth fade-in transitions - Remove skeleton placeholder for cleaner UX experience - Add loading state management with React useState - Set priority image loading to prevent flash - Clean up global CSS by removing unnecessary FOUC styles - Maintain fixed 170px height to prevent layout shifts - Increase transition duration to 500ms for smoother effect * fix(docs): prevent layout shift in aside logo - Create SidebarLogo component with Next.js Image optimization - Add proper sizing and loading states to prevent CLS - Update layout to use optimized component - Add preload for Zod 3 SVG logo Fixes layout shift reported in PR feedback for md+ screen sizes * Update packages/docs/components/sidebar-logo.tsx Co-authored-by: ByteEVM <[email protected]> * Update packages/docs/components/sidebar-logo.tsx Co-authored-by: ByteEVM <[email protected]> * fix(docs): eliminate CLS on aside logo for md+ screens - Fix height container with md:h-7 to prevent layout shift - Use LogoWhite for dark theme instead of Logo - Remove redundant md:w-8 classes for cleaner code - Address layout shift issue identified in PR review * Fix sizing --------- Co-authored-by: ByteEVM <[email protected]> Co-authored-by: Colin McDonnell <[email protected]>
1 parent 100e9aa commit de3517e

File tree

6 files changed

+88
-10
lines changed

6 files changed

+88
-10
lines changed

packages/docs/app/(doc)/layout.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { DocsLayout, type DocsLayoutProps } from "fumadocs-ui/layouts/docs";
44
import type { ReactNode } from "react";
55

66
import { SidebarItem, SidebarSeparator } from "@/components/sidebar-item";
7+
import { SidebarLogo } from "@/components/sidebar-logo";
78

89
const layoutProps: DocsLayoutProps = {
910
...baseOptions,
@@ -21,16 +22,18 @@ const layoutProps: DocsLayoutProps = {
2122
title: "Zod 4",
2223
description: "The latest version of Zod",
2324
url: "/",
24-
icon: <img src="/logo/logo.png" alt="Zod 4" className="h-5" />,
25+
icon: <SidebarLogo src="/logo/logo.png" alt="Zod 4" width={24} height={20} className="h-5" />,
2526
},
2627
{
2728
title: "Zod 3",
2829
description: "In maintenance mode",
2930
url: "https://v3.zod.dev",
3031
icon: (
31-
<img
32+
<SidebarLogo
3233
src="https://raw.githubusercontent.com/colinhacks/zod/3782fe29920c311984004c350b9fefaf0ae4c54a/logo.svg"
3334
alt="Zod 3"
35+
width={24}
36+
height={24}
3437
className="h-6"
3538
/>
3639
),

packages/docs/app/layout.config.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1+
import LogoWhite from "@/public/logo/logo-white.png";
12
import Logo from "@/public/logo/logo.png";
2-
// import LogoWhite from "@/public/logo/logo-white.png";
33
import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared";
44
import Image from "next/image";
55
export const logo = (
6-
<div className="md:mb-2">
7-
<Image alt="Zod logo" src={Logo} sizes="100px" className="hidden dark:block w-8 md:w-8" aria-label="Zod logo" />
8-
<Image alt="Zod logo" src={Logo} sizes="100px" className="block dark:hidden w-8 md:w-8" aria-label="Zod logo" />
6+
<div className="md:mb-1 md:h-7">
7+
<Image alt="Zod logo" src={LogoWhite} sizes="100px" className="hidden dark:block w-8" aria-label="Zod logo" />
8+
<Image alt="Zod logo" src={Logo} sizes="100px" className="block dark:hidden w-8" aria-label="Zod logo" />
99
</div>
1010
);
1111

packages/docs/app/layout.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ const inter = Inter({
1616
export default function Layout({ children }: { children: ReactNode }) {
1717
return (
1818
<html lang="en" className={inter.className} suppressHydrationWarning>
19+
<head>
20+
{/* Preload critical images to prevent FOUC */}
21+
<link rel="preload" as="image" href="/logo/logo-glow.png" />
22+
<link rel="preload" as="image" href="/logo/logo.png" />
23+
<link
24+
rel="preload"
25+
as="image"
26+
href="https://raw.githubusercontent.com/colinhacks/zod/3782fe29920c311984004c350b9fefaf0ae4c54a/logo.svg"
27+
/>
28+
</head>
1929
<body className="flex flex-col min-h-screen">
2030
<Banner id="zod4">
2131
💎 Zod 4 is now stable! <span>&nbsp;</span>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"use client";
2+
3+
import Image from "next/image";
4+
import { useState } from "react";
5+
6+
interface HeroLogoProps {
7+
className?: string;
8+
}
9+
10+
export function HeroLogo({ className }: HeroLogoProps) {
11+
const [isLoaded, setIsLoaded] = useState(false);
12+
13+
return (
14+
<div className={`relative ${className || ""}`} style={{ height: "170px" }}>
15+
<Image
16+
className={`block dark:hidden mx-auto my-0! transition-opacity duration-500 ${
17+
isLoaded ? "opacity-100" : "opacity-0"
18+
}`}
19+
alt="Zod logo"
20+
src="/logo/logo-glow.png"
21+
width={200}
22+
height={170}
23+
priority
24+
quality={100}
25+
onLoad={() => setIsLoaded(true)}
26+
style={{ height: "100%", width: "auto" }}
27+
/>
28+
29+
<Image
30+
className={`hidden dark:block mx-auto my-0! transition-opacity duration-500 ${
31+
isLoaded ? "opacity-100" : "opacity-0"
32+
}`}
33+
alt="Zod logo"
34+
src="/logo/logo-glow.png"
35+
width={200}
36+
height={170}
37+
priority
38+
quality={100}
39+
onLoad={() => setIsLoaded(true)}
40+
style={{ height: "100%", width: "auto" }}
41+
/>
42+
</div>
43+
);
44+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"use client";
2+
3+
import Image, { type ImageProps } from "next/image";
4+
import { useState } from "react";
5+
6+
export function SidebarLogo({ src, alt, width = 24, height = 24, className = "h-5" }: ImageProps) {
7+
const [isLoaded, setIsLoaded] = useState(false);
8+
9+
return (
10+
<div className="relative" style={{ width: `${width}px`, height: `${height}px` }}>
11+
<Image
12+
className={`transition-opacity duration-300 ${isLoaded ? "opacity-100" : "opacity-0"} ${className}`}
13+
alt={alt}
14+
src={src}
15+
width={width}
16+
height={height}
17+
quality={100}
18+
onLoad={() => setIsLoaded(true)}
19+
style={{ width: "auto", height: "100%" }}
20+
/>
21+
</div>
22+
);
23+
}

packages/docs/content/index.mdx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,11 @@ import { Platinum } from '../components/platinum';
1111
import { Gold } from '../components/gold';
1212
import { Silver } from '../components/silver';
1313
import { Bronze } from '../components/bronze';
14+
import { HeroLogo } from '../components/hero-logo';
1415

1516

1617
<div className="flex flex-col items-stretch text-center">
17-
<div style={{height:"170px"}}>
18-
<img className="block dark:hidden mx-auto my-0! h-full" alt="zod logo" src="/logo/logo-glow.png" />
19-
<img className="hidden dark:block mx-auto my-0! h-full" alt="zod logo" src="/logo/logo-glow.png" />
20-
</div>
18+
<HeroLogo />
2119
<h1 className="text-3xl font-bold">Zod</h1>
2220
<p className="mt-2 mb-1">TypeScript-first schema validation with static type inference<br/>by <a href="https://x.com/colinhacks">@colinhacks</a> </p>
2321
<br/>

0 commit comments

Comments
 (0)