Compare commits
20 Commits
v2.4.32
...
fix/remove
| Author | SHA1 | Date | |
|---|---|---|---|
| a9f16b49cc | |||
| 21cf0725ab | |||
| 2c162f5314 | |||
| be3b2e6a7b | |||
| f2e59fa52f | |||
| 16d494689f | |||
| d24b8a8694 | |||
| 8d42946711 | |||
| b15217b9be | |||
| b138025380 | |||
| 18d2ace03b | |||
| 89ed1faa6b | |||
| 7307e812a4 | |||
| 20d6194a7e | |||
| 86e2fd7097 | |||
| bf8073f5bf | |||
| e8adb87b02 | |||
| 067e34c19b | |||
| e6017e7436 | |||
| 1955bfcd1b |
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: '100vw'
|
||||
});
|
||||
|
||||
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 */}
|
||||
|
||||
@@ -34,7 +34,7 @@ export function HeroVideo(props: HeroVideoProps) {
|
||||
|
||||
let defaultPoster = "/assets/photos/DJI_0048.JPG";
|
||||
if (videoUrl && videoUrl.endsWith('.mp4')) {
|
||||
defaultPoster = videoUrl.replace('.mp4', '-poster.jpg');
|
||||
defaultPoster = videoUrl.replace('.mp4', '-poster.webp');
|
||||
}
|
||||
|
||||
const posterSrc = props.backgroundImage?.url || props.posterImage?.url || data?.posterImage?.url || defaultPoster;
|
||||
@@ -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') ? "100vw" : 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>
|
||||
);
|
||||
}
|
||||
@@ -72,8 +72,8 @@ export function InitialLoader({ shouldShowLoader = true }: { shouldShowLoader?:
|
||||
{/* Sweeping intense white light */}
|
||||
<m.div
|
||||
className="absolute inset-0 bg-gradient-to-r from-transparent via-white to-transparent skew-x-[-25deg]"
|
||||
initial={{ left: '-150%' }}
|
||||
animate={{ left: '250%' }}
|
||||
initial={{ x: '-150%' }}
|
||||
animate={{ x: '250%' }}
|
||||
transition={{ duration: 2.5, ease: "easeInOut", repeat: Infinity, repeatDelay: 0.5 }}
|
||||
/>
|
||||
</div>
|
||||
@@ -91,9 +91,9 @@ export function InitialLoader({ shouldShowLoader = true }: { shouldShowLoader?:
|
||||
className="h-[2px] w-full bg-white/5 overflow-hidden relative rounded-full"
|
||||
>
|
||||
<m.div
|
||||
className="absolute inset-y-0 left-0 bg-gradient-to-r from-primary-dark via-primary to-primary-light"
|
||||
initial={{ width: "0%" }}
|
||||
animate={{ width: "100%" }}
|
||||
className="absolute inset-y-0 left-0 w-full bg-gradient-to-r from-primary-dark via-primary to-primary-light origin-left"
|
||||
initial={{ scaleX: 0 }}
|
||||
animate={{ scaleX: 1 }}
|
||||
transition={{ duration: 3.8, ease: [0.16, 1, 0.3, 1] }}
|
||||
/>
|
||||
</m.div>
|
||||
|
||||
@@ -12,12 +12,12 @@ export function PageTransitionShutter() {
|
||||
<AnimatePresence>
|
||||
{isTransitioning && (
|
||||
<m.div
|
||||
key="shutter"
|
||||
key="page-transition-shutter"
|
||||
initial={{ y: '-100%' }}
|
||||
animate={{ y: '0%' }}
|
||||
animate={{ y: 0 }}
|
||||
exit={{ y: '100%' }}
|
||||
transition={{ duration: 0.9, ease: [0.16, 1, 0.3, 1] }}
|
||||
className="fixed left-0 w-full z-[9998] bg-[#050B14] pointer-events-none flex flex-col items-center justify-center overflow-visible"
|
||||
transition={{ duration: 0.65, ease: [0.76, 0, 0.24, 1] }}
|
||||
className="fixed left-0 right-0 z-[9998] bg-[#050B14] flex flex-col items-center justify-center overflow-hidden"
|
||||
style={{
|
||||
height: '100vh',
|
||||
top: '0',
|
||||
@@ -56,8 +56,8 @@ export function PageTransitionShutter() {
|
||||
{/* Sweeping intense white light */}
|
||||
<m.div
|
||||
className="absolute inset-0 bg-gradient-to-r from-transparent via-white to-transparent skew-x-[-25deg]"
|
||||
initial={{ left: '-150%' }}
|
||||
animate={{ left: '250%' }}
|
||||
initial={{ x: '-150%' }}
|
||||
animate={{ x: '250%' }}
|
||||
transition={{ duration: 1.5, ease: "easeInOut", repeat: Infinity, repeatDelay: 0.2 }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -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,12 @@ 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();
|
||||
|
||||
// We no longer eagerly load the shutter in the background.
|
||||
// It will be loaded precisely when the user initiates a transition.
|
||||
|
||||
// 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 +55,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 +83,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"
|
||||
|
||||
@@ -15,7 +15,6 @@ const nextConfig = {
|
||||
pagesBufferLength: 2,
|
||||
},
|
||||
experimental: {
|
||||
inlineCss: true,
|
||||
staleTimes: {
|
||||
dynamic: 0,
|
||||
static: 30,
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
"prepare": "husky",
|
||||
"preinstall": "npx only-allow pnpm"
|
||||
},
|
||||
"version": "2.2.94",
|
||||
"version": "2.4.5",
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"@parcel/watcher",
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 4.7 KiB |
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: 39 KiB |
BIN
public/assets/videos/web/hero-kabelpflug-poster.webp
Normal file
BIN
public/assets/videos/web/hero-kabelpflug-poster.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 86 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