Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 16d494689f | |||
| d24b8a8694 | |||
| 8d42946711 | |||
| b15217b9be | |||
| b138025380 | |||
| 18d2ace03b | |||
| 89ed1faa6b | |||
| 7307e812a4 | |||
| 20d6194a7e | |||
| 86e2fd7097 | |||
| bf8073f5bf | |||
| e8adb87b02 |
2895
.lighthouseci/lhr-1782911226698.html
Normal file
2895
.lighthouseci/lhr-1782911226698.html
Normal file
File diff suppressed because one or more lines are too long
11796
.lighthouseci/lhr-1782911226698.json
Normal file
11796
.lighthouseci/lhr-1782911226698.json
Normal file
File diff suppressed because one or more lines are too long
@@ -21,8 +21,7 @@ import { DynamicInitialLoader as InitialLoader } from '@/components/providers/Dy
|
||||
import { DynamicMobileBottomNav as MobileBottomNav } from '@/components/layout/DynamicMobileBottomNav';
|
||||
|
||||
import { DynamicCorporateBackground as CorporateBackground } from '@/components/decorations/DynamicCorporateBackground';
|
||||
import { DynamicPageTransitionShutter as PageTransitionShutter } from '@/components/providers/DynamicPageTransitionShutter';
|
||||
import { DynamicFramerMotion as FramerMotionProvider } from '@/components/providers/DynamicFramerMotion';
|
||||
const corporateBackground = null; // Removed to prevent line shifting
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ['latin'],
|
||||
@@ -191,28 +190,26 @@ export default async function Layout(props: {
|
||||
<body className="relative flex flex-col min-h-screen font-sans antialiased overflow-x-hidden selection:bg-primary/90 selection:text-white" suppressHydrationWarning>
|
||||
<NextIntlClientProvider messages={clientMessages} locale={safeLocale}>
|
||||
<TransitionProvider>
|
||||
<FramerMotionProvider>
|
||||
<CorporateBackground />
|
||||
<InitialLoader shouldShowLoader={!hasSeenLoader} />
|
||||
<PageTransitionShutter />
|
||||
<SkipLink />
|
||||
<Header navLinks={navLinks} />
|
||||
<MobileBottomNav navLinks={navLinks} currentLocale={safeLocale} />
|
||||
|
||||
<main
|
||||
id="main-content"
|
||||
className="flex-grow overflow-visible"
|
||||
tabIndex={-1}
|
||||
>
|
||||
{children}
|
||||
</main>
|
||||
<CorporateBackground />
|
||||
{!hasSeenLoader && <InitialLoader shouldShowLoader={!hasSeenLoader} />}
|
||||
{/* PageTransitionShutter loaded dynamically in TransitionProvider */}
|
||||
<SkipLink />
|
||||
<Header navLinks={navLinks} />
|
||||
<MobileBottomNav navLinks={navLinks} currentLocale={safeLocale} />
|
||||
|
||||
<main
|
||||
id="main-content"
|
||||
className="flex-grow overflow-visible"
|
||||
tabIndex={-1}
|
||||
>
|
||||
{children}
|
||||
</main>
|
||||
|
||||
<Footer companyInfo={companyInfo} />
|
||||
<JsonLd />
|
||||
<AnalyticsShell />
|
||||
<Footer companyInfo={companyInfo} />
|
||||
<JsonLd />
|
||||
<AnalyticsShell />
|
||||
|
||||
<FeedbackClientWrapper />
|
||||
</FramerMotionProvider>
|
||||
<FeedbackClientWrapper />
|
||||
</TransitionProvider>
|
||||
</NextIntlClientProvider>
|
||||
</body>
|
||||
|
||||
@@ -132,7 +132,12 @@ export default async function Home(props: { params: Promise<{ locale: string }>
|
||||
}
|
||||
|
||||
setRequestLocale(locale);
|
||||
preload('/germany-map.svg', { as: 'image', fetchPriority: 'high' });
|
||||
preload('/assets/videos/web/hero-kabelpflug-poster.webp', {
|
||||
as: 'image',
|
||||
fetchPriority: 'high',
|
||||
imageSrcSet: '/assets/videos/web/hero-kabelpflug-poster-mobile.webp 800w, /assets/videos/web/hero-kabelpflug-poster.webp 1920w',
|
||||
imageSizes: '(max-width: 768px) 800px, 1920px'
|
||||
});
|
||||
|
||||
const mdx = await getMdxContent(locale, 'home');
|
||||
const allReferences = await getAllReferences(locale);
|
||||
|
||||
@@ -41,6 +41,7 @@ export const HeroSection: React.FC<HeroSectionProps> = (props) => {
|
||||
objectPosition: `${backgroundImage?.focalX ?? 50}% ${backgroundImage?.focalY ?? 50}%`,
|
||||
}}
|
||||
sizes="100vw"
|
||||
quality={50}
|
||||
priority
|
||||
/>
|
||||
{/* Cinematic Color Grading Overlay */}
|
||||
|
||||
@@ -80,16 +80,14 @@ export function HeroVideo(props: HeroVideoProps) {
|
||||
{/* Background color while video loads */}
|
||||
<div className="absolute inset-0 z-0 bg-neutral-dark" />
|
||||
|
||||
{/* ALWAYS render the Next.js optimized Image as the LCP element */}
|
||||
<Image
|
||||
<img
|
||||
key={`img-${pathname}-${posterSrc}`}
|
||||
src={posterSrc}
|
||||
srcSet={posterSrc.includes('-poster.webp') ? `${posterSrc.replace('-poster.webp', '-poster-mobile.webp')} 800w, ${posterSrc} 1920w` : undefined}
|
||||
sizes={posterSrc.includes('-poster.webp') ? "(max-width: 768px) 800px, 1920px" : undefined}
|
||||
alt={posterAlt}
|
||||
fill
|
||||
className="object-cover z-[1] pointer-events-none"
|
||||
sizes="100vw"
|
||||
quality={25}
|
||||
priority
|
||||
className="absolute inset-0 w-full h-full object-cover z-[1] pointer-events-none"
|
||||
decoding="sync"
|
||||
fetchPriority="high"
|
||||
/>
|
||||
{/* Fast compositing layer to replace the heavy CSS filters on the image */}
|
||||
|
||||
@@ -23,7 +23,7 @@ export function EUFundingBadge() {
|
||||
alt="European Union Background"
|
||||
fill
|
||||
className="object-cover object-top"
|
||||
priority
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
'use client';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
export const DynamicFramerMotion = dynamic(
|
||||
() => import('./FramerMotionProvider').then((mod) => mod.FramerMotionProvider),
|
||||
{ ssr: true }
|
||||
);
|
||||
@@ -1,11 +0,0 @@
|
||||
'use client';
|
||||
import { LazyMotion } from 'framer-motion';
|
||||
const loadFeatures = () => import('@/lib/framer-features').then(res => res.default);
|
||||
|
||||
export function FramerMotionProvider({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<LazyMotion features={loadFeatures} strict>
|
||||
{children}
|
||||
</LazyMotion>
|
||||
);
|
||||
}
|
||||
@@ -2,6 +2,16 @@
|
||||
|
||||
import React, { createContext, useContext, useState, useCallback, ReactNode, useEffect } from 'react';
|
||||
import { useRouter, usePathname, useSearchParams } from 'next/navigation';
|
||||
import { LazyMotion } from 'framer-motion';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
const loadFeatures = () => import('@/lib/framer-features').then(res => res.default);
|
||||
|
||||
// Defer loading the shutter to eliminate AnimatePresence from the initial bundle
|
||||
const DynamicPageTransitionShutter = dynamic(
|
||||
() => import('./PageTransitionShutter').then(mod => mod.PageTransitionShutter),
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
interface TransitionContextType {
|
||||
isTransitioning: boolean;
|
||||
@@ -14,8 +24,18 @@ const TransitionContext = createContext<TransitionContextType | undefined>(undef
|
||||
export function TransitionProvider({ children }: { children: ReactNode }) {
|
||||
const [isTransitioning, setIsTransitioning] = useState(false);
|
||||
const [transitionMessage, setTransitionMessage] = useState<string | null>(null);
|
||||
const [hasMountedShutter, setHasMountedShutter] = useState(false);
|
||||
const router = useRouter();
|
||||
|
||||
// Load the heavy shutter chunk in the background when the main thread is idle
|
||||
useEffect(() => {
|
||||
if (typeof window !== 'undefined' && 'requestIdleCallback' in window) {
|
||||
window.requestIdleCallback(() => setHasMountedShutter(true));
|
||||
} else {
|
||||
setTimeout(() => setHasMountedShutter(true), 2000);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// We use a ref to track transition state without triggering the route change effect when it turns true
|
||||
const isTransitioningRef = React.useRef(isTransitioning);
|
||||
useEffect(() => {
|
||||
@@ -41,6 +61,7 @@ export function TransitionProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
setTransitionMessage(message);
|
||||
setIsTransitioning(true);
|
||||
setHasMountedShutter(true);
|
||||
|
||||
// Die Animation des Shutters abwarten (ca. 700ms), dann pushen
|
||||
setTimeout(() => {
|
||||
@@ -68,7 +89,10 @@ export function TransitionProvider({ children }: { children: ReactNode }) {
|
||||
setTransitionMessage={setTransitionMessage}
|
||||
/>
|
||||
</React.Suspense>
|
||||
{children}
|
||||
<LazyMotion features={loadFeatures}>
|
||||
{hasMountedShutter && <DynamicPageTransitionShutter />}
|
||||
{children}
|
||||
</LazyMotion>
|
||||
</TransitionContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ description: "Die E-TIB GmbH ist Ihr zuverlässiger Partner für komplexe Kabelt
|
||||
items={[
|
||||
{
|
||||
title: "Kabelleitungsnetzbau",
|
||||
description: "Kabelleitungsnetzbau (Hoch-/Mittel- und Niederspannung) sowie Kabelmontagen bis 110 kV",
|
||||
description: "Kabelleitungsnetzbau (Nieder-/Mittel- und Hochspannung bis 110 kV)",
|
||||
tag: "Energie",
|
||||
size: "large",
|
||||
href: "/de/kabeltiefbau",
|
||||
|
||||
@@ -8,7 +8,7 @@ layout: "fullBleed"
|
||||
<HeroSection
|
||||
badge="Kernkompetenz"
|
||||
title="Kabelleitungsnetzbau"
|
||||
subtitle="Komplette Infrastruktur-Lösungen für Hoch-, Mittel- und Niederspannungsnetze sowie Kabelmontagen bis 110 kV"
|
||||
subtitle="Komplette Infrastruktur-Lösungen für Nieder-, Mittel- und Hochspannungsnetze bis 110 kV"
|
||||
backgroundImage={{ url: '/assets/photos/DSC01123.JPG' }}
|
||||
alignment="center"
|
||||
ctaLabel="Projekt anfragen"
|
||||
|
||||
@@ -449,7 +449,7 @@ const nextConfig = {
|
||||
},
|
||||
images: {
|
||||
qualities: [25, 50, 75, 100],
|
||||
formats: ['image/webp'],
|
||||
formats: ['image/avif', 'image/webp'],
|
||||
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
|
||||
remotePatterns: [
|
||||
{
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
"prepare": "husky",
|
||||
"preinstall": "npx only-allow pnpm"
|
||||
},
|
||||
"version": "2.2.94",
|
||||
"version": "2.4.3",
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"@parcel/watcher",
|
||||
|
||||
BIN
public/assets/videos/web/hero-kabelpflug-poster-mobile.webp
Normal file
BIN
public/assets/videos/web/hero-kabelpflug-poster-mobile.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 110 KiB |
@@ -39,7 +39,7 @@ describe('Task 6 Content Updates', () => {
|
||||
|
||||
// DE Home
|
||||
expect(deContent).toContain('title: "Kabelleitungsnetzbau"');
|
||||
expect(deContent).toContain('description: "Kabelleitungsnetzbau (Hoch-/Mittel- und Niederspannung) sowie Kabelmontagen bis 110 kV"');
|
||||
expect(deContent).toContain('description: "Kabelleitungsnetzbau (Nieder-/Mittel- und Hochspannung bis 110 kV)"');
|
||||
expect(deContent).toContain('100+');
|
||||
expect(deContent).toContain('Mitarbeitende');
|
||||
|
||||
@@ -58,7 +58,7 @@ describe('Task 6 Content Updates', () => {
|
||||
expect(deContent).toContain('title: "Kabelleitungsnetzbau"');
|
||||
expect(deContent).toContain('excerpt: "Professioneller Kabelleitungsnetzbau');
|
||||
expect(deContent).toContain('Kabelmontagen bis 110 kV');
|
||||
expect(deContent).toContain('subtitle="Komplette Infrastruktur-Lösungen für Hoch-, Mittel- und Niederspannungsnetze sowie Kabelmontagen bis 110 kV"');
|
||||
expect(deContent).toContain('subtitle="Komplette Infrastruktur-Lösungen für Nieder-, Mittel- und Hochspannungsnetze bis 110 kV"');
|
||||
expect(deContent).toContain('"Kabelmontagen bis 110 kV"');
|
||||
|
||||
// EN Kabelnetzbau
|
||||
|
||||
Reference in New Issue
Block a user