Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
335 changes: 335 additions & 0 deletions frontend/src/components/AnimatedCursor.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,335 @@
/* Hide default cursor only on devices with hover and fine pointer */
@media (hover: hover) and (pointer: fine) {
* {
cursor: none !important;
}
}
Comment on lines +1 to +6
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Do not globally hide the native cursor by default

Hiding the cursor for all elements risks unusable UI if the custom cursor elements/logic aren’t mounted. Scope it to when the cursor is active.

Apply this diff to scope via :has (broad support) or a body class:

-/* Hide default cursor only on devices with hover and fine pointer */
-@media (hover: hover) and (pointer: fine) {
-  * {
-    cursor: none !important;
-  }
-}
+/* Hide default cursor only when custom cursor is present */
+@media (hover: hover) and (pointer: fine) {
+  html:has(.animated-cursor) * {
+    cursor: none !important;
+  }
+}
+/* Alternatively, gate by a body class you toggle when initializing */
+/* .has-animated-cursor * { cursor: none !important; } */
🤖 Prompt for AI Agents
In frontend/src/components/AnimatedCursor.css around lines 1 to 6, the
stylesheet currently hides the native cursor globally for all elements which can
break the UI if the custom cursor isn't mounted; change the selector so cursor:
none is only applied when the custom cursor is active (e.g., use a parent
selector like body.has-custom-cursor or a :has() condition that checks for the
presence of the custom cursor element) and keep the existing media query; update
any mounting/unmounting code to toggle that body class (or ensure the custom
cursor element exists to satisfy :has) so the native cursor remains visible
otherwise.


/* Main animated cursor - Conical/Teardrop shape */
.animated-cursor {
position: fixed;
width: 20px;
height: 20px;
background: linear-gradient(135deg, #8B0000, #FF4500, #FFA500);
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
pointer-events: none;
z-index: 9999;
left: 50%;
top: 50%;
transform: translate(-50%, -50%) rotate(45deg);
will-change: left, top, transform;
animation: pulse 3s infinite;
}

/* Add a small circle at the tip */
.animated-cursor::before {
content: '';
position: absolute;
top: -2px;
left: 50%;
width: 6px;
height: 6px;
background: linear-gradient(135deg, #FFD700, #FFA500);
border-radius: 50%;
transform: translate(-50%, -50%);
}

/* Simplified glow effect for better performance */
.animated-cursor::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 25px;
height: 25px;
background: rgba(255, 69, 0, 0.2);
border-radius: 50%;
transform: translate(-50%, -50%);
animation: glow 3s infinite;
}

/* Cursor trail - Conical shape */
.cursor-trail {
position: fixed;
width: 30px;
height: 30px;
background: linear-gradient(135deg, rgba(139, 0, 0, 0.4), rgba(255, 69, 0, 0.4), rgba(255, 165, 0, 0.4));
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
pointer-events: none;
z-index: 9998;
left: 50%;
top: 50%;
transform: translate(-50%, -50%) rotate(45deg);
transition: left 0.1s ease-out, top 0.1s ease-out;
will-change: left, top, transform;
animation: trailPulse 3s infinite;
}

/* Hover state - Maintain conical shape */
.animated-cursor.hover {
width: 30px;
height: 30px;
background: linear-gradient(135deg, #4ecdc4, #44a08d, #4ecdc4);
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
filter: drop-shadow(0 0 12px rgba(78, 205, 196, 0.8));
animation: hoverPulse 1s infinite;
}

.cursor-trail.hover {
width: 40px;
height: 40px;
background: linear-gradient(135deg, rgba(78, 205, 196, 0.4), rgba(68, 160, 141, 0.4));
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
animation: hoverTrailPulse 1s infinite;
}

/* Clicking state - Maintain conical shape */
.animated-cursor.clicking {
width: 15px;
height: 15px;
background: linear-gradient(135deg, #ff4757, #ff3838, #ff4757);
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
filter: drop-shadow(0 0 15px rgba(255, 71, 87, 0.9));
animation: clickPulse 0.3s ease-out;
}

.cursor-trail.clicking {
width: 25px;
height: 25px;
background: linear-gradient(135deg, rgba(255, 71, 87, 0.5), rgba(255, 56, 56, 0.5));
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
animation: clickTrailPulse 0.3s ease-out;
}

/* Optimized animations for better performance */
@keyframes pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
}

@keyframes trailPulse {
0%, 100% {
transform: scale(1);
opacity: 0.3;
}
50% {
transform: scale(1.1);
opacity: 0.2;
}
}
Comment on lines +105 to +123
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Keyframes overwrite translate/rotate causing jumps

pulse and trailPulse set transform: scale(...) only, overwriting the base translate/rotate, which can shift the cursor. Include translate/rotate in keyframes.

Apply this diff:

 @keyframes pulse {
   0%, 100% {
-    transform: scale(1);
+    transform: translate(-50%, -50%) rotate(45deg) scale(1);
   }
   50% {
-    transform: scale(1.05);
+    transform: translate(-50%, -50%) rotate(45deg) scale(1.05);
   }
 }
 
 @keyframes trailPulse {
   0%, 100% {
-    transform: scale(1);
+    transform: translate(-50%, -50%) rotate(45deg) scale(1);
     opacity: 0.3;
   }
   50% {
-    transform: scale(1.1);
+    transform: translate(-50%, -50%) rotate(45deg) scale(1.1);
     opacity: 0.2;
   }
 }
🤖 Prompt for AI Agents
In frontend/src/components/AnimatedCursor.css around lines 105 to 123, the pulse
and trailPulse keyframes only set transform: scale(...), which overwrites the
base translate/rotate and causes the cursor to jump; update each keyframe to
include the same translate(...) and rotate(...) used in the element’s base
transform (e.g. transform: translate(var(--cx), var(--cy)) rotate(var(--rot))
scale(...)) so the scale animates without removing translation/rotation, keeping
opacity changes for trailPulse as-is.


@keyframes hoverPulse {
0%, 100% {
transform: translate(-50%, -50%) scale(1);
}
50% {
transform: translate(-50%, -50%) scale(1.2);
}
}

@keyframes hoverTrailPulse {
0%, 100% {
transform: translate(-50%, -50%) scale(1);
opacity: 0.4;
}
50% {
transform: translate(-50%, -50%) scale(1.3);
opacity: 0.2;
}
}

@keyframes clickPulse {
0% {
transform: translate(-50%, -50%) scale(1);
}
50% {
transform: translate(-50%, -50%) scale(0.8);
}
100% {
transform: translate(-50%, -50%) scale(1);
}
}

@keyframes clickTrailPulse {
0% {
transform: translate(-50%, -50%) scale(1);
opacity: 0.5;
}
50% {
transform: translate(-50%, -50%) scale(1.5);
opacity: 0.2;
}
100% {
transform: translate(-50%, -50%) scale(1);
opacity: 0.5;
}
}

/* Responsive design - hide cursor on mobile devices */
@media (max-width: 768px) {
.animated-cursor,
.cursor-trail {
display: none;
}

* {
cursor: auto !important;
}
}

/* Special effects for different elements */
.animated-cursor.hover::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 60px;
height: 60px;
background: linear-gradient(135deg, rgba(78, 205, 196, 0.2), rgba(68, 160, 141, 0.2));
border-radius: 50%;
transform: translate(-50%, -50%);
animation: ripple 1s infinite;
}

/* Cursor text */
.cursor-text {
position: fixed;
color: #fff;
font-size: 12px;
font-weight: bold;
pointer-events: none;
z-index: 10000;
background: rgba(0, 0, 0, 0.7);
padding: 4px 8px;
border-radius: 4px;
animation: textFade 0.3s ease-out;
}

@keyframes textFade {
0% {
opacity: 0;
transform: translateY(10px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}

/* Different cursor types - All conical shapes */
.animated-cursor.link {
background: linear-gradient(135deg, #667eea, #764ba2, #667eea);
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
filter: drop-shadow(0 0 12px rgba(102, 126, 234, 0.7));
}

.animated-cursor.button {
background: linear-gradient(135deg, #f093fb, #f5576c, #f093fb);
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
filter: drop-shadow(0 0 12px rgba(240, 147, 251, 0.7));
}

.animated-cursor.input {
background: linear-gradient(135deg, #4facfe, #00f2fe, #4facfe);
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
filter: drop-shadow(0 0 12px rgba(79, 172, 254, 0.7));
}

.animated-cursor.nav {
background: linear-gradient(135deg, #fa709a, #fee140, #fa709a);
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
filter: drop-shadow(0 0 15px rgba(250, 112, 154, 0.8));
animation: navPulse 1.5s infinite;
}

.animated-cursor.hero {
background: linear-gradient(135deg, #a8edea, #fed6e3, #a8edea);
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
filter: drop-shadow(0 0 18px rgba(168, 237, 234, 0.9));
animation: heroPulse 2s infinite;
}

/* Cursor trail variations - All conical shapes */
.cursor-trail.link {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.4), rgba(118, 75, 162, 0.4));
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}

.cursor-trail.button {
background: linear-gradient(135deg, rgba(240, 147, 251, 0.4), rgba(245, 87, 108, 0.4));
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}

.cursor-trail.input {
background: linear-gradient(135deg, rgba(79, 172, 254, 0.4), rgba(0, 242, 254, 0.4));
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}

.cursor-trail.nav {
background: linear-gradient(135deg, rgba(250, 112, 154, 0.5), rgba(254, 209, 64, 0.5));
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}

.cursor-trail.hero {
background: linear-gradient(135deg, rgba(168, 237, 234, 0.5), rgba(254, 214, 227, 0.5));
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}

/* Additional animations */
@keyframes navPulse {
0%, 100% {
transform: translate(-50%, -50%) scale(1);
}
50% {
transform: translate(-50%, -50%) scale(1.3);
}
}

@keyframes heroPulse {
0%, 100% {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
50% {
transform: translate(-50%, -50%) scale(1.4);
opacity: 0.7;
}
}

@keyframes ripple {
0% {
transform: translate(-50%, -50%) scale(0);
opacity: 1;
}
100% {
transform: translate(-50%, -50%) scale(1);
opacity: 0;
}
}

@keyframes glow {
0%, 100% {
opacity: 0.3;
transform: translate(-50%, -50%) scale(1);
}
50% {
opacity: 0.6;
transform: translate(-50%, -50%) scale(1.2);
}
}

/* Respect users who prefer reduced motion */
@media (prefers-reduced-motion: reduce) {
.animated-cursor,
.cursor-trail,
.animated-cursor::after,
.animated-cursor.hover::before,
.cursor-text {
animation: none !important;
transition: none !important;
}
}
2 changes: 1 addition & 1 deletion frontend/src/components/Pages/Home.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function Home() {
window.scrollTo(0, 0);
}, []);
return (
<div id="home" className="bg-[#FDF3C7]">
<div id="home" className="bg-black text-white">
<Landing />
<About />
<ReviewCarousel />
Expand Down
Loading