diff --git a/components/Approach.tsx b/components/Approach.tsx
new file mode 100644
index 0000000..4ae51a6
--- /dev/null
+++ b/components/Approach.tsx
@@ -0,0 +1,141 @@
+"use client";
+import React from "react";
+
+import { AnimatePresence, motion } from "framer-motion";
+import { CanvasRevealEffect } from "./ui/CanvasRevealEffect";
+
+const Approach = () => {
+ return (
+
+
+ Project Approach
+
+
+
}
+ description="I initiate projects by gathering user requirements, conducting research and designing the applications architecture and user experience. Using Github Projects I organise tasks into boards, plan sprints and track progress, ensuring a streamlined approach to development"
+ >
+
+
+
}
+ description="I bring projects to life by crafting responsive front-end interfaces and handling back-end logic and databases. I manage code reviews, collaborate with team members and ensure smooth integration of features"
+ >
+
+ {/* Radial gradient for the cute fade */}
+
+
+
}
+ >
+
+
+
+
+ );
+};
+
+const Card = ({
+ title,
+ icon,
+ children,
+ description,
+}: {
+ title: string;
+ icon: React.ReactNode;
+ children?: React.ReactNode;
+ description: string;
+}) => {
+ const [hovered, setHovered] = React.useState(false);
+ return (
+
setHovered(true)}
+ onMouseLeave={() => setHovered(false)}
+ className="border border-black/[0.2] group/canvas-card flex items-center justify-center dark:border-white/[0.2] max-w-sm w-full mx-auto p-4 relative lg:h-[35rem] rounded-3xl"
+ >
+
+
+
+
+
+
+ {hovered && (
+
+ {children}
+
+ )}
+
+
+
+
+ {icon}
+
+
+ {title}
+
+
+ {description}
+
+
+
+ );
+};
+
+const AceternityIcon = ({ order }: { order: string }) => {
+ return (
+
+
+
+
+ {order}
+
+
+
+ );
+};
+
+export const Icon = ({ className, ...rest }: any) => {
+ return (
+
+
+
+ );
+};
+
+export default Approach;
diff --git a/components/ui/CanvasRevealEffect.tsx b/components/ui/CanvasRevealEffect.tsx
new file mode 100644
index 0000000..8cd8be3
--- /dev/null
+++ b/components/ui/CanvasRevealEffect.tsx
@@ -0,0 +1,308 @@
+"use client";
+import { cn } from "../../utils/cn";
+import { Canvas, useFrame, useThree } from "@react-three/fiber";
+import React, { useMemo, useRef } from "react";
+import * as THREE from "three";
+
+export const CanvasRevealEffect = ({
+ animationSpeed = 0.4,
+ opacities = [0.3, 0.3, 0.3, 0.5, 0.5, 0.5, 0.8, 0.8, 0.8, 1],
+ colors = [[0, 255, 255]],
+ containerClassName,
+ dotSize,
+ showGradient = true,
+}: {
+ /**
+ * 0.1 - slower
+ * 1.0 - faster
+ */
+ animationSpeed?: number;
+ opacities?: number[];
+ colors?: number[][];
+ containerClassName?: string;
+ dotSize?: number;
+ showGradient?: boolean;
+}) => {
+ return (
+
+
+
+
+ {showGradient && (
+
+ )}
+
+ );
+};
+
+interface DotMatrixProps {
+ colors?: number[][];
+ opacities?: number[];
+ totalSize?: number;
+ dotSize?: number;
+ shader?: string;
+ center?: ("x" | "y")[];
+}
+
+const DotMatrix: React.FC = ({
+ colors = [[0, 0, 0]],
+ opacities = [0.04, 0.04, 0.04, 0.04, 0.04, 0.08, 0.08, 0.08, 0.08, 0.14],
+ totalSize = 4,
+ dotSize = 2,
+ shader = "",
+ center = ["x", "y"],
+}) => {
+ const uniforms = React.useMemo(() => {
+ let colorsArray = [
+ colors[0],
+ colors[0],
+ colors[0],
+ colors[0],
+ colors[0],
+ colors[0],
+ ];
+ if (colors.length === 2) {
+ colorsArray = [
+ colors[0],
+ colors[0],
+ colors[0],
+ colors[1],
+ colors[1],
+ colors[1],
+ ];
+ } else if (colors.length === 3) {
+ colorsArray = [
+ colors[0],
+ colors[0],
+ colors[1],
+ colors[1],
+ colors[2],
+ colors[2],
+ ];
+ }
+
+ return {
+ u_colors: {
+ value: colorsArray.map((color) => [
+ color[0] / 255,
+ color[1] / 255,
+ color[2] / 255,
+ ]),
+ type: "uniform3fv",
+ },
+ u_opacities: {
+ value: opacities,
+ type: "uniform1fv",
+ },
+ u_total_size: {
+ value: totalSize,
+ type: "uniform1f",
+ },
+ u_dot_size: {
+ value: dotSize,
+ type: "uniform1f",
+ },
+ };
+ }, [colors, opacities, totalSize, dotSize]);
+
+ return (
+
+ );
+};
+
+type Uniforms = {
+ [key: string]: {
+ value: number[] | number[][] | number;
+ type: string;
+ };
+};
+const ShaderMaterial = ({
+ source,
+ uniforms,
+ maxFps = 60,
+}: {
+ source: string;
+ hovered?: boolean;
+ maxFps?: number;
+ uniforms: Uniforms;
+}) => {
+ const { size } = useThree();
+ const ref = useRef();
+ let lastFrameTime = 0;
+
+ useFrame(({ clock }) => {
+ if (!ref.current) return;
+ const timestamp = clock.getElapsedTime();
+ if (timestamp - lastFrameTime < 1 / maxFps) {
+ return;
+ }
+ lastFrameTime = timestamp;
+
+ const material: any = ref.current.material;
+ const timeLocation = material.uniforms.u_time;
+ timeLocation.value = timestamp;
+ });
+
+ const getUniforms = () => {
+ const preparedUniforms: any = {};
+
+ for (const uniformName in uniforms) {
+ const uniform: any = uniforms[uniformName];
+
+ switch (uniform.type) {
+ case "uniform1f":
+ preparedUniforms[uniformName] = { value: uniform.value, type: "1f" };
+ break;
+ case "uniform3f":
+ preparedUniforms[uniformName] = {
+ value: new THREE.Vector3().fromArray(uniform.value),
+ type: "3f",
+ };
+ break;
+ case "uniform1fv":
+ preparedUniforms[uniformName] = { value: uniform.value, type: "1fv" };
+ break;
+ case "uniform3fv":
+ preparedUniforms[uniformName] = {
+ value: uniform.value.map((v: number[]) =>
+ new THREE.Vector3().fromArray(v)
+ ),
+ type: "3fv",
+ };
+ break;
+ case "uniform2f":
+ preparedUniforms[uniformName] = {
+ value: new THREE.Vector2().fromArray(uniform.value),
+ type: "2f",
+ };
+ break;
+ default:
+ console.error(`Invalid uniform type for '${uniformName}'.`);
+ break;
+ }
+ }
+
+ preparedUniforms["u_time"] = { value: 0, type: "1f" };
+ preparedUniforms["u_resolution"] = {
+ value: new THREE.Vector2(size.width * 2, size.height * 2),
+ }; // Initialize u_resolution
+ return preparedUniforms;
+ };
+
+ // Shader material
+ const material = useMemo(() => {
+ const materialObject = new THREE.ShaderMaterial({
+ vertexShader: `
+ precision mediump float;
+ in vec2 coordinates;
+ uniform vec2 u_resolution;
+ out vec2 fragCoord;
+ void main(){
+ float x = position.x;
+ float y = position.y;
+ gl_Position = vec4(x, y, 0.0, 1.0);
+ fragCoord = (position.xy + vec2(1.0)) * 0.5 * u_resolution;
+ fragCoord.y = u_resolution.y - fragCoord.y;
+ }
+ `,
+ fragmentShader: source,
+ uniforms: getUniforms(),
+ glslVersion: THREE.GLSL3,
+ blending: THREE.CustomBlending,
+ blendSrc: THREE.SrcAlphaFactor,
+ blendDst: THREE.OneFactor,
+ });
+
+ return materialObject;
+ }, [size.width, size.height, source]);
+
+ return (
+
+
+
+
+ );
+};
+
+const Shader: React.FC = ({ source, uniforms, maxFps = 60 }) => {
+ return (
+
+
+
+ );
+};
+interface ShaderProps {
+ source: string;
+ uniforms: {
+ [key: string]: {
+ value: number[] | number[][] | number;
+ type: string;
+ };
+ };
+ maxFps?: number;
+}
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 8919139..df0e968 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -7,6 +7,7 @@ import RecentProjects from "../../components/RecentProjects";
import { navItems } from "../../data";
import Clients from "../../components/Clients";
import Experience from "../../components/Experience";
+import Approach from "../../components/Approach";
export default function Home() {
return (
@@ -18,6 +19,7 @@ export default function Home() {
+
);