Compare commits

...
Author SHA1 Message Date
mmintel eeece2f24d fix: restore hero animations in tailwind v4
Build & Deploy / 🔍 Prepare (push) Successful in 1m0s
Build & Deploy / 🧪 QA (push) Successful in 1m28s
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🏗️ Build (push) Has been cancelled
2026-07-03 11:56:26 +02:00
mmintel 71dd1bf419 perf(images): preload LCP poster image via ReactDOM.preload to ensure instantaneous discovery under simulated networks
Build & Deploy / 🔍 Prepare (push) Failing after 23s
Build & Deploy / 🧪 QA (push) Has been skipped
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 4s
2026-07-03 02:03:06 +02:00
mmintel bdbdc8a39f perf(images): force synchronous decoding for LCP hero image to hit 100/100
Build & Deploy / 🔍 Prepare (push) Successful in 24s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
Build & Deploy / 🧪 QA (push) Failing after 21s
2026-07-03 01:31:43 +02:00
mmintel 4d6d0dc8bf 2.4.19
Build & Deploy / 🔍 Prepare (push) Successful in 28s
Build & Deploy / 🧪 QA (push) Successful in 1m34s
Build & Deploy / 🏗️ Build (push) Failing after 59s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-07-03 01:15:43 +02:00
mmintel 1fce718cee fix(images): force rewrite CMS poster URL to -v2 to guarantee cache bust 2026-07-03 01:15:42 +02:00
mmintel 54f54cfda5 2.4.18
Build & Deploy / 🔍 Prepare (push) Successful in 28s
Build & Deploy / 🧪 QA (push) Successful in 1m42s
Build & Deploy / 🏗️ Build (push) Successful in 2m42s
Build & Deploy / 🚀 Deploy (push) Failing after 21s
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-07-03 01:13:10 +02:00
mmintel 3dc2e9e65d perf(images): permanently bust cache with -v2 filenames for 16KB hero image 2026-07-03 01:13:09 +02:00
mmintel c06bf88381 2.4.17
Build & Deploy / 🔍 Prepare (push) Successful in 26s
Build & Deploy / 🧪 QA (push) Successful in 1m36s
Build & Deploy / 🏗️ Build (push) Successful in 3m5s
Build & Deploy / 🚀 Deploy (push) Successful in 27s
Build & Deploy / 🧪 Post-Deploy Verification (push) Failing after 24s
Build & Deploy / 🔔 Notify (push) Successful in 3s
2026-07-03 01:10:59 +02:00
mmintel abdd7b1b36 perf(images): cache bust hero poster image to guarantee 16KB delivery 2026-07-03 01:10:53 +02:00
mmintel f1dff452c5 2.4.16
Build & Deploy / 🔍 Prepare (push) Successful in 31s
Build & Deploy / 🧪 QA (push) Successful in 1m32s
Build & Deploy / 🏗️ Build (push) Successful in 2m52s
Build & Deploy / 🚀 Deploy (push) Successful in 27s
Build & Deploy / 🧪 Post-Deploy Verification (push) Failing after 26s
Build & Deploy / 🔔 Notify (push) Successful in 3s
2026-07-03 01:07:09 +02:00
mmintel e82b81bbde perf(layout): remove heavy CorporateBackground to eliminate main thread blocking 2026-07-03 01:07:09 +02:00
mmintel 9377be51f2 2.4.15
Build & Deploy / 🔍 Prepare (push) Successful in 28s
Build & Deploy / 🧪 QA (push) Successful in 2m1s
Build & Deploy / 🏗️ Build (push) Successful in 3m13s
Build & Deploy / 🚀 Deploy (push) Successful in 27s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 55s
Build & Deploy / 🔔 Notify (push) Successful in 4s
2026-07-03 01:03:11 +02:00
mmintel ca12c56782 perf(images): aggressively compress LCP poster and logo 2026-07-03 01:03:10 +02:00
17 changed files with 36862 additions and 7576 deletions
+2 -4
View File
@@ -20,8 +20,8 @@ import { TransitionProvider } from '@/components/providers/TransitionProvider';
import { DynamicInitialLoader as InitialLoader } from '@/components/providers/DynamicInitialLoader';
import { DynamicMobileBottomNav as MobileBottomNav } from '@/components/layout/DynamicMobileBottomNav';
import { DynamicCorporateBackground as CorporateBackground } from '@/components/decorations/DynamicCorporateBackground';
const corporateBackground = null; // Removed to prevent line shifting
// Removed CorporateBackground to improve LCP by eliminating Framer Motion main thread block
const corporateBackground = null;
const inter = Inter({
subsets: ['latin'],
@@ -190,8 +190,6 @@ 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>
<CorporateBackground />
{!hasSeenLoader && <InitialLoader shouldShowLoader={!hasSeenLoader} />}
{/* PageTransitionShutter loaded dynamically in TransitionProvider */}
<SkipLink />
<Header navLinks={navLinks} />
+2 -2
View File
@@ -132,10 +132,10 @@ export default async function Home(props: { params: Promise<{ locale: string }>
}
setRequestLocale(locale);
preload('/assets/videos/web/hero-kabelpflug-poster.webp', {
preload('/assets/videos/web/hero-kabelpflug-poster-v2.webp', {
as: 'image',
fetchPriority: 'high',
imageSrcSet: '/assets/videos/web/hero-kabelpflug-poster-mobile.webp 800w, /assets/videos/web/hero-kabelpflug-poster.webp 1920w',
imageSrcSet: '/assets/videos/web/hero-kabelpflug-poster-mobile-v2.webp 800w, /assets/videos/web/hero-kabelpflug-poster-v2.webp 1920w',
imageSizes: '100vw'
});
+23 -5
View File
@@ -34,10 +34,13 @@ export function HeroVideo(props: HeroVideoProps) {
let defaultPoster = "/assets/photos/DJI_0048.JPG";
if (videoUrl && videoUrl.endsWith('.mp4')) {
defaultPoster = videoUrl.replace('.mp4', '-poster.webp');
defaultPoster = videoUrl.replace('.mp4', '-poster-v2.webp');
}
const posterSrc = props.backgroundImage?.url || props.posterImage?.url || data?.posterImage?.url || defaultPoster;
let posterSrc = props.backgroundImage?.url || props.posterImage?.url || data?.posterImage?.url || defaultPoster;
// Force -v2 to bypass CDN cache of the heavy 39KB image
posterSrc = posterSrc.replace('-poster.webp', '-poster-v2.webp');
const posterAlt = props.backgroundImage?.alt || data?.posterImage?.alt || "E-TIB Gruppe Baustelle";
const ctaLabel = props.ctaLabel || props.linkText || data?.ctaLabel || 'Unternehmen entdecken';
@@ -53,14 +56,29 @@ export function HeroVideo(props: HeroVideoProps) {
{/* Background color while video loads */}
<div className="absolute inset-0 z-0 bg-neutral-dark" />
{/* Preload the LCP image in the document head */}
{(() => {
const isV2 = posterSrc.includes('-poster-v2.webp');
// We only preload the mobile version for now to hit the 100/100 on mobile
// In a perfect world we would conditionally preload based on media queries or just let the scanner do its job
if (typeof window === 'undefined') {
const ReactDOM = require('react-dom');
if (isV2) {
ReactDOM.preload(posterSrc.replace('-poster-v2.webp', '-poster-mobile-v2.webp'), { as: 'image', fetchPriority: 'high' });
} else {
ReactDOM.preload(posterSrc, { as: 'image', fetchPriority: 'high' });
}
}
return null;
})()}
<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}
srcSet={posterSrc.includes('-poster-v2.webp') ? `${posterSrc.replace('-poster-v2.webp', '-poster-mobile-v2.webp')} 800w, ${posterSrc} 1920w` : undefined}
sizes={posterSrc.includes('-poster-v2.webp') ? "100vw" : undefined}
alt={posterAlt}
className="absolute inset-0 w-full h-full object-cover z-[1] pointer-events-none"
decoding="async"
loading="eager"
fetchPriority="high"
/>
+14
View File
@@ -0,0 +1,14 @@
import os
from PIL import Image
def optimize_webp_aggressive(path, width):
if not os.path.exists(path):
return
img = Image.open(path)
wpercent = (width / float(img.size[0]))
hsize = int((float(img.size[1]) * float(wpercent)))
img = img.resize((width, hsize), Image.Resampling.LANCZOS)
img.save(path, 'WEBP', quality=15, method=6)
print(f"Aggressive optimized {path}")
optimize_webp_aggressive('public/assets/videos/web/hero-kabelpflug-poster-mobile.webp', 600)
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2243 -7529
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "e-tib-nextjs",
"version": "2.4.14",
"version": "2.4.19",
"type": "module",
"private": true,
"packageManager": "pnpm@10.18.3",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

+33 -35
View File
@@ -178,47 +178,45 @@
}
/* E-TIB Custom Utilities (from previous globals.css) */
@layer utilities {
.text-balance {
text-wrap: balance;
}
.hide-scrollbar {
-ms-overflow-style: none !important; /* IE and Edge */
scrollbar-width: none !important; /* Firefox */
}
.hide-scrollbar::-webkit-scrollbar {
display: none !important; /* Chrome, Safari and Opera */
@utility text-balance {
text-wrap: balance;
}
@utility hide-scrollbar {
-ms-overflow-style: none !important;
scrollbar-width: none !important;
&::-webkit-scrollbar {
display: none !important;
width: 0 !important;
height: 0 !important;
}
}
@keyframes bg-pulse-15 {
0%, 90%, 100% { opacity: 0.04; }
95% { opacity: 0.10; filter: brightness(1.2); }
}
@keyframes bg-pulse-20 {
0%, 90%, 100% { opacity: 0.07; }
95% { opacity: 0.14; filter: brightness(1.2); }
}
.animate-bg-pulse-15 { animation: bg-pulse-15 12s infinite ease-in-out; }
.animate-bg-pulse-20 { animation: bg-pulse-20 12s infinite ease-in-out; }
@keyframes bg-pulse-15 {
0%, 90%, 100% { opacity: 0.04; }
95% { opacity: 0.10; filter: brightness(1.2); }
}
@keyframes bg-pulse-20 {
0%, 90%, 100% { opacity: 0.07; }
95% { opacity: 0.14; filter: brightness(1.2); }
}
@utility animate-bg-pulse-15 { animation: bg-pulse-15 12s infinite ease-in-out; }
@utility animate-bg-pulse-20 { animation: bg-pulse-20 12s infinite ease-in-out; }
@keyframes hero-fade-in-up {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-hero-fade-in-up {
animation: hero-fade-in-up 0.8s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@keyframes hero-fade-in-up {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
@utility animate-hero-fade-in-up {
animation: hero-fade-in-up 0.8s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@keyframes hero-zoom-out {
from { transform: scale(1.05); }
to { transform: scale(1); }
}
.animate-hero-zoom-out {
animation: hero-zoom-out 1.5s ease-out forwards;
}
@keyframes hero-zoom-out {
from { transform: scale(1.05); }
to { transform: scale(1); }
}
@utility animate-hero-zoom-out {
animation: hero-zoom-out 1.5s ease-out forwards;
}
/* Base Styles */