diff --git a/next.config.mjs b/next.config.mjs index 4678774..24ad529 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,4 +1,8 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {}; +const nextConfig = { + images: { + domains: ['images.unsplash.com', 'res.cloudinary.com'] + } +}; export default nextConfig; diff --git a/package-lock.json b/package-lock.json index 74093c0..9e52890 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,14 @@ "name": "01music", "version": "0.1.0", "dependencies": { + "clsx": "^2.1.1", + "framer-motion": "^11.3.28", + "mini-svg-data-uri": "^1.4.4", "next": "14.2.5", "react": "^18", - "react-dom": "^18" + "react-dom": "^18", + "simplex-noise": "^4.0.3", + "tailwind-merge": "^2.5.2" }, "devDependencies": { "@types/node": "^20", @@ -1068,6 +1073,14 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2082,6 +2095,30 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/framer-motion": { + "version": "11.3.28", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.3.28.tgz", + "integrity": "sha512-dqhoawipEAjqdv32zbv72sOMJZjol7dROWn7t/FOq23WXJ40O4OUybgnO2ldnuS+3YquSn8xO/KKRavZ+TBVOQ==", + "dependencies": { + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3079,6 +3116,14 @@ "node": ">=8.6" } }, + "node_modules/mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "bin": { + "mini-svg-data-uri": "cli.js" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4076,6 +4121,11 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simplex-noise": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/simplex-noise/-/simplex-noise-4.0.3.tgz", + "integrity": "sha512-qSE2I4AngLQG7BXqoZj51jokT4WUXe8mOBrvfOXpci8+6Yu44+/dD5zqDpOx3Ux792eamTd2lLcI8jqFntk/lg==" + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -4387,6 +4437,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tailwind-merge": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.2.tgz", + "integrity": "sha512-kjEBm+pvD+6eAwzJL2Bi+02/9LFLal1Gs61+QB7HvTfQQ0aXwC5LGT8PEt1gS0CWKktKe6ysPTAy3cBC5MeiIg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "3.4.10", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.10.tgz", diff --git a/package.json b/package.json index 053051d..b778a8d 100644 --- a/package.json +++ b/package.json @@ -9,18 +9,23 @@ "lint": "next lint" }, "dependencies": { + "clsx": "^2.1.1", + "framer-motion": "^11.3.28", + "mini-svg-data-uri": "^1.4.4", + "next": "14.2.5", "react": "^18", "react-dom": "^18", - "next": "14.2.5" + "simplex-noise": "^4.0.3", + "tailwind-merge": "^2.5.2" }, "devDependencies": { - "typescript": "^5", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", + "eslint": "^8", + "eslint-config-next": "14.2.5", "postcss": "^8", "tailwindcss": "^3.4.1", - "eslint": "^8", - "eslint-config-next": "14.2.5" + "typescript": "^5" } } diff --git a/public/courses/blues.jpg b/public/courses/blues.jpg new file mode 100644 index 0000000..3453e54 Binary files /dev/null and b/public/courses/blues.jpg differ diff --git a/public/courses/classical-music.jpg b/public/courses/classical-music.jpg new file mode 100644 index 0000000..6135d59 Binary files /dev/null and b/public/courses/classical-music.jpg differ diff --git a/public/courses/drumming.jpg b/public/courses/drumming.jpg new file mode 100644 index 0000000..d34f1c6 Binary files /dev/null and b/public/courses/drumming.jpg differ diff --git a/public/courses/edm.jpg b/public/courses/edm.jpg new file mode 100644 index 0000000..ed464b1 Binary files /dev/null and b/public/courses/edm.jpg differ diff --git a/public/courses/guitar.jpg b/public/courses/guitar.jpg new file mode 100644 index 0000000..c22f030 Binary files /dev/null and b/public/courses/guitar.jpg differ diff --git a/public/courses/jazz.jpg b/public/courses/jazz.jpg new file mode 100644 index 0000000..60c758f Binary files /dev/null and b/public/courses/jazz.jpg differ diff --git a/public/courses/music-prod.jpg b/public/courses/music-prod.jpg new file mode 100644 index 0000000..d412411 Binary files /dev/null and b/public/courses/music-prod.jpg differ diff --git a/public/courses/piano.jpg b/public/courses/piano.jpg new file mode 100644 index 0000000..d3cbdfd Binary files /dev/null and b/public/courses/piano.jpg differ diff --git a/public/courses/song-writing.jpg b/public/courses/song-writing.jpg new file mode 100644 index 0000000..e802e3a Binary files /dev/null and b/public/courses/song-writing.jpg differ diff --git a/public/courses/vocalist.jpg b/public/courses/vocalist.jpg new file mode 100644 index 0000000..ae981ca Binary files /dev/null and b/public/courses/vocalist.jpg differ diff --git a/src/app/contact/page.tsx b/src/app/contact/page.tsx new file mode 100644 index 0000000..3827cd3 --- /dev/null +++ b/src/app/contact/page.tsx @@ -0,0 +1,62 @@ +'use client'; + +import React, { FormEvent, useState } from 'react'; +import { BackgroundBeams } from '@/components/ui/background-beams'; + +function MusicSchoolContactUs() { + const [email, setEmail] = useState(''); + const [message, setMessage] = useState(''); + + const handleSubmit = (event: FormEvent) => { + event.preventDefault(); + console.log('Submitted:', { email, message }); + }; + + return ( +
+ {' '} + {/* Ensure the container is relative */} + {/* BackgroundBeams with adjusted z-index */} + + {/* Content with higher z-index */} +
+ {' '} + {/* Add relative and z-10 to bring content to the front */} +

+ Contact Us +

+

+ We're here to help with any questions about our courses, + programs, or events. Reach out and let us know how we can assist you + in your musical journey. +

+
+ setEmail(e.target.value)} + placeholder="Your email address" + className="rounded-lg border border-neutral-800 focus:ring-2 focus:ring-teal-500 w-full p-4 bg-neutral-950 placeholder:text-neutral-700" + required + /> + + +
+
+
+ ); +} + +export default MusicSchoolContactUs; \ No newline at end of file diff --git a/src/app/course/page.tsx b/src/app/course/page.tsx new file mode 100644 index 0000000..a04d1c8 --- /dev/null +++ b/src/app/course/page.tsx @@ -0,0 +1,61 @@ +'use client' +import Image from "next/image"; +import React from "react"; +import { CardBody, CardContainer, CardItem } from "@/components/ui/3d-card"; +import courseData from "@/data/music_courses.json" + +function page() { + return ( +
+

All courses ({courseData.courses.length})

+
+ {courseData.courses.map((course) => ( + + + + {course.title} + + + {course.description} + + + {course.title} + +
+ + Try now → + + + Sign up + +
+
+
+ ))} +
+
+ ) +} + +export default page \ No newline at end of file diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 3314e47..d125a2c 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,6 +1,7 @@ import type { Metadata } from "next"; import { Inter } from "next/font/google"; import "./globals.css"; +import NavBar from "@/components/NavBar"; const inter = Inter({ subsets: ["latin"] }); @@ -15,8 +16,15 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - - {children} + + + +
+ +
+ {children} + + ); } diff --git a/src/app/page.tsx b/src/app/page.tsx index 2acfd44..7ad6974 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,113 +1,22 @@ -import Image from "next/image"; +import FeaturedCourses from "@/components/FeaturedCourses"; +import Footer from "@/components/Footer"; +import HeroSection from "@/components/HeroSection"; +import Instructors from "@/components/Instructors"; +import MusicSchoolTestimonials from "@/components/TestimonialCards"; +import UpcomingWebinars from "@/components/UpcomingWebinars"; +import WhyChooseUs from "@/components/WhyChooseUs"; export default function Home() { return ( -
-
-

- Get started by editing  - src/app/page.tsx -

-
- - By{" "} - Vercel Logo - -
-
- -
- Next.js Logo -
- -
- -

- Docs{" "} - - -> - -

-

- Find in-depth information about Next.js features and API. -

-
- - -

- Learn{" "} - - -> - -

-

- Learn about Next.js in an interactive course with quizzes! -

-
- - -

- Templates{" "} - - -> - -

-

- Explore starter templates for Next.js. -

-
- - -

- Deploy{" "} - - -> - -

-

- Instantly deploy your Next.js site to a shareable URL with Vercel. -

-
-
-
+
+ {/*

chai aur code

*/} + + + + + + +
+
); } diff --git a/src/components/FeaturedCourses.tsx b/src/components/FeaturedCourses.tsx new file mode 100644 index 0000000..c9c069f --- /dev/null +++ b/src/components/FeaturedCourses.tsx @@ -0,0 +1,58 @@ +// 'use client'; +import Link from 'next/link' +import courseData from '../data/music_courses.json' +import { BackgroundGradient } from './ui/background-gradient' + + +interface Course{ + id: number, + title: string, + slug: string, + description: string, + price: number, + instructor: string, + isFeatured: boolean, +} + +function FeaturedCourses() { + + const featuredCourses = courseData.courses.filter((course : Course) => course.isFeatured) + + return ( +
+
+
+

Featured Courses

+

Learn With the Best

+
+
+
+
+ {featuredCourses.map((course:Course)=>( +
+ +
+

{course.title}

+

{course.description}

+ + Learn More + +
+
+
+ ))} +
+
+
+ + View All Courses + +
+
+ ) +} + +export default FeaturedCourses \ No newline at end of file diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx new file mode 100644 index 0000000..1f68d21 --- /dev/null +++ b/src/components/Footer.tsx @@ -0,0 +1,88 @@ + + +function Footer() { + return ( + + ) + } + + export default Footer \ No newline at end of file diff --git a/src/components/HeroSection.tsx b/src/components/HeroSection.tsx new file mode 100644 index 0000000..0cc89a6 --- /dev/null +++ b/src/components/HeroSection.tsx @@ -0,0 +1,34 @@ +import Link from "next/link" +import { Spotlight } from "./ui/Spotlight"; +import { Button } from "./ui/moving-border"; + +function HeroSection() { + return ( +
+ +
+

Master the art of music

+

Dive into our comprehensive music courses and transform your musical journey today. Whether you're begginer or looking to refine your skills, join us to unlock your true potential.

+
+ + + +
+
+
+ ) +} + +export default HeroSection \ No newline at end of file diff --git a/src/components/Instructors.tsx b/src/components/Instructors.tsx new file mode 100644 index 0000000..d3ddf32 --- /dev/null +++ b/src/components/Instructors.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 Instructors() { + return ( +
+ +

Meet Our Instructors

+

Discover the talented professionals who will guide your musical journey

+
+ +
+
+
+ ) +} + +export default Instructors \ No newline at end of file diff --git a/src/components/NavBar.tsx b/src/components/NavBar.tsx new file mode 100644 index 0000000..a5b4736 --- /dev/null +++ b/src/components/NavBar.tsx @@ -0,0 +1,37 @@ +"use client"; +import React, { useState } from "react"; +import { HoveredLink, Menu, MenuItem, ProductItem } from "./ui/navbar-menu"; +import { cn } from "@/lib/util"; +import Link from "next/link"; + +function NavBar({ className }: { className?: string }) { + const [active, setActive] = useState(null); + return ( +
+ + + + + + +
+ All Courses + Basic Music Theory + Advanced Composition + Songwriting + Music Production +
+
+ + + + + +
+
+ ) +} + +export default NavBar \ No newline at end of file diff --git a/src/components/TestimonialCards.tsx b/src/components/TestimonialCards.tsx new file mode 100644 index 0000000..a0421d9 --- /dev/null +++ b/src/components/TestimonialCards.tsx @@ -0,0 +1,55 @@ +'use client' + +import { InfiniteMovingCards } from "./ui/infinite-moving-cards"; + +const musicSchoolTestimonials = [ + { + quote: + 'Joining the music school transformed my understanding of music and helped me to truly discover my own sound. The instructors are world-class!', + name: 'Alex Johnson', + title: 'Guitar Student', + }, + { + quote: + "The community and support at this school are unmatched. I've grown not just as a pianist, but also as a performer, thanks to their comprehensive approach.", + name: 'Samantha Lee', + title: 'Piano Student', + }, + { + quote: + "This school offered me the tools and confidence to take my singing to the next level. I'm endlessly grateful for the personalized coaching.", + name: 'Michael Chen', + title: 'Vocal Student', + }, + { + quote: + 'As a violinist, finding the right mentor can be challenging, but this school matched me with a teacher who truly understands my goals and challenges.', + name: 'Emily Taylor', + title: 'Violin Student', + }, + { + quote: + 'The production courses here opened my eyes to the intricacies of music production. Highly recommend for any aspiring producers!', + name: 'Chris Morales', + title: 'Music Production Student', + }, +]; + +function MusicSchoolTestimonials() { + return ( +
+

Hear our Harmony: Voices of success

+
+
+ +
+
+
+ ) +} + +export default MusicSchoolTestimonials \ No newline at end of file diff --git a/src/components/UpcomingWebinars.tsx b/src/components/UpcomingWebinars.tsx new file mode 100644 index 0000000..5b0e561 --- /dev/null +++ b/src/components/UpcomingWebinars.tsx @@ -0,0 +1,85 @@ +'use client' +import Link from "next/link" +import { HoverEffect } from "./ui/card-hover-effects"; + +function UpcomingWebinars() { + + const featuredWebinars = [ + { + title: 'Understanding Music Theory', + description: + 'Dive deep into the fundamentals of music theory and enhance your musical skills.', + slug: 'understanding-music-theory', + isFeatured: true, + }, + { + title: 'The Art of Songwriting', + description: + 'Learn the craft of songwriting from experienced musicians and songwriters.', + slug: 'the-art-of-songwriting', + isFeatured: true, + }, + { + title: 'Mastering Your Instrument', + description: + 'Advanced techniques to master your musical instrument of choice.', + slug: 'mastering-your-instrument', + isFeatured: true, + }, + { + title: 'Music Production Essentials', + description: + 'Get started with music production with this comprehensive overview.', + slug: 'music-production-essentials', + isFeatured: true, + }, + // Added two more webinars + { + title: 'Live Performance Techniques', + description: + 'Enhance your live performance skills with expert tips and strategies.', + slug: 'live-performance-techniques', + isFeatured: true, + }, + { + title: 'Digital Music Marketing', + description: + 'Learn how to promote your music effectively in the digital age.', + slug: 'digital-music-marketing', + isFeatured: true, + }, + ]; + + return ( +
+
+
+

FEATURED WEBINARS

+

Enhance Your Musical Journey

+
+ +
+ ( + { + title: webinar.title, + description: webinar.description, + link: '/' + } + ))} + /> +
+ +
+ + View All webinars + +
+
+
+ ) +} + +export default UpcomingWebinars \ No newline at end of file diff --git a/src/components/WhyChooseUs.tsx b/src/components/WhyChooseUs.tsx new file mode 100644 index 0000000..439e1c2 --- /dev/null +++ b/src/components/WhyChooseUs.tsx @@ -0,0 +1,48 @@ +"use client"; +import React from "react"; +import { StickyScroll } from "./ui/sticky-scroll-reveal"; + +const musicSchoolContent = [ + { + title: 'Discover Your Sound with Us: A Personal Journey in Music Mastery', + description: + 'Embark on a musical journey that’s uniquely yours. Our personalized instruction adapts to your individual needs, setting the stage for unparalleled growth and creativity. At our music school, your aspirations meet our dedicated support, creating a harmonious path to mastery.', + }, + { + title: 'Discover Your Sound with Us: A Personal Journey in Music Mastery', + description: + 'Embark on a musical journey that’s uniquely yours. Our personalized instruction adapts to your individual needs, setting the stage for unparalleled growth and creativity. At our music school, your aspirations meet our dedicated support, creating a harmonious path to mastery.', + }, + { + title: 'Discover Your Sound with Us: A Personal Journey in Music Mastery', + description: + 'Embark on a musical journey that’s uniquely yours. Our personalized instruction adapts to your individual needs, setting the stage for unparalleled growth and creativity. At our music school, your aspirations meet our dedicated support, creating a harmonious path to mastery.', + }, + { + title: 'Live Feedback & Engagement', + description: + 'Immerse yourself in an interactive learning experience where feedback is immediate, just like real-time changes in a collaborative project. This approach enhances your understanding and mastery of music concepts and performance techniques.', + }, + { + title: 'Cutting-Edge Curriculum', + description: + 'Our curriculum is continuously updated to include the latest music education trends and technologies, ensuring you’re always learning with the most current and effective methods. Say goodbye to outdated materials and welcome an education that evolves with the industry.', + }, + { + title: 'Limitless Learning Opportunities', + description: + 'With our expansive resource library and dynamic course offerings, you’ll never find yourself without something new to explore. Our platform provides continuous opportunities for growth, ensuring your musical skills are always advancing.', + }, +]; + + + +function WhyChooseUs() { + return ( +
+ +
+ ) +} + +export default WhyChooseUs \ No newline at end of file diff --git a/src/components/ui/3d-card.tsx b/src/components/ui/3d-card.tsx new file mode 100644 index 0000000..89419f6 --- /dev/null +++ b/src/components/ui/3d-card.tsx @@ -0,0 +1,155 @@ +"use client"; + +import { cn } from "@/lib/util"; +import Image from "next/image"; +import React, { + createContext, + useState, + useContext, + useRef, + useEffect, +} from "react"; + +const MouseEnterContext = createContext< + [boolean, React.Dispatch>] | undefined +>(undefined); + +export const CardContainer = ({ + children, + className, + containerClassName, +}: { + children?: React.ReactNode; + className?: string; + containerClassName?: string; +}) => { + const containerRef = useRef(null); + const [isMouseEntered, setIsMouseEntered] = useState(false); + + const handleMouseMove = (e: React.MouseEvent) => { + if (!containerRef.current) return; + const { left, top, width, height } = + containerRef.current.getBoundingClientRect(); + const x = (e.clientX - left - width / 2) / 25; + const y = (e.clientY - top - height / 2) / 25; + containerRef.current.style.transform = `rotateY(${x}deg) rotateX(${y}deg)`; + }; + + const handleMouseEnter = (e: React.MouseEvent) => { + setIsMouseEntered(true); + if (!containerRef.current) return; + }; + + const handleMouseLeave = (e: React.MouseEvent) => { + if (!containerRef.current) return; + setIsMouseEntered(false); + containerRef.current.style.transform = `rotateY(0deg) rotateX(0deg)`; + }; + return ( + +
+
+ {children} +
+
+
+ ); +}; + +export const CardBody = ({ + children, + className, +}: { + children: React.ReactNode; + className?: string; +}) => { + return ( +
*]:[transform-style:preserve-3d]", + className + )} + > + {children} +
+ ); +}; + +export const CardItem = ({ + as: Tag = "div", + children, + className, + translateX = 0, + translateY = 0, + translateZ = 0, + rotateX = 0, + rotateY = 0, + rotateZ = 0, + ...rest +}: { + as?: React.ElementType; + children: React.ReactNode; + className?: string; + translateX?: number | string; + translateY?: number | string; + translateZ?: number | string; + rotateX?: number | string; + rotateY?: number | string; + rotateZ?: number | string; + [key: string]: any; +}) => { + const ref = useRef(null); + const [isMouseEntered] = useMouseEnter(); + + useEffect(() => { + handleAnimations(); + }, [isMouseEntered]); + + const handleAnimations = () => { + if (!ref.current) return; + if (isMouseEntered) { + ref.current.style.transform = `translateX(${translateX}px) translateY(${translateY}px) translateZ(${translateZ}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) rotateZ(${rotateZ}deg)`; + } else { + ref.current.style.transform = `translateX(0px) translateY(0px) translateZ(0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg)`; + } + }; + + return ( + + {children} + + ); +}; + +// Create a hook to use the context +export const useMouseEnter = () => { + const context = useContext(MouseEnterContext); + if (context === undefined) { + throw new Error("useMouseEnter must be used within a MouseEnterProvider"); + } + return context; +}; diff --git a/src/components/ui/Spotlight.tsx b/src/components/ui/Spotlight.tsx new file mode 100644 index 0000000..05a34c8 --- /dev/null +++ b/src/components/ui/Spotlight.tsx @@ -0,0 +1,56 @@ +import React from "react"; +import { cn } from "@/lib/util"; + +type SpotlightProps = { + className?: string; + fill?: string; +}; + +export const Spotlight = ({ className, fill }: SpotlightProps) => { + return ( + + + + + + + + + + + + + ); +}; diff --git a/src/components/ui/animated-tooltip.tsx b/src/components/ui/animated-tooltip.tsx new file mode 100644 index 0000000..1b76f83 --- /dev/null +++ b/src/components/ui/animated-tooltip.tsx @@ -0,0 +1,92 @@ +"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}
+ + )} + + {item.name} +
+ ))} + + ); +}; diff --git a/src/components/ui/background-beams.tsx b/src/components/ui/background-beams.tsx new file mode 100644 index 0000000..7608e29 --- /dev/null +++ b/src/components/ui/background-beams.tsx @@ -0,0 +1,141 @@ +"use client"; +import React from "react"; +import { motion } from "framer-motion"; +import { cn } from "@/lib/util"; + +export const BackgroundBeams = React.memo( + ({ className }: { className?: string }) => { + const paths = [ + "M-380 -189C-380 -189 -312 216 152 343C616 470 684 875 684 875", + "M-373 -197C-373 -197 -305 208 159 335C623 462 691 867 691 867", + "M-366 -205C-366 -205 -298 200 166 327C630 454 698 859 698 859", + "M-359 -213C-359 -213 -291 192 173 319C637 446 705 851 705 851", + "M-352 -221C-352 -221 -284 184 180 311C644 438 712 843 712 843", + "M-345 -229C-345 -229 -277 176 187 303C651 430 719 835 719 835", + "M-338 -237C-338 -237 -270 168 194 295C658 422 726 827 726 827", + "M-331 -245C-331 -245 -263 160 201 287C665 414 733 819 733 819", + "M-324 -253C-324 -253 -256 152 208 279C672 406 740 811 740 811", + "M-317 -261C-317 -261 -249 144 215 271C679 398 747 803 747 803", + "M-310 -269C-310 -269 -242 136 222 263C686 390 754 795 754 795", + "M-303 -277C-303 -277 -235 128 229 255C693 382 761 787 761 787", + "M-296 -285C-296 -285 -228 120 236 247C700 374 768 779 768 779", + "M-289 -293C-289 -293 -221 112 243 239C707 366 775 771 775 771", + "M-282 -301C-282 -301 -214 104 250 231C714 358 782 763 782 763", + "M-275 -309C-275 -309 -207 96 257 223C721 350 789 755 789 755", + "M-268 -317C-268 -317 -200 88 264 215C728 342 796 747 796 747", + "M-261 -325C-261 -325 -193 80 271 207C735 334 803 739 803 739", + "M-254 -333C-254 -333 -186 72 278 199C742 326 810 731 810 731", + "M-247 -341C-247 -341 -179 64 285 191C749 318 817 723 817 723", + "M-240 -349C-240 -349 -172 56 292 183C756 310 824 715 824 715", + "M-233 -357C-233 -357 -165 48 299 175C763 302 831 707 831 707", + "M-226 -365C-226 -365 -158 40 306 167C770 294 838 699 838 699", + "M-219 -373C-219 -373 -151 32 313 159C777 286 845 691 845 691", + "M-212 -381C-212 -381 -144 24 320 151C784 278 852 683 852 683", + "M-205 -389C-205 -389 -137 16 327 143C791 270 859 675 859 675", + "M-198 -397C-198 -397 -130 8 334 135C798 262 866 667 866 667", + "M-191 -405C-191 -405 -123 0 341 127C805 254 873 659 873 659", + "M-184 -413C-184 -413 -116 -8 348 119C812 246 880 651 880 651", + "M-177 -421C-177 -421 -109 -16 355 111C819 238 887 643 887 643", + "M-170 -429C-170 -429 -102 -24 362 103C826 230 894 635 894 635", + "M-163 -437C-163 -437 -95 -32 369 95C833 222 901 627 901 627", + "M-156 -445C-156 -445 -88 -40 376 87C840 214 908 619 908 619", + "M-149 -453C-149 -453 -81 -48 383 79C847 206 915 611 915 611", + "M-142 -461C-142 -461 -74 -56 390 71C854 198 922 603 922 603", + "M-135 -469C-135 -469 -67 -64 397 63C861 190 929 595 929 595", + "M-128 -477C-128 -477 -60 -72 404 55C868 182 936 587 936 587", + "M-121 -485C-121 -485 -53 -80 411 47C875 174 943 579 943 579", + "M-114 -493C-114 -493 -46 -88 418 39C882 166 950 571 950 571", + "M-107 -501C-107 -501 -39 -96 425 31C889 158 957 563 957 563", + "M-100 -509C-100 -509 -32 -104 432 23C896 150 964 555 964 555", + "M-93 -517C-93 -517 -25 -112 439 15C903 142 971 547 971 547", + "M-86 -525C-86 -525 -18 -120 446 7C910 134 978 539 978 539", + "M-79 -533C-79 -533 -11 -128 453 -1C917 126 985 531 985 531", + "M-72 -541C-72 -541 -4 -136 460 -9C924 118 992 523 992 523", + "M-65 -549C-65 -549 3 -144 467 -17C931 110 999 515 999 515", + "M-58 -557C-58 -557 10 -152 474 -25C938 102 1006 507 1006 507", + "M-51 -565C-51 -565 17 -160 481 -33C945 94 1013 499 1013 499", + "M-44 -573C-44 -573 24 -168 488 -41C952 86 1020 491 1020 491", + "M-37 -581C-37 -581 31 -176 495 -49C959 78 1027 483 1027 483", + ]; + return ( +
+ + + + {paths.map((path, index) => ( + + ))} + + {paths.map((path, index) => ( + + + + + + + ))} + + + + + + + + +
+ ); + } +); + +BackgroundBeams.displayName = "BackgroundBeams"; diff --git a/src/components/ui/background-gradient.tsx b/src/components/ui/background-gradient.tsx new file mode 100644 index 0000000..e38d081 --- /dev/null +++ b/src/components/ui/background-gradient.tsx @@ -0,0 +1,73 @@ +"use client"; +import { cn } from "@/lib/util"; +import React from "react"; +import { motion } from "framer-motion"; + +export const BackgroundGradient = ({ + children, + className, + containerClassName, + animate = true, +}: { + children?: React.ReactNode; + className?: string; + containerClassName?: string; + animate?: boolean; +}) => { + const variants = { + initial: { + backgroundPosition: "0 50%", + }, + animate: { + backgroundPosition: ["0, 50%", "100% 50%", "0 50%"], + }, + }; + return ( +
+ + + +
{children}
+
+ ); +}; diff --git a/src/components/ui/card-hover-effects.tsx b/src/components/ui/card-hover-effects.tsx new file mode 100644 index 0000000..c880abf --- /dev/null +++ b/src/components/ui/card-hover-effects.tsx @@ -0,0 +1,111 @@ +import { cn } from "@/lib//util"; +import { AnimatePresence, motion } from "framer-motion"; +import Link from "next/link"; +import { useState } from "react"; + +export const HoverEffect = ({ + items, + className, +}: { + items: { + title: string; + description: string; + link: string; + }[]; + className?: string; +}) => { + let [hoveredIndex, setHoveredIndex] = useState(null); + + return ( +
+ {items.map((item, idx) => ( + setHoveredIndex(idx)} + onMouseLeave={() => setHoveredIndex(null)} + > + + {hoveredIndex === idx && ( + + )} + + + {item.title} + {item.description} + + + ))} +
+ ); +}; + +export const Card = ({ + className, + children, +}: { + className?: string; + children: React.ReactNode; +}) => { + return ( +
+
+
{children}
+
+
+ ); +}; +export const CardTitle = ({ + className, + children, +}: { + className?: string; + children: React.ReactNode; +}) => { + return ( +

+ {children} +

+ ); +}; +export const CardDescription = ({ + className, + children, +}: { + className?: string; + children: React.ReactNode; +}) => { + return ( +

+ {children} +

+ ); +}; diff --git a/src/components/ui/infinite-moving-cards.tsx b/src/components/ui/infinite-moving-cards.tsx new file mode 100644 index 0000000..b589669 --- /dev/null +++ b/src/components/ui/infinite-moving-cards.tsx @@ -0,0 +1,121 @@ +"use client"; + +import { cn } from "@/lib/util"; +import React, { useEffect, useState } from "react"; + +export const InfiniteMovingCards = ({ + items, + direction = "left", + speed = "fast", + pauseOnHover = true, + className, +}: { + items: { + quote: string; + name: string; + title: string; + }[]; + direction?: "left" | "right"; + speed?: "fast" | "normal" | "slow"; + pauseOnHover?: boolean; + className?: string; +}) => { + const containerRef = React.useRef(null); + const scrollerRef = React.useRef(null); + + useEffect(() => { + addAnimation(); + }, []); + const [start, setStart] = useState(false); + function addAnimation() { + if (containerRef.current && scrollerRef.current) { + const scrollerContent = Array.from(scrollerRef.current.children); + + scrollerContent.forEach((item) => { + const duplicatedItem = item.cloneNode(true); + if (scrollerRef.current) { + scrollerRef.current.appendChild(duplicatedItem); + } + }); + + getDirection(); + getSpeed(); + setStart(true); + } + } + const getDirection = () => { + if (containerRef.current) { + if (direction === "left") { + containerRef.current.style.setProperty( + "--animation-direction", + "forwards" + ); + } else { + containerRef.current.style.setProperty( + "--animation-direction", + "reverse" + ); + } + } + }; + const getSpeed = () => { + if (containerRef.current) { + if (speed === "fast") { + containerRef.current.style.setProperty("--animation-duration", "20s"); + } else if (speed === "normal") { + containerRef.current.style.setProperty("--animation-duration", "40s"); + } else { + containerRef.current.style.setProperty("--animation-duration", "80s"); + } + } + }; + return ( +
+
    + {items.map((item, idx) => ( +
  • +
    + + + {item.quote} + +
    + + + {item.name} + + + {item.title} + + +
    +
    +
  • + ))} +
+
+ ); +}; diff --git a/src/components/ui/moving-border.tsx b/src/components/ui/moving-border.tsx new file mode 100644 index 0000000..edf3773 --- /dev/null +++ b/src/components/ui/moving-border.tsx @@ -0,0 +1,139 @@ +"use client"; +import React from "react"; +import { + motion, + useAnimationFrame, + useMotionTemplate, + useMotionValue, + useTransform, +} from "framer-motion"; +import { useRef } from "react"; +import { cn } from "@/lib/util"; + +export function Button({ + borderRadius = "1.75rem", + children, + as: Component = "button", + containerClassName, + borderClassName, + duration, + className, + ...otherProps +}: { + borderRadius?: string; + children: React.ReactNode; + as?: any; + containerClassName?: string; + borderClassName?: string; + duration?: number; + className?: string; + [key: string]: any; +}) { + return ( + +
+ +
+ +
+ +
+ {children} +
+ + ); +} + +export const MovingBorder = ({ + children, + duration = 2000, + rx, + ry, + ...otherProps +}: { + children: React.ReactNode; + duration?: number; + rx?: string; + ry?: string; + [key: string]: any; +}) => { + const pathRef = useRef(); + const progress = useMotionValue(0); + + useAnimationFrame((time) => { + const length = pathRef.current?.getTotalLength(); + if (length) { + const pxPerMillisecond = length / duration; + progress.set((time * pxPerMillisecond) % length); + } + }); + + const x = useTransform( + progress, + (val) => pathRef.current?.getPointAtLength(val).x + ); + const y = useTransform( + progress, + (val) => pathRef.current?.getPointAtLength(val).y + ); + + const transform = useMotionTemplate`translateX(${x}px) translateY(${y}px) translateX(-50%) translateY(-50%)`; + + return ( + <> + + + + + {children} + + + ); +}; diff --git a/src/components/ui/navbar-menu.tsx b/src/components/ui/navbar-menu.tsx new file mode 100644 index 0000000..feb347c --- /dev/null +++ b/src/components/ui/navbar-menu.tsx @@ -0,0 +1,121 @@ +"use client"; +import React from "react"; +import { motion } from "framer-motion"; +import Link from "next/link"; +import Image from "next/image"; + +const transition = { + type: "spring", + mass: 0.5, + damping: 11.5, + stiffness: 100, + restDelta: 0.001, + restSpeed: 0.001, +}; + +export const MenuItem = ({ + setActive, + active, + item, + children, +}: { + setActive: (item: string) => void; + active: string | null; + item: string; + children?: React.ReactNode; +}) => { + return ( +
setActive(item)} className="relative "> + + {item} + + {active !== null && children && ( + + {active === item && ( +
+ + + {children} + + +
+ )} +
+ )} +
+ ); +}; + +export const Menu = ({ + setActive, + children, +}: { + setActive: (item: string | null) => void; + children: React.ReactNode; +}) => { + return ( + + ); +}; + +export const ProductItem = ({ + title, + description, + href, + src, +}: { + title: string; + description: string; + href: string; + src: string; +}) => { + return ( + + {title} +
+

+ {title} +

+

+ {description} +

+
+ + ); +}; + +export const HoveredLink = ({ children, ...rest }: any) => { + return ( + + {children} + + ); +}; diff --git a/src/components/ui/sticky-scroll-reveal.tsx b/src/components/ui/sticky-scroll-reveal.tsx new file mode 100644 index 0000000..6dc77ae --- /dev/null +++ b/src/components/ui/sticky-scroll-reveal.tsx @@ -0,0 +1,112 @@ +"use client"; +import React, { useEffect, useRef, useState } from "react"; +import { useMotionValueEvent, useScroll } from "framer-motion"; +import { motion } from "framer-motion"; +import { cn } from "@/lib/util"; + +export const StickyScroll = ({ + content, + contentClassName, +}: { + content: { + title: string; + description: string; + content?: React.ReactNode | any; + }[]; + contentClassName?: string; +}) => { + const [activeCard, setActiveCard] = React.useState(0); + const ref = useRef(null); + const { scrollYProgress } = useScroll({ + // uncomment line 22 and comment line 23 if you DONT want the overflow container and want to have it change on the entire page scroll + // target: ref + container: ref, + offset: ["start start", "end start"], + }); + const cardLength = content.length; + + useMotionValueEvent(scrollYProgress, "change", (latest) => { + const cardsBreakpoints = content.map((_, index) => index / cardLength); + const closestBreakpointIndex = cardsBreakpoints.reduce( + (acc, breakpoint, index) => { + const distance = Math.abs(latest - breakpoint); + if (distance < Math.abs(latest - cardsBreakpoints[acc])) { + return index; + } + return acc; + }, + 0 + ); + setActiveCard(closestBreakpointIndex); + }); + + const backgroundColors = [ + "var(--slate-900)", + "var(--black)", + "var(--neutral-900)", + ]; + const linearGradients = [ + "linear-gradient(to bottom right, var(--cyan-500), var(--emerald-500))", + "linear-gradient(to bottom right, var(--pink-500), var(--indigo-500))", + "linear-gradient(to bottom right, var(--orange-500), var(--yellow-500))", + ]; + + const [backgroundGradient, setBackgroundGradient] = useState( + linearGradients[0] + ); + + useEffect(() => { + setBackgroundGradient(linearGradients[activeCard % linearGradients.length]); + }, [activeCard]); + + return ( + +
+
+ {content.map((item, index) => ( +
+ + {item.title} + + + {item.description} + +
+ ))} +
+
+
+
+ {content[activeCard].content ?? null} +
+ + ); +}; diff --git a/src/components/ui/wavy-background.tsx b/src/components/ui/wavy-background.tsx new file mode 100644 index 0000000..386dc66 --- /dev/null +++ b/src/components/ui/wavy-background.tsx @@ -0,0 +1,132 @@ +"use client"; +import { cn } from "@/lib/util"; +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 ( +
+ +
+ {children} +
+
+ ); +}; diff --git a/src/data/music_courses.json b/src/data/music_courses.json new file mode 100644 index 0000000..526ce0c --- /dev/null +++ b/src/data/music_courses.json @@ -0,0 +1,105 @@ +{ + "courses": [ + { + "id": 1, + "title": "Guitar Fundamentals", + "slug": "guitar-fundamentals", + "description": "Learn the basics of playing guitar with our comprehensive beginner's course.", + "price": 99.99, + "instructor": "John Doe", + "isFeatured": true, + "image": "/courses/guitar.jpg" + }, + { + "id": 2, + "title": "Piano for Beginners", + "slug": "piano-for-beginners", + "description": "Start your musical journey with foundational piano skills taught by expert instructors.", + "price": 109.99, + "instructor": "Jane Smith", + "isFeatured": false, + "image": "/courses/piano.jpg" + }, + { + "id": 3, + "title": "Advanced Vocal Techniques", + "slug": "advanced-vocal-techniques", + "description": "Enhance your singing with advanced vocal techniques for intermediate to advanced learners.", + "price": 119.99, + "instructor": "Emily Johnson", + "isFeatured": true, + "image": "/courses/vocalist.jpg" + }, + { + "id": 4, + "title": "Drumming Mastery", + "slug": "drumming-mastery", + "description": "Master the drums with our comprehensive course covering all skill levels.", + "price": 129.99, + "instructor": "Mike Brown", + "isFeatured": false, + "image": "/courses/drumming.jpg" + }, + { + "id": 5, + "title": "Jazz Improvisation", + "slug": "jazz-improvisation", + "description": "Learn the art of jazz improvisation with this course designed for all levels.", + "price": 139.99, + "instructor": "Chris Davis", + "isFeatured": false, + "image": "/courses/jazz.jpg" + }, + { + "id": 6, + "title": "Music Production Fundamentals", + "slug": "music-production-fundamentals", + "description": "Dive into music production with this introductory course on the basics of sound engineering and mixing.", + "price": 149.99, + "instructor": "Alex Wilson", + "isFeatured": true, + "image": "/courses/music-prod.jpg" + }, + { + "id": 7, + "title": "Songwriting Essentials", + "slug": "songwriting-essentials", + "description": "Learn the essentials of songwriting to express your musical creativity.", + "price": 159.99, + "instructor": "Samantha Miller", + "isFeatured": false, + "image": "/courses/song-writing.jpg" + }, + { + "id": 8, + "title": "Electronic Music Production", + "slug": "electronic-music-production", + "description": "Create compelling electronic music with our course designed for beginners to advanced users.", + "price": 169.99, + "instructor": "Luke Harris", + "isFeatured": true, + "image": "/courses/music-prod.jpg" + }, + { + "id": 9, + "title": "Classical Music History", + "slug": "classical-music-history", + "description": "Explore the rich history of classical music from its origins to the present day.", + "price": 179.99, + "instructor": "Grace Lee", + "isFeatured": false, + "image": "/courses/classical-music.jpg" + }, + { + "id": 10, + "title": "Blues Guitar Techniques", + "slug": "blues-guitar-techniques", + "description": "Discover the techniques of blues guitar to add soul and depth to your playing.", + "price": 189.99, + "instructor": "Ethan Moore", + "isFeatured": true, + "image": "/courses/blues.jpg" + } + ] + } + \ No newline at end of file diff --git a/src/lib/util.ts b/src/lib/util.ts new file mode 100644 index 0000000..cec6ac9 --- /dev/null +++ b/src/lib/util.ts @@ -0,0 +1,6 @@ +import { ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/tailwind.config.ts b/tailwind.config.ts index e9a0944..85c4309 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,20 +1,78 @@ import type { Config } from "tailwindcss"; +const svgToDataUri = require("mini-svg-data-uri"); +const colors = require("tailwindcss/colors"); +const { + default: flattenColorPalette, +} = require("tailwindcss/lib/util/flattenColorPalette"); + +// Plugin to add each Tailwind color as a global CSS variable +function addVariablesForColors({ addBase, theme }: any) { + const allColors = flattenColorPalette(theme('colors')); + const newVars = Object.fromEntries( + Object.entries(allColors).map(([key, value]) => [`--${key}`, value]) + ); + + addBase({ + ':root': newVars, + }); +} + +function addSvgPatterns({ matchUtilities, theme }: any) { + matchUtilities( + { + 'bg-grid': (value: any) => ({ + backgroundImage: `url("${svgToDataUri( + `` + )}")`, + }), + 'bg-grid-small': (value: any) => ({ + backgroundImage: `url("${svgToDataUri( + `` + )}")`, + }), + 'bg-dot': (value: any) => ({ + backgroundImage: `url("${svgToDataUri( + `` + )}")`, + }), + }, + { values: flattenColorPalette(theme('backgroundColor')), type: 'color' } + ); +} + + const config: Config = { content: [ "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", "./src/components/**/*.{js,ts,jsx,tsx,mdx}", "./src/app/**/*.{js,ts,jsx,tsx,mdx}", ], + darkMode: 'class', theme: { extend: { + animation:{ + spotlight: "spotlight 2s ease .75s 1 forwards", + scroll: "scroll var(--animation-duration, 40s) var(--animation-direction, forwards) linear infinite" + }, backgroundImage: { "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", "gradient-conic": "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", }, + keyframes:{ + spotlight: { + '0%': { opacity: '0', transform: 'translate(-72%, -62%) scale(0.5)' }, + '100%': { opacity: '1', transform: 'translate(-50%,-40%) scale(1)' }, + }, + scroll: { + to: { + transform: "translate(calc(-50% - 0.5rem))", + }, + }, + } }, }, - plugins: [], + plugins: [addVariablesForColors, addSvgPatterns], }; -export default config; +export default config; \ No newline at end of file