├── src
├── vite-env.d.ts
├── main.tsx
├── index.css
├── components
│ └── Flame.tsx
├── App.tsx
└── assets
│ └── react.svg
├── postcss.config.js
├── README.md
├── vite.config.ts
├── tsconfig.node.json
├── tailwind.config.js
├── .gitignore
├── .eslintrc.cjs
├── tsconfig.json
├── index.html
├── package.json
└── public
└── vite.svg
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SVG Hover Animation
2 |
3 | This is the code that can be found within this tutorial
4 |
5 | [Watch the video](https://youtu.be/KKQQn_lDuVQ?si=44Eir01RVUmvWLx8)
6 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true,
8 | "strict": true
9 | },
10 | "include": ["vite.config.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
4 | theme: {
5 | extend: {
6 | fontFamily: {
7 | poppins: ["Poppins", "sans-serif"],
8 | },
9 | },
10 | },
11 | plugins: [],
12 | }
13 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | body {
6 | @apply bg-neutral-900;
7 | }
8 |
9 | .card {
10 | @apply w-[44rem] h-[26rem] bg-neutral-800 rounded-lg border border-neutral-600 flex flex-row p-8 absolute justify-between stroke-[0.1] hover:stroke-[0.15];
11 | }
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: { browser: true, es2020: true },
4 | extends: [
5 | 'eslint:recommended',
6 | 'plugin:@typescript-eslint/recommended',
7 | 'plugin:react-hooks/recommended',
8 | ],
9 | ignorePatterns: ['dist', '.eslintrc.cjs'],
10 | parser: '@typescript-eslint/parser',
11 | plugins: ['react-refresh'],
12 | rules: {
13 | 'react-refresh/only-export-components': [
14 | 'warn',
15 | { allowConstantExport: true },
16 | ],
17 | },
18 | }
19 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "noEmit": true,
15 | "jsx": "react-jsx",
16 |
17 | /* Linting */
18 | "strict": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noFallthroughCasesInSwitch": true
22 | },
23 | "include": ["src"],
24 | "references": [{ "path": "./tsconfig.node.json" }]
25 | }
26 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 | Vite + React + TS
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "svg-hover-effect",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@heroicons/react": "^2.1.1",
14 | "react": "^18.2.0",
15 | "react-dom": "^18.2.0"
16 | },
17 | "devDependencies": {
18 | "@types/react": "^18.2.64",
19 | "@types/react-dom": "^18.2.21",
20 | "@typescript-eslint/eslint-plugin": "^7.1.1",
21 | "@typescript-eslint/parser": "^7.1.1",
22 | "@vitejs/plugin-react": "^4.2.1",
23 | "autoprefixer": "^10.4.18",
24 | "eslint": "^8.57.0",
25 | "eslint-plugin-react-hooks": "^4.6.0",
26 | "eslint-plugin-react-refresh": "^0.4.5",
27 | "postcss": "^8.4.36",
28 | "tailwindcss": "^3.4.1",
29 | "typescript": "^5.2.2",
30 | "vite": "^5.1.6"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Flame.tsx:
--------------------------------------------------------------------------------
1 | import { useState, type RefObject, useEffect } from "react"
2 |
3 | interface Props {
4 | cursor: { x: number; y: number }
5 | cardRef: RefObject
6 | mouseOnCard: boolean
7 | }
8 |
9 | const Flame = ({ cursor, cardRef, mouseOnCard }: Props) => {
10 | const [gradientCenter, setGradientCenter] = useState({ cx: "50%", cy: "50%" })
11 |
12 | useEffect(() => {
13 | if (cardRef.current && cursor.x !== null && cursor.y !== null) {
14 | const cardRect = cardRef.current.getBoundingClientRect()
15 | const cxPercentage = (cursor.x / cardRect.width) * 100 - 24
16 | const cyPercentage = (cursor.y / cardRect.height) * 100
17 | setGradientCenter({
18 | cx: `${cxPercentage}%`,
19 | cy: `${cyPercentage}%`,
20 | })
21 | }
22 | }, [cursor, cardRef])
23 |
24 | return (
25 |
57 | )
58 | }
59 |
60 | export default Flame
61 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { CheckIcon, CircleStackIcon } from "@heroicons/react/24/outline"
2 | import Flame from "./components/Flame"
3 | import { useRef, useState } from "react"
4 |
5 | const App = () => {
6 | const cardsRef = useRef(null)
7 | const [cursor, setCursor] = useState({ x: 0, y: 0 })
8 | const [mouseOnCard, setMouseOnCard] = useState(false)
9 |
10 | const handleMouseMove = (
11 | event: React.MouseEvent
12 | ) => {
13 | if (cardsRef.current !== null) {
14 | const rect = cardsRef.current.getBoundingClientRect()
15 | const x = event.clientX - rect.left
16 | const y = event.clientY - rect.top
17 | setCursor({ x: x, y: y })
18 | }
19 | }
20 |
21 | return (
22 |
23 | setMouseOnCard(true)}
27 | onMouseLeave={() => setMouseOnCard(false)}
28 | onMouseMove={(event) => handleMouseMove(event)}
29 | >
30 |
31 |
32 |
33 |
34 | Database
35 |
36 |
37 | Every project is a full Postgres database, the world's most
38 | trusted relational database.
39 |
40 |
41 |
42 |
43 |
44 | 100% portable
45 |
46 |
47 |
48 | Built-in Auth with RLS
49 |
50 |
51 |
52 | Easy to extend
53 |
54 |
55 |
56 |
57 | {/* SVG Here */}
58 |
59 |
60 |
61 |
62 | )
63 | }
64 |
65 | export default App
66 |
--------------------------------------------------------------------------------
/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------