klz case study
Some checks failed
Build & Deploy Mintel Blog / build-and-deploy (push) Failing after 2m14s

This commit is contained in:
2026-02-02 12:01:48 +01:00
parent c9a4afe080
commit 8a7110e9ef
661 changed files with 11273 additions and 38324 deletions

View File

@@ -2,6 +2,8 @@
import React from 'react';
import { cn } from '../utils/cn';
import { ShieldCheck } from 'lucide-react';
import { MonoLabel } from './Typography';
interface IframeSectionProps {
src: string;
@@ -20,8 +22,40 @@ interface IframeSectionProps {
rotate?: number;
delay?: number;
noScale?: boolean;
dynamicGlow?: boolean;
}
/**
* Reusable Browser UI components to maintain consistency
*/
const BrowserChrome: React.FC<{ url: string; minimal?: boolean }> = ({ url, minimal }) => {
if (minimal) return null;
return (
<div className="h-14 bg-white/70 backdrop-blur-2xl border-b border-slate-200/40 flex items-center px-6 gap-8 z-30 flex-shrink-0 relative">
{/* Status Indicators (Traffic Lights) */}
<div className="flex gap-1.5 opacity-40">
<div className="w-1.5 h-1.5 rounded-full bg-slate-900" />
<div className="w-1.5 h-1.5 rounded-full bg-slate-900" />
</div>
{/* URL Bar */}
<div className="flex-1 max-w-[600px] mx-auto bg-white/30 backdrop-blur-3xl rounded-full flex items-center justify-center px-6 h-8 border border-white/60 shadow-[0_2px_12px_-4px_rgba(0,0,0,0.08)]">
<div className="flex items-center gap-3 opacity-80 group-hover:opacity-100 transition-all duration-700">
<ShieldCheck className="w-3.5 h-3.5 text-slate-900" />
<span className="text-[10px] font-mono font-bold tracking-[0.25em] uppercase truncate whitespace-nowrap text-slate-900">
{url}
</span>
</div>
</div>
{/* Industrial Accent */}
<div className="flex items-center gap-2 opacity-30">
<div className="w-8 h-1 bg-slate-400 rounded-full" />
</div>
</div>
);
};
export const IframeSection: React.FC<IframeSectionProps> = ({
src,
title,
@@ -38,12 +72,24 @@ export const IframeSection: React.FC<IframeSectionProps> = ({
perspective = false,
rotate = 0,
delay = 0,
noScale = false
noScale = false,
dynamicGlow = true
}) => {
const containerRef = React.useRef<HTMLDivElement>(null);
const iframeRef = React.useRef<HTMLIFrameElement>(null);
const canvasRef = React.useRef<HTMLCanvasElement>(null);
const [scale, setScale] = React.useState(1);
const [isLoading, setIsLoading] = React.useState(true);
const [glowColors, setGlowColors] = React.useState<string[]>([
'rgba(148, 163, 184, 0.1)',
'rgba(148, 163, 184, 0.1)',
'rgba(148, 163, 184, 0.1)',
'rgba(148, 163, 184, 0.1)'
]);
const [scrollState, setScrollState] = React.useState({ atTop: true, atBottom: false, isScrollable: false });
// Scaling Logic
React.useEffect(() => {
if (!containerRef.current || noScale) {
setScale(1);
@@ -63,22 +109,95 @@ export const IframeSection: React.FC<IframeSectionProps> = ({
updateScale();
const observer = new ResizeObserver(updateScale);
observer.observe(containerRef.current);
return () => observer.disconnect();
}, [desktopWidth, zoom, noScale]);
const containerStyle = clipHeight
? { height: `${clipHeight * (noScale ? 1 : scale)}px` }
: { height };
const updateScrollState = React.useCallback(() => {
try {
const doc = iframeRef.current?.contentDocument?.documentElement;
if (doc) {
const atTop = doc.scrollTop <= 5;
const atBottom = doc.scrollTop + doc.clientHeight >= doc.scrollHeight - 5;
const isScrollable = doc.scrollHeight > doc.clientHeight + 10;
setScrollState({ atTop, atBottom, isScrollable });
}
} catch (e) { }
}, []);
// Ambilight effect (sampled from iframe if same-origin)
const updateAmbilight = React.useCallback(() => {
if (!dynamicGlow || !iframeRef.current || !canvasRef.current) return;
try {
const iframe = iframeRef.current;
const doc = iframe.contentDocument;
if (!doc) return;
const canvas = canvasRef.current;
const ctx = canvas.getContext('2d', { willReadFrequently: true });
if (!ctx) return;
canvas.width = 100;
canvas.height = 100;
const body = doc.body;
const computedStyle = window.getComputedStyle(body);
const bgColor = computedStyle.backgroundColor || 'rgba(255,255,255,1)';
const sampleX = (x: number, y: number) => {
const el = doc.elementFromPoint(x, y);
if (el) return window.getComputedStyle(el).backgroundColor;
return bgColor;
};
const w = doc.documentElement.scrollWidth || iframe.offsetWidth;
const h = doc.documentElement.scrollHeight || iframe.offsetHeight;
const sampleMargin = 20;
const colors = [
sampleX(w / 2, sampleMargin + offsetY),
sampleX(w - sampleMargin, h / 2 + offsetY),
sampleX(w / 2, h - sampleMargin + offsetY),
sampleX(sampleMargin, h / 2 + offsetY)
];
setGlowColors(colors.map(c => {
if (!c || c === 'transparent') return 'rgba(148, 163, 184, 0.1)';
return c.replace('rgb(', 'rgba(').replace(')', ', 0.5)');
}));
updateScrollState();
} catch (e) { }
}, [dynamicGlow, offsetY, updateScrollState]);
const headerHeightPx = (browserFrame && !minimal) ? 56 : 0;
// Height parse helper
const parseNumericHeight = (h: string | number) => {
if (typeof h === 'number') return h;
const match = h.match(/^(\d+(?:\.\d+)?)(px)$/);
return match ? parseFloat(match[1]) : null;
};
const baseNumericHeight = parseNumericHeight(height);
const finalScaledHeight = clipHeight
? (clipHeight * scale)
: (baseNumericHeight ? (baseNumericHeight * scale) : null);
const chassisStyle = {
height: height === '100%'
? '100%'
: (finalScaledHeight ? `${finalScaledHeight + headerHeightPx}px` : `calc(${height} + ${headerHeightPx}px)`)
};
return (
<div
className={cn("w-full group", !minimal && "space-y-6", className)}
className={cn("w-full group relative", !minimal && "space-y-6", className)}
style={{
opacity: 0,
animation: `fadeIn 0.8s ease-out ${delay}s forwards`
animation: `fadeIn 0.8s ease-out ${delay}s forwards`,
...className?.includes('h-full') ? { height: '100%' } : {}
}}
>
<canvas ref={canvasRef} className="hidden" aria-hidden="true" />
{!minimal && (title || description) && (
<div className="space-y-2 px-1">
{title && <h4 className="text-2xl font-bold text-slate-900 tracking-tight leading-none">{title}</h4>}
@@ -86,73 +205,138 @@ export const IframeSection: React.FC<IframeSectionProps> = ({
</div>
)}
{/* Main Device Chassis */}
<div
ref={containerRef}
className={cn(
"w-full relative overflow-hidden transition-all duration-700 ease-[cubic-bezier(0.23,1,0.32,1)]",
minimal ? "bg-transparent" : "bg-slate-100",
!minimal && (browserFrame ? "rounded-t-3xl border-t border-x border-slate-200 shadow-[0_32px_128px_-16px_rgba(0,0,0,0.1)]" : "rounded-3xl border border-slate-200/60 shadow-xl"),
perspective && "hover:scale-[1.01] hover:-translate-y-1",
noScale && "overflow-x-auto"
"w-full relative transition-all duration-1000 ease-[cubic-bezier(0.23,1,0.32,1)] flex flex-col",
minimal ? "bg-transparent" : "bg-slate-50",
!minimal && "rounded-[2.5rem] border border-slate-200/50 shadow-[0_80px_160px_-40px_rgba(0,0,0,0.18),0_0_1px_rgba(0,0,0,0.1)]",
perspective && "hover:scale-[1.03] hover:-translate-y-3",
"overflow-hidden"
)}
style={{
...containerStyle,
perspective: perspective ? '1000px' : 'none',
transform: rotate ? `rotateY(${rotate}deg)` : 'none',
}}
style={chassisStyle}
>
{/* AMBILIGHT DYNAMIC GLOW */}
{dynamicGlow && (
<div className="absolute -inset-[30%] blur-[140px] opacity-30 group-hover:opacity-90 transition-all duration-[2000ms] pointer-events-none z-0">
<div
className="absolute inset-0 rounded-[6rem]"
style={{
background: `
radial-gradient(circle at 50% 10%, ${glowColors[0]} 0%, transparent 60%),
radial-gradient(circle at 95% 50%, ${glowColors[1]} 0%, transparent 60%),
radial-gradient(circle at 50% 90%, ${glowColors[2]} 0%, transparent 60%),
radial-gradient(circle at 5% 50%, ${glowColors[3]} 0%, transparent 60%)
`,
filter: 'saturate(2.2) brightness(1.1)'
}}
/>
</div>
)}
{/* Loader Overlay */}
{isLoading && (
<div className="absolute inset-0 flex items-center justify-center bg-slate-50 z-30 transition-opacity duration-300">
<div className="w-8 h-8 border-2 border-slate-200 border-t-slate-900 rounded-full animate-spin" />
<div className="absolute inset-0 flex items-center justify-center bg-white/60 backdrop-blur-xl z-50 transition-opacity duration-700">
<div className="flex flex-col items-center gap-4">
<div className="w-12 h-12 border-[3px] border-slate-100 border-t-slate-900 rounded-full animate-spin" />
<MonoLabel className="text-[10px] text-slate-400 animate-pulse uppercase tracking-[0.2em]">Establishing Connection</MonoLabel>
</div>
</div>
)}
{browserFrame && !minimal && (
<div className="absolute top-0 left-0 right-0 h-12 bg-slate-50/80 backdrop-blur-md border-b border-slate-200 flex items-center px-6 gap-3 z-10">
<div className="flex gap-2">
<div className="w-3.5 h-3.5 rounded-full bg-slate-200 group-hover:bg-red-200 transition-colors" />
<div className="w-3.5 h-3.5 rounded-full bg-slate-200 group-hover:bg-amber-200 transition-colors" />
<div className="w-3.5 h-3.5 rounded-full bg-slate-200 group-hover:bg-green-200 transition-colors" />
</div>
<div className="flex-1 max-w-md mx-auto bg-white/50 rounded-xl flex items-center justify-center px-4 h-7 border border-slate-200/50">
<span className="text-[11px] text-slate-400 font-bold tracking-tight truncate whitespace-nowrap">
https://klz-cables.com
</span>
</div>
<div className="w-16" />
</div>
)}
{/* Browser Frame */}
{browserFrame && <BrowserChrome url="varnish-cache://secure.klz-cables.com" minimal={minimal} />}
<div
className={cn(
"transition-transform duration-700",
(browserFrame && !minimal) ? "mt-12" : "mt-0",
noScale ? "static" : "origin-top-left absolute left-0 right-0 h-full"
)}
style={{
width: noScale ? '100%' : `${desktopWidth}px`,
transform: noScale ? 'none' : `scale(${scale})`,
height: noScale ? (clipHeight ? `${clipHeight}px` : '100%') : (clipHeight ? `${clipHeight + Math.abs(offsetY)}px` : `${100 / scale}%`),
}}
>
<iframe
src={src}
scrolling={allowScroll ? "yes" : "no"}
{/* Scaled Viewport Container */}
<div className="flex-1 relative overflow-hidden bg-slate-50/50">
<div
className={cn(
"w-full h-full border-none transition-opacity duration-500",
isLoading ? "opacity-0" : "opacity-100"
"transition-all duration-1000 ease-[cubic-bezier(0.23,1,0.32,1)] origin-top-left absolute left-0 right-0",
noScale && "relative w-full h-full"
)}
onLoad={() => setIsLoading(false)}
style={{
transform: `translateY(-${offsetY}px)`,
pointerEvents: allowScroll ? 'auto' : 'none',
width: noScale ? '100%' : `${desktopWidth}px`,
transform: noScale ? 'none' : `scale(${scale})`,
height: noScale ? '100%' : `${100 / scale}%`,
}}
title={title || "Interactive Project Preview"}
/>
>
<iframe
ref={iframeRef}
src={src}
scrolling={allowScroll ? "yes" : "no"}
className={cn(
"w-full border-none transition-all duration-700 no-scrollbar relative z-0",
isLoading ? "opacity-0 scale-95" : "opacity-100 scale-100"
)}
onLoad={(e) => {
setIsLoading(false);
try {
const iframe = e.currentTarget;
if (iframe.contentDocument) {
const style = iframe.contentDocument.createElement('style');
style.textContent = `
*::-webkit-scrollbar { display: none !important; }
* { -ms-overflow-style: none !important; scrollbar-width: none !important; }
body { background: transparent !important; }
`;
iframe.contentDocument.head.appendChild(style);
setTimeout(updateAmbilight, 600);
const onScroll = () => {
requestAnimationFrame(updateAmbilight);
updateScrollState();
};
iframe.contentWindow?.addEventListener('scroll', onScroll, { passive: true });
}
iframe.contentWindow?.addEventListener('wheel', (e) => {
const { deltaY } = e as WheelEvent;
const doc = iframe.contentDocument?.documentElement;
if (!doc) return;
const scrollTop = doc.scrollTop;
const isAtTop = scrollTop <= 0;
const isAtBottom = scrollTop + doc.clientHeight >= doc.scrollHeight - 1;
if ((isAtTop && deltaY < 0) || (isAtBottom && deltaY > 0)) {
window.scrollBy({ top: deltaY, behavior: 'auto' });
}
}, { passive: true });
} catch (err) { }
}}
style={{
transform: `translateY(-${offsetY}px)`,
height: `calc(100% + ${offsetY}px)`,
pointerEvents: allowScroll ? 'auto' : 'none',
width: 'calc(100% + 20px)', // Bleed for seamless edge
marginLeft: '-10px'
}}
title={title || "Project Display"}
/>
</div>
{/* Custom Industrial Scroll Indicator */}
{allowScroll && scrollState.isScrollable && (
<div className="absolute right-4 top-1/2 -translate-y-1/2 w-1 h-32 bg-slate-200/20 rounded-full z-20 backdrop-blur-sm">
<div
className="w-full bg-slate-900 rounded-full transition-all duration-150 ease-out shadow-[0_0_12px_rgba(15,23,42,0.1)]"
style={{
height: '30px',
transform: `translateY(${(() => {
try {
const doc = iframeRef.current?.contentDocument?.documentElement;
if (!doc) return 0;
const scrollPct = doc.scrollTop / (doc.scrollHeight - doc.clientHeight);
return scrollPct * (128 - 30);
} catch (e) { return 0; }
})()}px)`
}}
/>
</div>
)}
</div>
{!allowScroll && !minimal && <div className="absolute inset-0 pointer-events-auto cursor-default z-20" />}
{!allowScroll && <div className="absolute inset-x-0 bottom-0 top-14 pointer-events-auto cursor-default z-20" />}
</div>
<style jsx global>{`