feat: set up basic project structure and initial styling
This commit initializes the project with a basic Next.js setup, including: - Integration of `tailwindcss` and `tw-animate-css` for styling. - Definition of a comprehensive color palette using CSS variables for both light and dark themes, supporting a theming system. - Implementation of a custom `aurora` keyframe animation. - Configuration of global CSS styles for `border` and `body` elements. - Setup of `next.config.ts` with image optimization, performance enhancements, and security headers (X-Frame-Options, X-Content-Type-Options, Referrer-Policy). - Update of `package.json` to include new dependencies for UI components (`class-variance-authority`, `clsx`, `lucide-react`, `motion`, `tailwind-merge`, `tailwindcss-animate`) and development tools (`@biomejs/biome`, `@tailwindcss/postcss`, `tw-animate-css`). - Refinement of `app/layout.tsx` to include comprehensive metadata for SEO (title, description, keywords, authors, Open Graph, Twitter cards, robots), preconnect/dns-prefetch links for external resources, favicon, manifest, and mobile app meta tags. - Restructuring of `app/page.tsx` to use dynamic imports for various sections (Hero, About, Skills, Experience, Projects, Contact, WorkModalities) to improve performance, and integration of `Squares` component for background effects. This foundational commit prepares the application for further development by establishing a robust and performant base.
This commit is contained in:
1 parent
ca9e70a53e
commit
90cf3c73ad
37 files changed
+3898
-118
No files matched your search
@@ -0,0 +1,181 @@
|
||||
"use client";
|
||||
|
||||
import { useRef, useEffect, useState } from "react";
|
||||
|
||||
interface SquaresProps {
|
||||
direction?: "right" | "left" | "up" | "down" | "diagonal";
|
||||
speed?: number;
|
||||
borderColor?: string;
|
||||
squareSize?: number;
|
||||
hoverFillColor?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function Squares({
|
||||
direction = "right",
|
||||
speed = 1,
|
||||
borderColor = "#333",
|
||||
squareSize = 40,
|
||||
hoverFillColor = "#222",
|
||||
className,
|
||||
}: SquaresProps) {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const requestRef = useRef<number | undefined>(undefined);
|
||||
const numSquaresX = useRef<number | undefined>(undefined);
|
||||
const numSquaresY = useRef<number | undefined>(undefined);
|
||||
const gridOffset = useRef({ x: 0, y: 0 });
|
||||
const [hoveredSquare, setHoveredSquare] = useState<{
|
||||
x: number;
|
||||
y: number;
|
||||
} | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) return;
|
||||
|
||||
// Set canvas background
|
||||
canvas.style.background = "#060606";
|
||||
|
||||
const resizeCanvas = () => {
|
||||
canvas.width = canvas.offsetWidth;
|
||||
canvas.height = canvas.offsetHeight;
|
||||
numSquaresX.current = Math.ceil(canvas.width / squareSize) + 1;
|
||||
numSquaresY.current = Math.ceil(canvas.height / squareSize) + 1;
|
||||
};
|
||||
|
||||
window.addEventListener("resize", resizeCanvas);
|
||||
resizeCanvas();
|
||||
|
||||
const drawGrid = () => {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
const startX = Math.floor(gridOffset.current.x / squareSize) * squareSize;
|
||||
const startY = Math.floor(gridOffset.current.y / squareSize) * squareSize;
|
||||
|
||||
ctx.lineWidth = 0.5;
|
||||
|
||||
for (let x = startX; x < canvas.width + squareSize; x += squareSize) {
|
||||
for (let y = startY; y < canvas.height + squareSize; y += squareSize) {
|
||||
const squareX = x - (gridOffset.current.x % squareSize);
|
||||
const squareY = y - (gridOffset.current.y % squareSize);
|
||||
|
||||
if (
|
||||
hoveredSquare &&
|
||||
Math.floor((x - startX) / squareSize) === hoveredSquare.x &&
|
||||
Math.floor((y - startY) / squareSize) === hoveredSquare.y
|
||||
) {
|
||||
ctx.fillStyle = hoverFillColor;
|
||||
ctx.fillRect(squareX, squareY, squareSize, squareSize);
|
||||
}
|
||||
|
||||
ctx.strokeStyle = borderColor;
|
||||
ctx.strokeRect(squareX, squareY, squareSize, squareSize);
|
||||
}
|
||||
}
|
||||
|
||||
const gradient = ctx.createRadialGradient(
|
||||
canvas.width / 2,
|
||||
canvas.height / 2,
|
||||
0,
|
||||
canvas.width / 2,
|
||||
canvas.height / 2,
|
||||
Math.sqrt(Math.pow(canvas.width, 2) + Math.pow(canvas.height, 2)) / 2
|
||||
);
|
||||
gradient.addColorStop(0, "rgba(6, 6, 6, 0)");
|
||||
gradient.addColorStop(1, "#060606");
|
||||
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
};
|
||||
|
||||
const updateAnimation = () => {
|
||||
const effectiveSpeed = Math.max(speed, 0.1);
|
||||
|
||||
switch (direction) {
|
||||
case "right":
|
||||
gridOffset.current.x =
|
||||
(gridOffset.current.x - effectiveSpeed + squareSize) % squareSize;
|
||||
break;
|
||||
case "left":
|
||||
gridOffset.current.x =
|
||||
(gridOffset.current.x + effectiveSpeed + squareSize) % squareSize;
|
||||
break;
|
||||
case "up":
|
||||
gridOffset.current.y =
|
||||
(gridOffset.current.y + effectiveSpeed + squareSize) % squareSize;
|
||||
break;
|
||||
case "down":
|
||||
gridOffset.current.y =
|
||||
(gridOffset.current.y - effectiveSpeed + squareSize) % squareSize;
|
||||
break;
|
||||
case "diagonal":
|
||||
gridOffset.current.x =
|
||||
(gridOffset.current.x - effectiveSpeed + squareSize) % squareSize;
|
||||
gridOffset.current.y =
|
||||
(gridOffset.current.y - effectiveSpeed + squareSize) % squareSize;
|
||||
break;
|
||||
}
|
||||
|
||||
drawGrid();
|
||||
requestRef.current = requestAnimationFrame(updateAnimation);
|
||||
};
|
||||
|
||||
const handleMouseMove = (event: MouseEvent) => {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const mouseX = event.clientX - rect.left;
|
||||
const mouseY = event.clientY - rect.top;
|
||||
|
||||
const startX = Math.floor(gridOffset.current.x / squareSize) * squareSize;
|
||||
const startY = Math.floor(gridOffset.current.y / squareSize) * squareSize;
|
||||
|
||||
const hoveredSquareX = Math.floor(
|
||||
(mouseX + gridOffset.current.x - startX) / squareSize
|
||||
);
|
||||
const hoveredSquareY = Math.floor(
|
||||
(mouseY + gridOffset.current.y - startY) / squareSize
|
||||
);
|
||||
|
||||
setHoveredSquare({ x: hoveredSquareX, y: hoveredSquareY });
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
setHoveredSquare(null);
|
||||
};
|
||||
|
||||
// Event listeners
|
||||
window.addEventListener("resize", resizeCanvas);
|
||||
canvas.addEventListener("mousemove", handleMouseMove);
|
||||
canvas.addEventListener("mouseleave", handleMouseLeave);
|
||||
|
||||
// Initial setup
|
||||
resizeCanvas();
|
||||
requestRef.current = requestAnimationFrame(updateAnimation);
|
||||
|
||||
// Cleanup
|
||||
return () => {
|
||||
window.removeEventListener("resize", resizeCanvas);
|
||||
canvas.removeEventListener("mousemove", handleMouseMove);
|
||||
canvas.removeEventListener("mouseleave", handleMouseLeave);
|
||||
if (requestRef.current) {
|
||||
cancelAnimationFrame(requestRef.current);
|
||||
}
|
||||
};
|
||||
}, [
|
||||
direction,
|
||||
speed,
|
||||
borderColor,
|
||||
hoverFillColor,
|
||||
hoveredSquare,
|
||||
squareSize,
|
||||
]);
|
||||
|
||||
return (
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
className={`w-full h-full border-none block ${className}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user