├── .env.example ├── .eslintrc.json ├── .github └── FUNDING.yml ├── .gitignore ├── Image Prompts.txt ├── LICENSE.md ├── README.md ├── jsconfig.json ├── next.config.mjs ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── audio │ └── birds39-forest-20772.mp3 ├── background │ ├── about-background.png │ ├── contact-background.png │ ├── home-background.png │ └── projects-background.png ├── models │ ├── hat-transformed.glb │ ├── staff-transformed.glb │ └── wizard-transformed.glb ├── next.svg ├── resume.pdf └── vercel.svg ├── src ├── app │ ├── (sub pages) │ │ ├── about │ │ │ └── page.js │ │ ├── contact │ │ │ └── page.js │ │ ├── layout.js │ │ └── projects │ │ │ └── page.js │ ├── data.js │ ├── favicon.ico │ ├── globals.css │ ├── layout.js │ └── page.js └── components │ ├── FireFliesBackground.jsx │ ├── HomeBtn.jsx │ ├── RenderModel.jsx │ ├── ResponsiveComponent.jsx │ ├── Sound.jsx │ ├── about │ ├── ItemLayout.jsx │ └── index.jsx │ ├── contact │ └── Form.jsx │ ├── hooks │ └── useScreenSize.jsx │ ├── models │ ├── HatModel.jsx │ ├── Staff.jsx │ └── Wizard.jsx │ ├── navigation │ ├── NavButton.jsx │ └── index.jsx │ └── projects │ ├── ProjectLayout.jsx │ └── index.jsx └── tailwind.config.js /.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_SERVICE_ID="Enter your service id here" 2 | NEXT_PUBLIC_TEMPLATE_ID="Enter your template id here" 3 | NEXT_PUBLIC_PUBLIC_KEY="Enter your public key here" 4 | 5 | # Following env variables should be replaced with your own hosted github stats 6 | 7 | NEXT_PUBLIC_GITHUB_STATS_URL="https://github-readme-stats.vercel.app" 8 | NEXT_PUBLIC_GITHUB_STREAK_STATS_URL="https://github-readme-streak-stats.herokuapp.com" 9 | 10 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [codebucks27] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | thanks_dev: # Replace with a single thanks.dev username 15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /Image Prompts.txt: -------------------------------------------------------------------------------- 1 | Site: https://playground.com/ 2 | 3 | Default Configuration: 4 | 5 | - Model: Stable Diffusion XL 6 | 7 | - Filter: RealVis XL 8 | 9 | - Image Dimentions: 1024*576 (16:9) 10 | 11 | - Quality & Details: 30 steps 12 | 13 | -------------------------------------------------- 14 | 15 | Home Page Background 16 | 17 | - Prompt: "An enchanted forest at night, under a starlit sky. The scene is dark by mystical glows from luminescent flowers, exotic mushrooms, and arcane symbols on ancient trees. The atmosphere is serene, with a magical aura conveyed through ethereal particles and soft glimmers of light floating in the air. The color scheme should focus on dark tones with highlights in mild yellow and gold, creating a magical and inviting scene that blends the beauty of an astral night with the mystery of a magical forest." 18 | 19 | - Seed: 656140970 20 | 21 | 22 | -------------------------------------------------- 23 | 24 | About Page Background 25 | 26 | - Prompt: "mystical forest, dawn, luminescent plants, mist, ancient trees, tranquil, magical ambiance." 27 | 28 | - Seed: 568754894 29 | 30 | 31 | -------------------------------------------------- 32 | 33 | Projects Page Background 34 | 35 | - Prompt: "An enchanted workshop at night, nestled within a forest clearing. The workshop is illuminated by glowing crystals and lanterns, casting soft lights on intricate machinery and floating spellbooks. Workbenches are cluttered with magical tools, potions, and ancient maps. The backdrop is a blend of natural forest elements and magical innovation, suggesting a space where magical projects and creations come to life." 36 | 37 | - Seed: 949988405 38 | 39 | 40 | -------------------------------------------------- 41 | 42 | Contact Page Background 43 | 44 | - Prompt: "Twilight forest, ancient stone archway, glowing runes, magical flora, shimmering leaves, ethereal wisps, ambient light." 45 | 46 | - Seed: 225378614 47 | 48 | 49 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024-present CODEBUCKS. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Next.js Creative Portfolio Tutorial: Build Amazing Portfolio Website with Next.js, Three.js, and Tailwind CSS 🔥 2 | 3 | ![GitHub stars](https://img.shields.io/github/stars/codebucks27/Next.js-Creative-Portfolio-Website?style=social&logo=ApacheSpark&label=Stars)   4 | ![GitHub forks](https://img.shields.io/github/forks/codebucks27/Next.js-Creative-Portfolio-Website?style=social&logo=KashFlow&maxAge=3600)   5 | ![Github Followers](https://img.shields.io/github/followers/codebucks27.svg?style=social&label=Follow)  
6 | 7 | This repository contains **final code** for Next.js Creative Portfolio website built using Next.js and Three.js.
8 | 9 | For Demo checkout following link👇:
10 | [Nextjs Creative Portfolio Website Demo](https://next-js-creative-portfolio-website.vercel.app/)
11 | 12 | Starter Code Files👇:
13 | ➡ Link 💚: [Nextjs Creative Portfolio Website Starter Code](https://github.com/codebucks27/Nextjs-Creative-Portfolio-Starter-Code-Files)
14 | 15 | 16 | If you want to learn how to create it please follow below tutorial👇:
17 | ➡ Tutorial Link 💚: [Personal Portfolio Website with Next.js, Three.js & Tailwind CSS Tutorial](https://youtu.be/T5t46vuW8fo) 18 | [![YouTube Video Views](https://img.shields.io/youtube/views/T5t46vuW8fo 19 | )](https://youtu.be/T5t46vuW8fo)
20 | 21 | 💚 Checkout my personal website [DevDreaming](https://devdreaming.com)
22 | 23 | --- 24 | # ⭐DO NOT FORGET TO STAR THIS REPO⭐ 25 | --- 26 | 27 | ## Images of The Portfolio Website: 28 | 29 | #### Home 30 | ![Nextjs Creative Portfolio Website](https://github.com/codebucks27/Nextjs-Creative-Portfolio-Starter-Code-Files/blob/main/website%20images/Home-desktop.png) 31 | 32 | #### About 33 | ![Nextjs Creative Portfolio Website About Page](https://github.com/codebucks27/Nextjs-Creative-Portfolio-Starter-Code-Files/blob/main/website%20images/About-desktop-full.png) 34 | 35 | #### Projects 36 | ![Next.js Creative Portfolio Website Projects Page](https://github.com/codebucks27/Nextjs-Creative-Portfolio-Starter-Code-Files/blob/main/website%20images/Projects-desktop.png) 37 | 38 | #### Contact 39 | ![Next.js Creative Portfolio Website Contact Page](https://github.com/codebucks27/Nextjs-Creative-Portfolio-Starter-Code-Files/blob/main/website%20images/Contact-desktop.png) 40 | 41 | #### Mobile Version 42 | ![Next.js Creative Portfolio Website Contact Page](https://github.com/codebucks27/Nextjs-Creative-Portfolio-Starter-Code-Files/blob/main/website%20images/Home-mobile.png) 43 | ![Next.js Creative Portfolio Website Contact Page](https://github.com/codebucks27/Nextjs-Creative-Portfolio-Starter-Code-Files/blob/main/website%20images/About-mobile.png) 44 | ![Next.js Creative Portfolio Website Contact Page](https://github.com/codebucks27/Nextjs-Creative-Portfolio-Starter-Code-Files/blob/main/website%20images/Projects-mobile.png) 45 | ![Next.js Creative Portfolio Website Contact Page](https://github.com/codebucks27/Nextjs-Creative-Portfolio-Starter-Code-Files/blob/main/website%20images/Contact-mobile.png) 46 | 47 | ## Resources Used in This Project 48 | 49 | #### 3D Models 50 | 51 | - ["Tim Mckee - Boy Wizard"](https://skfb.ly/6YATu) by [elbertwithane is licensed under Creative Commons Attribution ](http://creativecommons.org/licenses/by/4.0/). 52 | - ["Stylized wizard hat"](https://skfb.ly/ozxOQ) by [Enkarra is licensed under Creative Commons Attribution](http://creativecommons.org/licenses/by/4.0/). 53 | - ["Wizard Staff"](https://skfb.ly/6QYZw) by [Toymancer Studio is licensed under Creative Commons Attribution](http://creativecommons.org/licenses/by/4.0/). 54 | 55 | #### AI Images 56 | 57 | - Created with the help of [Playground AI](https://playgroundai.com/) 58 | 59 | #### Github Stats & Details 60 | 61 | - [Github ReadMe Stats](https://github.com/anuraghazra/github-readme-stats) 62 | - [Skills Icons](https://github.com/tandpfun/skill-icons) 63 | - [Github Readme Streak Stats](https://github.com/denvercoder1/github-readme-streak-stats) 64 | 65 | #### Development Resources 66 | 67 | - Fonts from [Google Fonts](https://fonts.google.com/)
68 | - Icons from [Lucide Icons](https://lucide.dev/)
69 | - Notifications from [Sonner](https://sonner.emilkowal.ski/)
70 | - Form created using [react-hook-form](https://react-hook-form.com/)
71 | - Animations using [framer-motion](https://www.framer.com/motion/)
72 | - Emails using [Emailjs](https://www.emailjs.com/)
73 | - Convert 3d models to JSX using [Gltf JSX](https://github.com/pmndrs/gltfjsx) 74 | 75 | #### Audio 76 | 77 | - Music by Shiden Beats Music from Pixabay 78 | 79 | --- 80 | 81 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 82 | 83 | ## Getting Started 84 | 85 | First, install the dependencies and run the development server: 86 | 87 | ```bash 88 | npm run install # to install all dependencies 89 | 90 | npm run dev 91 | # or 92 | yarn dev 93 | # or 94 | pnpm dev 95 | # or 96 | bun dev 97 | ``` 98 | 99 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 100 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "@/*": ["./src/*"] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-creative-portfolio", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@emailjs/browser": "^4.2.0", 13 | "@react-three/drei": "^9.99.5", 14 | "@react-three/fiber": "^8.15.16", 15 | "clsx": "^2.1.0", 16 | "framer-motion": "^11.0.8", 17 | "lucide-react": "^0.344.0", 18 | "next": "14.2.10", 19 | "react": "^18", 20 | "react-dom": "^18", 21 | "react-hook-form": "^7.51.0", 22 | "sharp": "^0.33.2", 23 | "sonner": "^1.4.3", 24 | "three": "^0.162.0" 25 | }, 26 | "devDependencies": { 27 | "autoprefixer": "^10.0.1", 28 | "eslint": "^8", 29 | "eslint-config-next": "14.1.1", 30 | "postcss": "^8", 31 | "tailwindcss": "^3.3.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /public/audio/birds39-forest-20772.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebucks27/Next.js-Creative-Portfolio-Website/cff35d71760fe35797e675a15073d17307d81e72/public/audio/birds39-forest-20772.mp3 -------------------------------------------------------------------------------- /public/background/about-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebucks27/Next.js-Creative-Portfolio-Website/cff35d71760fe35797e675a15073d17307d81e72/public/background/about-background.png -------------------------------------------------------------------------------- /public/background/contact-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebucks27/Next.js-Creative-Portfolio-Website/cff35d71760fe35797e675a15073d17307d81e72/public/background/contact-background.png -------------------------------------------------------------------------------- /public/background/home-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebucks27/Next.js-Creative-Portfolio-Website/cff35d71760fe35797e675a15073d17307d81e72/public/background/home-background.png -------------------------------------------------------------------------------- /public/background/projects-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebucks27/Next.js-Creative-Portfolio-Website/cff35d71760fe35797e675a15073d17307d81e72/public/background/projects-background.png -------------------------------------------------------------------------------- /public/models/hat-transformed.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebucks27/Next.js-Creative-Portfolio-Website/cff35d71760fe35797e675a15073d17307d81e72/public/models/hat-transformed.glb -------------------------------------------------------------------------------- /public/models/staff-transformed.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebucks27/Next.js-Creative-Portfolio-Website/cff35d71760fe35797e675a15073d17307d81e72/public/models/staff-transformed.glb -------------------------------------------------------------------------------- /public/models/wizard-transformed.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebucks27/Next.js-Creative-Portfolio-Website/cff35d71760fe35797e675a15073d17307d81e72/public/models/wizard-transformed.glb -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/resume.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebucks27/Next.js-Creative-Portfolio-Website/cff35d71760fe35797e675a15073d17307d81e72/public/resume.pdf -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/(sub pages)/about/page.js: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import bg from "../../../../public/background/about-background.png"; 3 | import RenderModel from "@/components/RenderModel"; 4 | // import HatModel from "@/components/models/HatModel"; 5 | import AboutDetails from "@/components/about"; 6 | import dynamic from "next/dynamic"; 7 | const HatModel = dynamic(() => import("@/components/models/HatModel"), { 8 | ssr: false, 9 | }); 10 | 11 | export const metadata = { 12 | title: "About", 13 | }; 14 | 15 | export default function Home() { 16 | return ( 17 | <> 18 | Next.js Portfolio website's about page background image 25 | 26 |
27 | 28 | 29 | 30 |
31 | 32 |
33 |
34 |

35 | CodeBucks 36 |

37 |

38 | Meet the wizard behind this portfolio 39 |

40 |
41 |
42 | 43 | 44 | 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /src/app/(sub pages)/contact/page.js: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import bg from "../../../../public/background/contact-background.png"; 3 | import Form from "@/components/contact/Form"; 4 | 5 | export const metadata = { 6 | title: "Contact", 7 | }; 8 | 9 | export default function Contact() { 10 | return ( 11 | <> 12 | Next.js Portfolio website's contact page background image 19 | 20 |
21 |
22 |

23 | summon the wizard 24 |

25 |

26 | Step into the circle of enchantment and weave your words into the 27 | fabric of the cosmos. Whether you seek to conjure collaborations, 28 | unlock mysteries, or simply share tales of adventure, your messages 29 | are treasured scrolls within this realm. Use the form below to send 30 | your missives through the ethereal network, and await the whisper of 31 | magic in response. 32 |

33 |
34 |
35 |
36 | 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /src/app/(sub pages)/layout.js: -------------------------------------------------------------------------------- 1 | import HomeBtn from "@/components/HomeBtn"; 2 | 3 | export default function SubPagesLayout({ children }) { 4 | return ( 5 |
6 | 7 | {children} 8 |
9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /src/app/(sub pages)/projects/page.js: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import bg from "../../../../public/background/projects-background.png"; 3 | import ProjectList from "@/components/projects"; 4 | import { projectsData } from "../../data"; 5 | import RenderModel from "@/components/RenderModel"; 6 | // import Staff from "@/components/models/Staff"; 7 | import dynamic from "next/dynamic"; 8 | 9 | const Staff = dynamic(() => import("@/components/models/Staff"), { 10 | ssr: false, 11 | }); 12 | 13 | export const metadata = { 14 | title: "Projects", 15 | }; 16 | 17 | export default function Home() { 18 | return ( 19 | <> 20 | Next.js Portfolio website's about page background image 27 | 28 | 29 | 30 |
31 | 32 | 33 | 34 |
35 | 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /src/app/data.js: -------------------------------------------------------------------------------- 1 | /* 2 | Websites: 3 | 4 | - https://github.com/pmndrs/gltfjsx (GLTF JSX for 3D Models) 5 | - https://lucide.dev/icons/ (Lucide Icons) 6 | - https://github.com/anuraghazra/github-readme-stats (Github Readme Stats) 7 | - https://skillicons.dev (Skill Icons to show skills) 8 | - https://github-readme-streak-stats.herokuapp.com (Github Readme Streak Stats) 9 | 10 | :root { 11 | --background: 27 27 27; 12 | --foreground: 225 225 225; 13 | --muted: 115 115 115; 14 | --accent: 254 254 91; #FEFE5B 15 | } 16 | 17 | */ 18 | 19 | export const projectsData = [ 20 | { 21 | id: 1, 22 | name: "EcoTracker", 23 | description: "Track your carbon footprint", 24 | date: "2022-08-15", 25 | demoLink: "https://ecotracker.example.com", 26 | }, 27 | { 28 | id: 2, 29 | name: "ArtGallery Online", 30 | description: "Digital art showcase platform", 31 | date: "2022-06-20", 32 | demoLink: "https://artgalleryonline.example.com", 33 | }, 34 | { 35 | id: 3, 36 | name: "BudgetPlanner", 37 | description: "Plan and track expenses", 38 | date: "2022-09-10", 39 | demoLink: "https://budgetplanner.example.com", 40 | }, 41 | { 42 | id: 4, 43 | name: "HealthBeat", 44 | description: "Monitor heart rate zones", 45 | date: "2022-05-30", 46 | demoLink: "https://healthbeat.example.com", 47 | }, 48 | { 49 | id: 5, 50 | name: "RecipeFinder", 51 | description: "Discover new recipes", 52 | date: "2022-07-12", 53 | demoLink: "https://recipefinder.example.com", 54 | }, 55 | { 56 | id: 6, 57 | name: "JourneyLogger", 58 | description: "Log your travels", 59 | date: "2022-10-01", 60 | demoLink: "https://journeylogger.example.com", 61 | }, 62 | { 63 | id: 7, 64 | name: "StudyBuddy", 65 | description: "Collaborative learning platform", 66 | date: "2022-04-18", 67 | demoLink: "https://studybuddy.example.com", 68 | }, 69 | { 70 | id: 8, 71 | name: "TechTalk", 72 | description: "Tech news aggregator", 73 | date: "2022-11-05", 74 | demoLink: "https://techtalk.example.com", 75 | }, 76 | { 77 | id: 9, 78 | name: "FitTrack", 79 | description: "Fitness and workout tracker", 80 | date: "2022-03-22", 81 | demoLink: "https://fittrack.example.com", 82 | }, 83 | { 84 | id: 10, 85 | name: "MindfulMoments", 86 | description: "Meditation and mindfulness app", 87 | date: "2022-02-14", 88 | demoLink: "https://mindfulmoments.example.com", 89 | }, 90 | ]; 91 | 92 | export const BtnList = [ 93 | { label: "Home", link: "/", icon: "home", newTab: false }, 94 | { label: "About", link: "/about", icon: "about", newTab: false }, 95 | { label: "Projects", link: "/projects", icon: "projects", newTab: false }, 96 | { label: "Contact", link: "/contact", icon: "contact", newTab: false }, 97 | { 98 | label: "Github", 99 | link: "https://www.github.com/codebucks27", 100 | icon: "github", 101 | newTab: true, 102 | }, 103 | { 104 | label: "LinkedIn", 105 | link: "https://www.linkedin.com/in/codebucks", 106 | icon: "linkedin", 107 | newTab: true, 108 | }, 109 | { 110 | label: "X", 111 | link: "https://www.x.com/code_bucks", 112 | icon: "twitter", 113 | newTab: true, 114 | }, 115 | { 116 | label: "Resume", 117 | link: "/resume.pdf", 118 | icon: "resume", 119 | newTab: true, 120 | }, 121 | ]; 122 | 123 | 124 | -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebucks27/Next.js-Creative-Portfolio-Website/cff35d71760fe35797e675a15073d17307d81e72/src/app/favicon.ico -------------------------------------------------------------------------------- /src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --background: 27 27 27; 8 | --foreground: 225 225 225; 9 | --muted: 115 115 115; 10 | --accent: 254 254 91; 11 | /* hex value of rgb(254,254,91) color is #FEFE5B */ 12 | } 13 | } 14 | 15 | @layer utilities { 16 | .pause { 17 | animation-play-state: paused; 18 | } 19 | 20 | .custom-bg { 21 | @apply bg-background/20 border border-accent/30 border-solid backdrop-blur-[6px] shadow-glass-inset hover:shadow-glass-sm; 22 | } 23 | } 24 | 25 | @keyframes move { 26 | 0% { 27 | transform: translate(0, 0); 28 | } 29 | 100% { 30 | transform: translate(100px, 100px); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/app/layout.js: -------------------------------------------------------------------------------- 1 | import { Inter } from "next/font/google"; 2 | import "./globals.css"; 3 | import clsx from "clsx"; 4 | import FireFliesBackground from "@/components/FireFliesBackground"; 5 | import Sound from "@/components/Sound"; 6 | 7 | const inter = Inter({ 8 | subsets: ["latin"], 9 | variable: "--font-inter", 10 | }); 11 | 12 | export const metadata = { 13 | title: { 14 | template: 15 | "Next.js Portfolio Created with Three.js and Tailwind CSS | %s | CodeBucks", 16 | default: 17 | "Next.js Portfolio Created with Three.js and Tailwind CSS by CodeBucks", 18 | }, 19 | description: 20 | "A unique creative portfolio designed by CodeBucks with cutting-edge technologies like Next.js, Tailwind CSS, Three.js, and Framer Motion. Experience the art of modern web development firsthand. Checkout CodeBucks on youtube.", 21 | }; 22 | 23 | export default function RootLayout({ children }) { 24 | return ( 25 | 26 | 32 | {children} 33 | 34 | 35 |
36 | 37 | 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /src/app/page.js: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import bg from "../../public/background/home-background.png"; 3 | import RenderModel from "@/components/RenderModel"; 4 | // import Wizard from "@/components/models/Wizard"; 5 | import Navigation from "@/components/navigation"; 6 | 7 | import dynamic from "next/dynamic"; 8 | const Wizard = dynamic(() => import("@/components/models/Wizard"), { 9 | ssr: false, 10 | }); 11 | 12 | export default function Home() { 13 | return ( 14 |
15 | background-image 23 | 24 |
25 | 26 | 27 | 28 | 29 |
30 |
31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /src/components/FireFliesBackground.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React, { useEffect, useState } from "react"; 3 | 4 | const createFirefly = () => ({ 5 | id: Math.random(), 6 | top: `${Math.random() * 100}%`, 7 | left: `${Math.random() * 100}%`, 8 | animationDuration: `${Math.random() * 5 + 5}s`, 9 | }); 10 | 11 | const FireFliesBackground = () => { 12 | const [fireflies, setFireflies] = useState([]); 13 | 14 | useEffect(() => { 15 | const addFireflyPeriodically = () => { 16 | const newFirefly = createFirefly(); 17 | setFireflies((currentFireflies) => [ 18 | ...currentFireflies.slice(-14), 19 | newFirefly, 20 | ]); 21 | }; 22 | 23 | const interval = setInterval(addFireflyPeriodically, 1000); 24 | 25 | return () => clearInterval(interval); 26 | }, []); 27 | 28 | return ( 29 |
30 | {fireflies.map((firefly) => { 31 | return ( 32 |
41 | ); 42 | })} 43 |
44 | ); 45 | }; 46 | 47 | export default FireFliesBackground; 48 | -------------------------------------------------------------------------------- /src/components/HomeBtn.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { motion } from "framer-motion"; 3 | import { Home } from "lucide-react"; 4 | import Link from "next/link"; 5 | 6 | const NavLink = motion(Link); 7 | const HomeBtn = () => { 8 | return ( 9 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | Home 29 | 30 | 31 | Go to Home Page 32 | 33 | ); 34 | }; 35 | 36 | export default HomeBtn; 37 | -------------------------------------------------------------------------------- /src/components/RenderModel.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Environment } from "@react-three/drei"; 3 | import { Canvas } from "@react-three/fiber"; 4 | import clsx from "clsx"; 5 | import React, { Suspense } from "react"; 6 | 7 | const RenderModel = ({ children, className }) => { 8 | return ( 9 | 15 | {children} 16 | 17 | 18 | ); 19 | }; 20 | 21 | export default RenderModel; 22 | -------------------------------------------------------------------------------- /src/components/ResponsiveComponent.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | import useScreenSize from "./hooks/useScreenSize"; 5 | 6 | const ResponsiveComponent = ({ children }) => { 7 | const size = useScreenSize(); 8 | 9 | return <>{children({ size })}; 10 | }; 11 | 12 | export default ResponsiveComponent; 13 | -------------------------------------------------------------------------------- /src/components/Sound.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { motion } from "framer-motion"; 3 | import { Volume2, VolumeX } from "lucide-react"; 4 | import React, { useEffect, useRef, useState } from "react"; 5 | import { createPortal } from "react-dom"; 6 | 7 | const Modal = ({ onClose, toggle }) => { 8 | return createPortal( 9 |
10 |
15 |

Do you like to play the background music?

16 |
17 | 23 | 29 |
30 |
31 |
, 32 | 33 | document.getElementById("my-modal") 34 | ); 35 | }; 36 | 37 | const Sound = () => { 38 | const audioRef = useRef(null); 39 | const [isPlaying, setIsPlaying] = useState(false); 40 | const [showModal, setShowModal] = useState(false); 41 | 42 | const handleFirstUserInteraction = () => { 43 | const musicConsent = localStorage.getItem("musicConsent"); 44 | if (musicConsent === "true" && !isPlaying) { 45 | audioRef.current.play(); 46 | setIsPlaying(true); 47 | } 48 | 49 | ["click", "keydown", "touchstart"].forEach((event) => 50 | document.removeEventListener(event, handleFirstUserInteraction) 51 | ); 52 | }; 53 | 54 | useEffect(() => { 55 | const consent = localStorage.getItem("musicConsent"); 56 | const consentTime = localStorage.getItem("consentTime"); 57 | 58 | if ( 59 | consent && 60 | consentTime && 61 | new Date(consentTime).getTime() + 3 * 24 * 60 * 60 * 1000 > new Date() 62 | ) { 63 | setIsPlaying(consent === "true"); 64 | 65 | if (consent === "true") { 66 | ["click", "keydown", "touchstart"].forEach((event) => 67 | document.addEventListener(event, handleFirstUserInteraction) 68 | ); 69 | } 70 | } else { 71 | setShowModal(true); 72 | } 73 | }, []); 74 | 75 | const toggle = () => { 76 | const newState = !isPlaying; 77 | setIsPlaying(!isPlaying); 78 | newState ? audioRef.current.play() : audioRef.current.pause(); 79 | localStorage.setItem("musicConsent", String(newState)); 80 | localStorage.setItem("consentTime", new Date().toISOString()); 81 | setShowModal(false); 82 | }; 83 | return ( 84 |
85 | {showModal && ( 86 | setShowModal(false)} toggle={toggle} /> 87 | )} 88 | 89 | 93 | 102 | {isPlaying ? ( 103 | 107 | ) : ( 108 | 112 | )} 113 | 114 |
115 | ); 116 | }; 117 | 118 | export default Sound; 119 | -------------------------------------------------------------------------------- /src/components/about/ItemLayout.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { motion } from "framer-motion"; 3 | import clsx from "clsx"; 4 | 5 | const ItemLayout = ({ children, className }) => { 6 | return ( 7 | 17 | {children} 18 | 19 | ); 20 | }; 21 | 22 | export default ItemLayout; 23 | -------------------------------------------------------------------------------- /src/components/about/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ItemLayout from "./ItemLayout"; 3 | import Link from "next/link"; 4 | 5 | const AboutDetails = () => { 6 | return ( 7 |
8 |
9 | 14 |

15 | Architect of Enchantment 16 |

17 |

18 | My journey in web development is powered by an array of mystical 19 | tools and languages, with JavaScript casting the core of my 20 | enchantments. I wield frameworks like React.js and Next.js with 21 | precision, crafting seamless portals (websites) that connect realms 22 | (users) across the digital universe. The ancient arts of the 23 | Jamstack empower me to create fast, secure, and dynamic experiences, 24 | while my design skills ensure every creation is not only functional 25 | but visually captivating. Join me as I continue to explore new 26 | spells and technologies to shape the future of the web. 27 |

28 |
29 | 30 | 33 |

34 | 25+ clients 35 |

36 |
37 | 38 | 41 |

42 | 4+{" "} 43 | years of experience 44 |

45 |
46 | 47 | 50 | CodeBucks 56 | 57 | 58 | 59 | CodeBucks 65 | 66 | 67 | 68 | CodeBucks 74 | 75 | 76 | 77 | CodeBucks 83 | 84 | 85 | 86 | 91 | CodeBucks 97 | 98 | 99 |
100 |
101 | ); 102 | }; 103 | 104 | export default AboutDetails; 105 | -------------------------------------------------------------------------------- /src/components/contact/Form.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React from "react"; 3 | import { useForm } from "react-hook-form"; 4 | import emailjs from "@emailjs/browser"; 5 | import { Toaster, toast } from "sonner"; 6 | import { motion } from "framer-motion"; 7 | 8 | const container = { 9 | hidden: { opacity: 0 }, 10 | show: { 11 | opacity: 1, 12 | transition: { 13 | staggerChildren: 0.3, 14 | delayChildren: 0.2, 15 | }, 16 | }, 17 | }; 18 | 19 | const item = { 20 | hidden: { scale: 0 }, 21 | show: { scale: 1 }, 22 | }; 23 | 24 | export default function Form() { 25 | const { 26 | register, 27 | handleSubmit, 28 | formState: { errors }, 29 | } = useForm(); 30 | 31 | const sendEmail = (params) => { 32 | const toastId = toast.loading("Sending your message, please wait..."); 33 | 34 | toast.info( 35 | "Form submissions are demo-only here. Please checkout the final code repo to enable it. If you want to connect you can reach out to me via codebucks27@gmail.com.", 36 | { 37 | id: toastId, 38 | } 39 | ); 40 | 41 | // comment out the above toast.info and uncomment the below code to enable emailjs 42 | 43 | // emailjs 44 | // .send( 45 | // process.env.NEXT_PUBLIC_SERVICE_ID, 46 | // process.env.NEXT_PUBLIC_TEMPLATE_ID, 47 | // params, 48 | // { 49 | // publicKey: process.env.NEXT_PUBLIC_PUBLIC_KEY, 50 | // limitRate: { 51 | // throttle: 5000, // you can not send more then 1 email per 5 seconds 52 | // }, 53 | // } 54 | // ) 55 | // .then( 56 | // () => { 57 | // toast.success( 58 | // "I have received your message, I will get back to you soon!", 59 | // { 60 | // id: toastId, 61 | // } 62 | // ); 63 | // }, 64 | // (error) => { 65 | // // console.log("FAILED...", error.text); 66 | // toast.error( 67 | // "There was an error sending your message, please try again later!", 68 | // { 69 | // id: toastId, 70 | // } 71 | // ); 72 | // } 73 | // ); 74 | }; 75 | 76 | const onSubmit = (data) => { 77 | const templateParams = { 78 | to_name: "CodeBucks", 79 | from_name: data.name, 80 | reply_to: data.email, 81 | message: data.message, 82 | }; 83 | 84 | sendEmail(templateParams); 85 | }; 86 | 87 | return ( 88 | <> 89 | 90 | 97 | 110 | {errors.name && ( 111 | 112 | {errors.name.message} 113 | 114 | )} 115 | 122 | {errors.email && ( 123 | 124 | {errors.email.message} 125 | 126 | )} 127 | 143 | {errors.message && ( 144 | 145 | {errors.message.message} 146 | 147 | )} 148 | 149 | 157 | 158 | 159 | ); 160 | } 161 | -------------------------------------------------------------------------------- /src/components/hooks/useScreenSize.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useEffect, useState } from "react"; 4 | 5 | const useScreenSize = () => { 6 | const [screenSize, setScreenSize] = useState(); 7 | 8 | useEffect(() => { 9 | function getScreenSize() { 10 | return window.innerWidth; 11 | } 12 | 13 | function handleResize() { 14 | setScreenSize(getScreenSize()); 15 | } 16 | 17 | handleResize(); 18 | 19 | window.addEventListener("resize", handleResize); 20 | 21 | return () => window.removeEventListener("resize", handleResize); 22 | }, []); 23 | 24 | return screenSize; 25 | }; 26 | 27 | export default useScreenSize; 28 | -------------------------------------------------------------------------------- /src/components/models/HatModel.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | Auto-generated by: https://github.com/pmndrs/gltfjsx 3 | */ 4 | "use client"; 5 | import React, { useRef } from "react"; 6 | import { useGLTF } from "@react-three/drei"; 7 | import { useFrame } from "@react-three/fiber"; 8 | 9 | const HatModel = React.memo(function HatModel(props) { 10 | // Use React.memo for performance optimization 11 | const { nodes, materials } = useGLTF("/models/hat-transformed.glb"); 12 | 13 | const modelRef = useRef(); 14 | 15 | useFrame(() => { 16 | modelRef.current.rotation.y += 0.007; 17 | }); 18 | return ( 19 | 27 | 35 | 36 | ); 37 | }); 38 | 39 | export default HatModel; 40 | useGLTF.preload("/models/hat-transformed.glb"); 41 | -------------------------------------------------------------------------------- /src/components/models/Staff.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | Auto-generated by: https://github.com/pmndrs/gltfjsx 3 | */ 4 | "use client"; 5 | import React, { useRef } from "react"; 6 | import { useGLTF } from "@react-three/drei"; 7 | import { useFrame } from "@react-three/fiber"; 8 | 9 | const Staff = React.memo(function Staff(props) { 10 | // Use React.memo for performance optimization 11 | const { nodes, materials } = useGLTF("/models/staff-transformed.glb"); 12 | const modelRef = useRef(); 13 | 14 | useFrame(() => { 15 | modelRef.current.rotation.y += 0.007; 16 | }); 17 | 18 | return ( 19 | 26 | 35 | 44 | 53 | 62 | 71 | 72 | ); 73 | }); 74 | 75 | export default Staff; 76 | useGLTF.preload("/models/staff-transformed.glb"); 77 | -------------------------------------------------------------------------------- /src/components/models/Wizard.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | Auto-generated by: https://github.com/pmndrs/gltfjsx 3 | */ 4 | "use client"; 5 | import React, { useRef } from "react"; 6 | import { useGLTF } from "@react-three/drei"; 7 | import { useFrame } from "@react-three/fiber"; 8 | 9 | const Wizard = React.memo(function Wizard(props) { 10 | // Use React.memo for performance optimization 11 | const { nodes, materials } = useGLTF("/models/wizard-transformed.glb"); 12 | 13 | const modelRef = useRef(); 14 | 15 | useFrame((state) => { 16 | modelRef.current.position.y = 17 | -1.5 + Math.sin(state.clock.elapsedTime) * 0.15; 18 | }); 19 | 20 | return ( 21 | 29 | 38 | 47 | 56 | 65 | 74 | 83 | 92 | 101 | 110 | 119 | 128 | 137 | 146 | 155 | 164 | 173 | 182 | 191 | 200 | 209 | 210 | ); 211 | }); 212 | 213 | export default Wizard; 214 | useGLTF.preload("/models/wizard-transformed.glb"); 215 | -------------------------------------------------------------------------------- /src/components/navigation/NavButton.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | Github, 3 | Home, 4 | Linkedin, 5 | NotebookText, 6 | Palette, 7 | Phone, 8 | Twitter, 9 | User, 10 | } from "lucide-react"; 11 | import Link from "next/link"; 12 | import React from "react"; 13 | import ResponsiveComponent from "../ResponsiveComponent"; 14 | import clsx from "clsx"; 15 | import { motion } from "framer-motion"; 16 | 17 | const getIcon = (icon) => { 18 | switch (icon) { 19 | case "home": 20 | return ; 21 | case "about": 22 | return ; 23 | case "projects": 24 | return ; 25 | case "contact": 26 | return ; 27 | case "github": 28 | return ; 29 | case "linkedin": 30 | return ; 31 | case "twitter": 32 | return ; 33 | case "resume": 34 | return ; 35 | 36 | default: 37 | return ; 38 | } 39 | }; 40 | 41 | const item = { 42 | hidden: { scale: 0 }, 43 | show: { scale: 1 }, 44 | }; 45 | 46 | const NavLink = motion(Link); 47 | 48 | const NavButton = ({ 49 | x, 50 | y, 51 | label, 52 | link, 53 | icon, 54 | newTab, 55 | labelDirection = "right", 56 | }) => { 57 | return ( 58 | 59 | {({ size }) => { 60 | return size && size >= 480 ? ( 61 |
65 | 77 | 78 | {getIcon(icon)} 79 | 80 | 81 | 82 | 83 | {label} 84 | 85 | 86 | 87 |
88 | ) : ( 89 |
90 | 102 | 103 | {getIcon(icon)} 104 | 105 | 106 | 107 | 115 | 116 | 117 |
118 | ); 119 | }} 120 |
121 | ); 122 | }; 123 | 124 | export default NavButton; 125 | -------------------------------------------------------------------------------- /src/components/navigation/index.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { BtnList } from "@/app/data"; 3 | import React from "react"; 4 | import NavButton from "./NavButton"; 5 | import useScreenSize from "../hooks/useScreenSize"; 6 | import ResponsiveComponent from "../ResponsiveComponent"; 7 | import { motion } from "framer-motion"; 8 | 9 | const container = { 10 | hidden: { opacity: 0 }, 11 | show: { 12 | opacity: 1, 13 | transition: { 14 | staggerChildren: 0.3, 15 | }, 16 | }, 17 | }; 18 | 19 | const Navigation = () => { 20 | const angleIncrement = 360 / BtnList.length; 21 | const size = useScreenSize(); 22 | const isLarge = size >= 1024; 23 | const isMedium = size >= 768; 24 | 25 | return ( 26 |
27 | 28 | {({ size }) => { 29 | return size && size >= 480 ? ( 30 | 36 | {BtnList.map((btn, index) => { 37 | const angleRad = (index * angleIncrement * Math.PI) / 180; 38 | const radius = isLarge 39 | ? "calc(20vw - 1rem)" 40 | : isMedium 41 | ? "calc(30vw - 1rem)" 42 | : "calc(40vw - 1rem)"; 43 | const x = `calc(${radius}*${Math.cos(angleRad)})`; 44 | const y = `calc(${radius}*${Math.sin(angleRad)})`; 45 | 46 | return ; 47 | })} 48 | 49 | ) : ( 50 | <> 51 | 57 | {BtnList.slice(0, BtnList.length / 2).map((btn) => { 58 | return ; 59 | })} 60 | 61 | 62 | 68 | {BtnList.slice(BtnList.length / 2, BtnList.length).map( 69 | (btn) => { 70 | return ( 71 | 78 | ); 79 | } 80 | )} 81 | 82 | 83 | ); 84 | }} 85 | 86 |
87 | ); 88 | }; 89 | 90 | export default Navigation; 91 | -------------------------------------------------------------------------------- /src/components/projects/ProjectLayout.jsx: -------------------------------------------------------------------------------- 1 | import { motion } from "framer-motion"; 2 | import Link from "next/link"; 3 | 4 | const item = { 5 | hidden: { opacity: 0, y: 100 }, 6 | show: { opacity: 1, y: 0 }, 7 | }; 8 | 9 | const ProjectLink = motion(Link); 10 | const ProjectLayout = ({ name, description, date, demoLink }) => { 11 | return ( 12 | 18 |
19 |

{name}

20 |

{description}

21 |
22 |
23 |

24 | {new Date(date).toDateString()} 25 |

26 | 27 | ); 28 | }; 29 | 30 | export default ProjectLayout; 31 | -------------------------------------------------------------------------------- /src/components/projects/index.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { motion } from "framer-motion"; 3 | import ProjectLayout from "./ProjectLayout"; 4 | 5 | const container = { 6 | hidden: { opacity: 0 }, 7 | show: { 8 | opacity: 1, 9 | transition: { 10 | staggerChildren: 0.3, 11 | delayChildren: 1.5, 12 | }, 13 | }, 14 | }; 15 | 16 | const ProjectList = ({ projects }) => { 17 | return ( 18 | 24 | {projects.map((project, index) => { 25 | return ; 26 | })} 27 | 28 | ); 29 | }; 30 | 31 | export default ProjectList; 32 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", 5 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}", 7 | ], 8 | theme: { 9 | extend: { 10 | fontFamily:{ 11 | inter: ['var(--font-inter)'] 12 | }, 13 | colors:{ 14 | background: 'rgb(var(--background))', 15 | foreground: 'rgb(var(--foreground))', 16 | muted: 'rgb(var(--muted))', 17 | accent: 'rgb(var(--accent))', 18 | }, 19 | backgroundImage:{ 20 | 'firefly-radial': "radial-gradient(50% 50% at 50% 50%, rgba(253, 255, 80, 0.5) 0%, rgba(217,217,217, 0) 100%)" 21 | }, 22 | boxShadow:{ 23 | 'glass-inset': 'inset 0 17px 5px -9px rgba(254,254,91, 0.05)', 24 | 'glass-sm': '5px 5px 20px 0px rgba(254,254,91, 0.3)', 25 | }, 26 | keyframes:{ 27 | 'spin-reverse':{ 28 | '0%': {transform: 'rotate(0deg)'}, 29 | '100%': {transform: 'rotate(-360deg)'} 30 | } 31 | }, 32 | animation:{ 33 | 'spin-slow': 'spin 40s linear infinite', 34 | 'spin-slow-reverse': 'spin-reverse 40s linear infinite', 35 | }, 36 | screens:{ 37 | xs: '480px', 38 | } 39 | }, 40 | }, 41 | plugins: [], 42 | }; 43 | --------------------------------------------------------------------------------