Compare commits

...

6 Commits

Author SHA1 Message Date
29f83f8151 chore: release 2.2.89
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 30s
Build & Deploy / 🏗️ Build (push) Successful in 3m30s
Build & Deploy / 🧪 QA (push) Successful in 1m45s
Build & Deploy / 🚀 Deploy (push) Successful in 35s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 1m13s
Build & Deploy / 🔔 Notify (push) Successful in 4s
2026-06-29 21:04:03 +02:00
cec08e3ab3 fix(perf): dynamic import of framer-motion features to eliminate 43 KiB unused JS 2026-06-29 21:04:01 +02:00
837567c1f4 chore: release 2.2.88
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 30s
Build & Deploy / 🚀 Deploy (push) Successful in 35s
Build & Deploy / 🧪 QA (push) Successful in 1m45s
Build & Deploy / 🏗️ Build (push) Successful in 3m23s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 1m11s
Build & Deploy / 🔔 Notify (push) Successful in 3s
2026-06-29 18:58:32 +02:00
4dcc0061fc fix(perf): throttle CorporateBackground mousemove to fix forced reflow penalty 2026-06-29 18:58:32 +02:00
bd25ec935d chore: release 2.2.87
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 28s
Build & Deploy / 🚀 Deploy (push) Successful in 34s
Build & Deploy / 🧪 QA (push) Successful in 1m45s
Build & Deploy / 🏗️ Build (push) Successful in 3m23s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 1m9s
Build & Deploy / 🔔 Notify (push) Successful in 3s
2026-06-29 18:23:54 +02:00
b3b85202eb fix(perf): replace next/image with native img for svg to drastically improve LCP 2026-06-29 18:23:53 +02:00
4 changed files with 40 additions and 28 deletions

View File

@@ -88,13 +88,12 @@ export function InteractiveGermanyMap({
const mapBackground = useMemo(() => (
<div className="absolute inset-0 opacity-[0.25] mix-blend-screen drop-shadow-2xl brightness-200 pointer-events-none transform-gpu">
<Image
<img
src="/germany-map.svg"
alt={locale === 'en' ? "Map of Germany" : "Deutschlandkarte"}
fill
className="object-cover"
priority
className="w-full h-full object-cover"
fetchPriority="high"
decoding="async"
/>
</div>
), [locale]);

View File

@@ -1,12 +1,14 @@
'use client';
import React from 'react';
import { m, AnimatePresence, LazyMotion, domAnimation } from 'framer-motion';
import { m, AnimatePresence, LazyMotion } from 'framer-motion';
import { MapPin, CheckCircle2, ArrowUpRight } from 'lucide-react';
import Image from 'next/image';
import Link from 'next/link';
import { Location } from '@/lib/map-data';
const loadFeatures = () => import('@/lib/framer-features').then(res => res.default);
const MinorNode = React.memo(({ loc, isActive, onEnter, onLeave }: { loc: Location, isActive: boolean, onEnter: (loc: Location) => void, onLeave: () => void }) => (
<div
className={`absolute group/minor cursor-pointer w-8 h-8 md:w-5 md:h-5 flex items-center justify-center ${isActive ? 'z-[60]' : 'z-10 hover:z-30'}`}
@@ -76,7 +78,7 @@ export default function InteractiveMapPins({
handleMouseLeave: () => void
}) {
return (
<LazyMotion features={domAnimation}>
<LazyMotion features={loadFeatures}>
{locations.filter(l => l.type === 'minor_node').map((loc, idx) => (
<MinorNode
key={`minor-${loc.id || idx}`}

View File

@@ -25,34 +25,45 @@ function MagneticRing({
// Only run on client with mouse
if (window.matchMedia("(pointer: coarse)").matches) return;
let ticking = false;
const handleMouseMove = (e: MouseEvent) => {
if (!ref.current) return;
const rect = ref.current.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
if (!ticking) {
window.requestAnimationFrame(() => {
if (!ref.current) {
ticking = false;
return;
}
const rect = ref.current.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
const distX = e.clientX - centerX;
const distY = e.clientY - centerY;
const distance = Math.sqrt(distX * Math.pow(distX, 2) + Math.pow(distY, 2)); // Fix: correct pythagoras
const distX = e.clientX - centerX;
const distY = e.clientY - centerY;
const actualDistance = Math.sqrt(distX * distX + distY * distY);
// Calculate actual distance correctly
const actualDistance = Math.sqrt(distX * distX + distY * distY);
if (actualDistance < pullRadius) {
// Exponential falloff for natural magnetic feel
const pullFactor = Math.pow(1 - (actualDistance / pullRadius), 2);
// Target offset (fraction of the distance based on strength)
x.set(distX * pullFactor * (pullStrength / 100));
y.set(distY * pullFactor * (pullStrength / 100));
} else {
// Return to origin smoothly
x.set(0);
y.set(0);
if (actualDistance < pullRadius) {
// Exponential falloff for natural magnetic feel
const pullFactor = Math.pow(1 - (actualDistance / pullRadius), 2);
// Target offset (fraction of the distance based on strength)
x.set(distX * pullFactor * (pullStrength / 100));
y.set(distY * pullFactor * (pullStrength / 100));
} else {
// Return to origin smoothly
x.set(0);
y.set(0);
}
ticking = false;
});
ticking = true;
}
};
window.addEventListener('mousemove', handleMouseMove);
window.addEventListener('mousemove', handleMouseMove, { passive: true });
return () => window.removeEventListener('mousemove', handleMouseMove);
}, [x, y, pullRadius, pullStrength]);

View File

@@ -139,7 +139,7 @@
"prepare": "husky",
"preinstall": "npx only-allow pnpm"
},
"version": "2.2.86",
"version": "2.2.89",
"pnpm": {
"onlyBuiltDependencies": [
"@parcel/watcher",