Upgrade Next.js to 15, fix first-load theme bug, add SVG assets

- Upgrade next 14.1.0 → 15.5.9, next-themes 0.2.1 → 0.4.6
- Fix theme not initializing on first load after server restart
- Add SVG logos and favicon, update site-logo to use SVGs
- Create shared Header component with skip-to-main link
- Add portfolio links to bento grid cards
- Improve accessibility: aria-describedby, prefers-reduced-motion
- Fix useFormState → useActionState for React 19 compatibility
- Fix founder-signature icon (use square favicon instead of wide logo)
This commit is contained in:
jwinterm
2026-01-16 21:02:05 -05:00
parent b81bea6297
commit 0d284b5c7d
23 changed files with 1331 additions and 303 deletions
+1 -1
View File
@@ -24,7 +24,7 @@ export const DataGrid = () => {
width="2" height="2" fill="currentColor"
className="text-blue-500 dark:text-blue-400"
>
<motion.animateMotion
<animateMotion
path="M 0 50 H 100"
dur="5s"
repeatCount="indefinite"
+7 -14
View File
@@ -77,14 +77,9 @@ const StreamLine = ({ d, themeMode, onHit, width, index }: StreamLineProps) => {
);
};
export const NebulaField = ({ className, density = 8, themeMode = 'dark' }: NebulaProps) => {
const [mounted, setMounted] = useState(false);
export const NebulaField = ({ className, density = 8, themeMode = 'dark' }: NebulaProps) => {
const monitorControls = useAnimation();
const [monitorColor, setMonitorColor] = useState(themeMode === 'light' ? "text-slate-400" : "text-emerald-500/60");
useEffect(() => {
setMounted(true);
}, []);
// Sync color with theme if no impacts have happened yet
useEffect(() => {
@@ -130,25 +125,23 @@ export const NebulaField = ({ className, density = 8, themeMode = 'dark' }: Nebu
}, [density, themeMode, targetX, targetY]);
const handleHit = useCallback(() => {
const colors = themeMode === 'light'
const colors = themeMode === 'light'
? ["text-slate-400", "text-blue-400", "text-indigo-400", "text-emerald-400"]
: ["text-emerald-500/50", "text-blue-500/50", "text-purple-500/50", "text-cyan-500/50", "text-teal-500/50"];
setMonitorColor(colors[Math.floor(Math.random() * colors.length)]);
monitorControls.start({
scale: [1, 1.1, 1], // Slightly more pronounced pulse
scale: [1, 1.1, 1],
filter: [
"brightness(1) drop-shadow(0 0 0px rgba(0,0,0,0))",
"brightness(1.3) drop-shadow(0 0 12px currentColor)", // Stronger glow
"brightness(1) drop-shadow(0 0 0px rgba(0,0,0,0))",
"brightness(1.3) drop-shadow(0 0 12px currentColor)",
"brightness(1) drop-shadow(0 0 0px rgba(0,0,0,0))"
],
transition: { duration: 1.5, ease: "easeOut" } // Smoother, longer pulse
transition: { duration: 1.5, ease: "easeOut" }
});
}, [monitorControls, themeMode]);
if (!mounted) return null;
return (
<div className={cn("absolute inset-0 overflow-hidden pointer-events-none z-0", className)}>