diff --git a/app/components/Instructor.tsx b/app/components/Instructor.tsx
new file mode 100644
index 0000000..46ebcad
--- /dev/null
+++ b/app/components/Instructor.tsx
@@ -0,0 +1,50 @@
+'use client'
+import { WavyBackground } from "./ui/wavy-background"
+import { AnimatedTooltip } from "./ui/animated-tooltip";
+
+const instructors = [
+ {
+ id: 1,
+ name: 'Elena Briggs',
+ designation: 'Vocal Coach',
+ image:
+ 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTB8fGF2YXRhcnxlbnwwfHwwfHx8MA%3D%3D&auto=format&fit=crop&w=800&q=60',
+ },
+ {
+ id: 2,
+ name: 'Marcus Reid',
+ designation: 'Guitar Instructor',
+ image:
+ 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=3540&q=80',
+ },
+ {
+ id: 3,
+ name: 'Julia Zhang',
+ designation: 'Piano Teacher',
+ image:
+ 'https://images.unsplash.com/photo-1580489944761-15a19d654956?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8NXx8YXZhdGFyfGVufDB8fDB8fHww&auto=format&fit=crop&w=800&q=60',
+ },
+ {
+ id: 4,
+ name: 'Andre Gomez',
+ designation: 'Drumming Expert',
+ image:
+ 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8Mnx8YXZhdGFyfGVufDB8fDB8fHww&auto=format&fit=crop&w=800&q=60',
+ },
+ ];
+
+function Instructor() {
+ return (
+
+
+ Meet Our Instructors
+ Discover the talented professionals who will guide your musical journey
+
+
+
+ )
+}
+
+export default Instructor
\ No newline at end of file
diff --git a/app/components/ui/animated-tooltip.tsx b/app/components/ui/animated-tooltip.tsx
new file mode 100644
index 0000000..6ea36be
--- /dev/null
+++ b/app/components/ui/animated-tooltip.tsx
@@ -0,0 +1,90 @@
+"use client";
+import Image from "next/image";
+import React, { useState } from "react";
+import {
+ motion,
+ useTransform,
+ AnimatePresence,
+ useMotionValue,
+ useSpring,
+} from "framer-motion";
+
+export const AnimatedTooltip = ({
+ items,
+}: {
+ items: {
+ id: number;
+ name: string;
+ designation: string;
+ image: string;
+ }[];
+}) => {
+ const [hoveredIndex, setHoveredIndex] = useState(null);
+ const springConfig = { stiffness: 100, damping: 5 };
+ const x = useMotionValue(0); // going to set this value on mouse move
+ // rotate the tooltip
+ const rotate = useSpring(
+ useTransform(x, [-100, 100], [-45, 45]),
+ springConfig
+ );
+ // translate the tooltip
+ const translateX = useSpring(
+ useTransform(x, [-100, 100], [-50, 50]),
+ springConfig
+ );
+ const handleMouseMove = (event: any) => {
+ const halfWidth = event.target.offsetWidth / 2;
+ x.set(event.nativeEvent.offsetX - halfWidth); // set the x value, which is then used in transform and rotate
+ };
+
+ return (
+ <>
+ {items.map((item, idx) => (
+ setHoveredIndex(item.id)}
+ onMouseLeave={() => setHoveredIndex(null)}
+ >
+ {hoveredIndex === item.id && (
+
+
+
+
+ {item.name}
+
+ {item.designation}
+
+ )}
+
+
+ ))}
+ >
+ );
+};
diff --git a/app/components/ui/wavy-background.tsx b/app/components/ui/wavy-background.tsx
new file mode 100644
index 0000000..90a070d
--- /dev/null
+++ b/app/components/ui/wavy-background.tsx
@@ -0,0 +1,132 @@
+"use client";
+import { cn } from "@/app/utils/cn";
+import React, { useEffect, useRef, useState } from "react";
+import { createNoise3D } from "simplex-noise";
+
+export const WavyBackground = ({
+ children,
+ className,
+ containerClassName,
+ colors,
+ waveWidth,
+ backgroundFill,
+ blur = 10,
+ speed = "fast",
+ waveOpacity = 0.5,
+ ...props
+}: {
+ children?: any;
+ className?: string;
+ containerClassName?: string;
+ colors?: string[];
+ waveWidth?: number;
+ backgroundFill?: string;
+ blur?: number;
+ speed?: "slow" | "fast";
+ waveOpacity?: number;
+ [key: string]: any;
+}) => {
+ const noise = createNoise3D();
+ let w: number,
+ h: number,
+ nt: number,
+ i: number,
+ x: number,
+ ctx: any,
+ canvas: any;
+ const canvasRef = useRef(null);
+ const getSpeed = () => {
+ switch (speed) {
+ case "slow":
+ return 0.001;
+ case "fast":
+ return 0.002;
+ default:
+ return 0.001;
+ }
+ };
+
+ const init = () => {
+ canvas = canvasRef.current;
+ ctx = canvas.getContext("2d");
+ w = ctx.canvas.width = window.innerWidth;
+ h = ctx.canvas.height = window.innerHeight;
+ ctx.filter = `blur(${blur}px)`;
+ nt = 0;
+ window.onresize = function () {
+ w = ctx.canvas.width = window.innerWidth;
+ h = ctx.canvas.height = window.innerHeight;
+ ctx.filter = `blur(${blur}px)`;
+ };
+ render();
+ };
+
+ const waveColors = colors ?? [
+ "#38bdf8",
+ "#818cf8",
+ "#c084fc",
+ "#e879f9",
+ "#22d3ee",
+ ];
+ const drawWave = (n: number) => {
+ nt += getSpeed();
+ for (i = 0; i < n; i++) {
+ ctx.beginPath();
+ ctx.lineWidth = waveWidth || 50;
+ ctx.strokeStyle = waveColors[i % waveColors.length];
+ for (x = 0; x < w; x += 5) {
+ var y = noise(x / 800, 0.3 * i, nt) * 100;
+ ctx.lineTo(x, y + h * 0.5); // adjust for height, currently at 50% of the container
+ }
+ ctx.stroke();
+ ctx.closePath();
+ }
+ };
+
+ let animationId: number;
+ const render = () => {
+ ctx.fillStyle = backgroundFill || "black";
+ ctx.globalAlpha = waveOpacity || 0.5;
+ ctx.fillRect(0, 0, w, h);
+ drawWave(5);
+ animationId = requestAnimationFrame(render);
+ };
+
+ useEffect(() => {
+ init();
+ return () => {
+ cancelAnimationFrame(animationId);
+ };
+ }, []);
+
+ const [isSafari, setIsSafari] = useState(false);
+ useEffect(() => {
+ // I'm sorry but i have got to support it on safari.
+ setIsSafari(
+ typeof window !== "undefined" &&
+ navigator.userAgent.includes("Safari") &&
+ !navigator.userAgent.includes("Chrome")
+ );
+ }, []);
+
+ return (
+
+ );
+};
diff --git a/app/page.tsx b/app/page.tsx
index 871a1af..677f143 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -4,6 +4,7 @@ import FeaturedCourse from "./components/FeaturedCourse";
import WhyChooseUs from "./components/WhyChooseUs";
import MusicSchoolCards from "./components/MusicSchoolCards";
import UpcomingWebinars from "./components/UpcomingWebinars";
+import Instructor from "./components/Instructor";
export default function Home() {
return (
@@ -13,6 +14,7 @@ export default function Home() {
+
>
);
}
diff --git a/next.config.mjs b/next.config.mjs
index 7b491cb..a4de04c 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -1,6 +1,10 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
output : "export",
+ images : {
+ domains: ['images.unsplash.com'],
+ unoptimized: true,
+ }
};
export default nextConfig;
diff --git a/package-lock.json b/package-lock.json
index 19e2fd2..cdd8489 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -58,6 +58,11 @@
+ "simplex-noise": "^4.0.1",
+
+
+
+
"tailwind-merge": "^2.3.0"
@@ -16789,6 +16794,24 @@
},
+ "node_modules/simplex-noise": {
+
+
+
+ "version": "4.0.1",
+
+
+
+ "resolved": "https://registry.npmjs.org/simplex-noise/-/simplex-noise-4.0.1.tgz",
+
+
+
+ "integrity": "sha512-zl/+bdSqW7HJOQ0oDbxrNYaF4F5ik0i7M6YOYmEoIJNtg16NpvWaTTM1Y7oV/7T0jFljawLgYPS81Uu2rsfo1A=="
+
+
+ },
+
+
"node_modules/slash": {
diff --git a/package.json b/package.json
index f8a17e8..4a7384d 100644
--- a/package.json
+++ b/package.json
@@ -43,6 +43,9 @@
"react-dom": "^18",
+ "simplex-noise": "^4.0.1",
+
+
"tailwind-merge": "^2.3.0"
},
diff --git a/tailwind.config.ts b/tailwind.config.ts
index 95cc26a..ef49966 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -3,10 +3,17 @@ const defaultTheme = require("tailwindcss/defaultTheme");
const colors = require("tailwindcss/colors");
const svgToDataUri = require("mini-svg-data-uri");
+import { ClassValue, clsx } from "clsx";
+import { twMerge } from "tailwind-merge";
+
const {
default: flattenColorPalette,
} = require("tailwindcss/lib/util/flattenColorPalette");
-
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs));
+}
+
const config: Config = {
content: [