perf: fix PageSpeed Insights issues (WebGL errors, bfcache, redirects, a11y)
All checks were successful
Gitea CI / test-build (push) Successful in 11m38s
All checks were successful
Gitea CI / test-build (push) Successful in 11m38s
- Add WebGL support detection in ShaderGradientBackground to prevent console errors - Add .catch() fallback to ShaderGradientBackground dynamic import - Remove hardcoded aria-label from consent banner minimize button (fixes label-content-name-mismatch) - Use rewrite instead of redirect for root locale routing (eliminates one redirect hop) - Change n8n API cache headers from no-store to no-cache (enables bfcache) - Add three and @react-three/fiber to optimizePackageImports for better tree-shaking Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -15,7 +15,7 @@ const BackgroundBlobs = dynamic(() => import("@/components/BackgroundBlobs").cat
|
|||||||
});
|
});
|
||||||
|
|
||||||
const ShaderGradientBackground = dynamic(
|
const ShaderGradientBackground = dynamic(
|
||||||
() => import("./ShaderGradientBackground"),
|
() => import("./ShaderGradientBackground").catch(() => ({ default: () => null })),
|
||||||
{ ssr: false, loading: () => null }
|
{ ssr: false, loading: () => null }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -54,8 +54,6 @@ export default function ConsentBanner() {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => setMinimized(true)}
|
onClick={() => setMinimized(true)}
|
||||||
className="shrink-0 text-xs text-stone-500 hover:text-stone-900 transition-colors"
|
className="shrink-0 text-xs text-stone-500 hover:text-stone-900 transition-colors"
|
||||||
aria-label="Minimize privacy banner"
|
|
||||||
title="Minimize"
|
|
||||||
>
|
>
|
||||||
{s.hide}
|
{s.hide}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,9 +1,23 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { ShaderGradientCanvas, ShaderGradient } from "@shadergradient/react";
|
import { ShaderGradientCanvas, ShaderGradient } from "@shadergradient/react";
|
||||||
|
|
||||||
const ShaderGradientBackground = () => {
|
const ShaderGradientBackground = () => {
|
||||||
|
const [supported, setSupported] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
try {
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
const gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
|
||||||
|
if (!gl) setSupported(false);
|
||||||
|
} catch {
|
||||||
|
setSupported(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!supported) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -88,11 +88,15 @@ export function middleware(request: NextRequest) {
|
|||||||
return addHeaders(request, res);
|
return addHeaders(request, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect bare routes to locale-prefixed ones
|
// Add locale prefix: rewrite root path (avoids redirect roundtrip), redirect others
|
||||||
const preferred = pickLocaleFromHeader(request.headers.get("accept-language"));
|
const preferred = pickLocaleFromHeader(request.headers.get("accept-language"));
|
||||||
const redirectTarget =
|
if (pathname === "/") {
|
||||||
pathname === "/" ? `/${preferred}` : `/${preferred}${pathname}${search || ""}`;
|
responseUrl.pathname = `/${preferred}`;
|
||||||
responseUrl.pathname = redirectTarget;
|
const res = NextResponse.rewrite(responseUrl);
|
||||||
|
res.cookies.set("NEXT_LOCALE", preferred, { path: "/" });
|
||||||
|
return addHeaders(request, res);
|
||||||
|
}
|
||||||
|
responseUrl.pathname = `/${preferred}${pathname}${search || ""}`;
|
||||||
const res = NextResponse.redirect(responseUrl);
|
const res = NextResponse.redirect(responseUrl);
|
||||||
res.cookies.set("NEXT_LOCALE", preferred, { path: "/" });
|
res.cookies.set("NEXT_LOCALE", preferred, { path: "/" });
|
||||||
return addHeaders(request, res);
|
return addHeaders(request, res);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const nextConfig: NextConfig = {
|
|||||||
experimental:
|
experimental:
|
||||||
process.env.NODE_ENV === "production"
|
process.env.NODE_ENV === "production"
|
||||||
? {
|
? {
|
||||||
optimizePackageImports: ["lucide-react", "framer-motion"],
|
optimizePackageImports: ["lucide-react", "framer-motion", "three", "@react-three/fiber"],
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
// In development, enable webpack build worker for faster builds
|
// In development, enable webpack build worker for faster builds
|
||||||
@@ -167,12 +167,12 @@ const nextConfig: NextConfig = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Only prevent caching for real-time/sensitive API routes
|
// Allow bfcache for n8n routes: use no-cache (revalidate) instead of no-store
|
||||||
source: "/api/n8n/(.*)",
|
source: "/api/n8n/(.*)",
|
||||||
headers: [
|
headers: [
|
||||||
{
|
{
|
||||||
key: "Cache-Control",
|
key: "Cache-Control",
|
||||||
value: "no-store, no-cache, must-revalidate, proxy-revalidate",
|
value: "no-cache, must-revalidate",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user