Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"downloadjs": "^1.4.7",
"file-saver": "^2.0.5",
"firebase": "9.9.0",
"framer-motion": "^11.18.2",
"git-repo-api": "^0.0.17",
"graphql": "^16.8.1",
"highlight.js": "^11.9.0",
Expand Down
76 changes: 76 additions & 0 deletions src/plays/dj-motion-cards/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# 🎧 DJ Motion Cards

An animated DJ profile cards component built with **React**, **Tailwind CSS**, and **Framer Motion** — inspired by modern Electronic Press Kits (EPKs) for DJs and music creators.
It demonstrates smooth motion effects, clean visual hierarchy, and interactive elements that respond elegantly to user hover actions.

---

## 🧩 Play Information

- **Language:** TypeScript
- **Style:** CSS (Tailwind utility classes)
- **Difficulty:** Beginner
- **Play Type:** Animation / UI Design

---

## 👨‍🎤 Creator Information

- **User:** ivandiaz333
- **GitHub:** [@ivandiaz333](https://github.com/ivandiaz333)
- **Video Demo:** _(optional)_
- **Blog/Portfolio:** _(optional)_

---

## ⚙️ Implementation Details

This Play presents a **modern DJ profile card** featuring hover animations and glowing transitions using Framer Motion.
It illustrates how motion and layout can combine to create visually engaging UI components.

### ✨ Key Features

- Animated image rotation and scale on hover.
- Pulsating soundwave bars for rhythmic motion.
- Smooth easing and transition timing.
- Fully responsive layout built with Tailwind CSS.

### 🛠️ Technologies Used

- **React 18**
- **TypeScript**
- **Tailwind CSS**
- **Framer Motion**

---

## 💡 Notes & Considerations

- Focused on **UI aesthetics and motion principles**, not backend logic.
- All images are local assets for demo purposes (not fetched externally).
- You can replace the images or modify Tailwind classes to adapt the look.

---

## 🧠 Learn More

- [Framer Motion Documentation](https://www.framer.com/motion/)
- [Tailwind CSS Documentation](https://tailwindcss.com/docs)
- [React Play Contributor Guide](https://docs.reactplay.io/)

---

## 📷 Preview

_Include a screenshot or GIF here (optional but recommended)._

---

## 🚀 How to Run Locally

1. Clone the React Play repository and install dependencies:
```bash
git clone https://github.com/reactplay/react-play.git
cd react-play
npm install
```
Binary file added src/plays/dj-motion-cards/axwell.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/plays/dj-motion-cards/deadmau5.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/plays/dj-motion-cards/dj.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
288 changes: 288 additions & 0 deletions src/plays/dj-motion-cards/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
import React, { useState } from 'react';
import PlayHeader from 'common/playlists/PlayHeader';
import { motion } from 'framer-motion';
import djImage from './james-hype.jpg';
import djImage2 from './axwell.jpg';
import djImage3 from './swedish-house-mafia.jpg';
import djImage4 from './deadmau5.jpg';
import './styles.css';

// Interfaz para tipar los datos de cada DJ
interface SpotifyTrack {
id: string;
url: string;
title: string;
}

interface DJData {
id: number;
name: string;
country: string;
image: string;
tracks: SpotifyTrack[];
colorTheme: {
button: string;
buttonHover: string;
soundWaves: string;
};
}

// Componente DJCard reutilizable
interface DJCardProps {
dj: DJData;
}

function DJCard({ dj }: DJCardProps) {
const [flipped, setFlipped] = useState(false);

return (
<motion.div
className="relative w-[400px] h-[500px]"
style={{
perspective: '1000px',
transformStyle: 'preserve-3d'
}}
>
{/* FRONT SIDE */}
<motion.div
animate={{ rotateY: flipped ? 180 : 0 }}
className="absolute w-full h-full bg-carbon text-frost p-6 rounded-2xl shadow-lg flex flex-col items-center justify-center"
initial={{ rotateY: 0 }}
style={{
backfaceVisibility: 'hidden',
transformStyle: 'preserve-3d'
}}
transition={{ duration: 0.9, ease: [0.65, 0, 0.35, 1] }}
>
<motion.img
alt={dj.name}
className="rounded-xl shadow-md w-60 h-60 object-cover"
src={dj.image}
transition={{ stiffness: 120, type: 'spring' }}
whileHover={{ rotateY: 8, scale: 1.05 }}
/>

<h2 className="text-2xl font-bold mt-4">{dj.name}</h2>
<p className="text-silver">{dj.country}</p>

<motion.div
animate={{ opacity: [0.3, 1, 0.3] }}
className="flex gap-1 mt-4 justify-center"
transition={{ duration: 2, repeat: Infinity }}
>
{[...Array(5)].map((_, i) => (
<span
className={`${i % 2 ? 'h-3' : 'h-2'} w-2 rounded-full`}
key={i}
style={{ backgroundColor: dj.colorTheme.soundWaves }}
/>
))}
</motion.div>

<motion.button
className="mt-5 px-5 py-2 rounded-lg font-medium text-white shadow-md"
style={{
background: dj.colorTheme.button
}}
transition={{
type: 'spring',
stiffness: 160,
damping: 22
}}
whileHover={{
scale: 1.07,
background: dj.colorTheme.buttonHover,
boxShadow: '0 0 20px rgba(20, 184, 166, 0.6)',
filter: 'brightness(1.1)'
}}
onClick={() => setFlipped(true)}
>
My Favourite Tracks →
</motion.button>
</motion.div>

{/* BACK SIDE */}
<motion.div
animate={{ rotateY: flipped ? 0 : -180 }}
className="absolute w-full h-full rounded-2xl bg-white text-gray-900 flex flex-col items-center justify-between p-6 shadow-lg overflow-hidden"
initial={{ rotateY: -180 }}
style={{
transformStyle: 'preserve-3d',
backfaceVisibility: 'hidden',
transform: 'rotateY(180deg)'
}}
transition={{ duration: 0.9, ease: [0.65, 0, 0.35, 1] }}
>
<h3 className="text-xl font-semibold mb-3">Top Tracks 🎧</h3>

<div className="flex flex-col gap-3 w-full flex-1 justify-center">
{dj.tracks.map((track) => (
<iframe
allow="encrypted-media"
className="rounded-lg"
height="90"
key={track.id}
src={track.url}
style={{ border: 0, overflow: 'hidden' }}
title={track.title}
width="100%"
/>
))}
</div>

<motion.button
className="mt-3 px-4 py-2 text-white rounded-lg shadow-md"
style={{
background: dj.colorTheme.button
}}
whileHover={{
background: dj.colorTheme.buttonHover
}}
whileTap={{ scale: 0.95 }}
onClick={() => setFlipped(false)}
>
Back
</motion.button>
</motion.div>
</motion.div>
);
}

// Array de datos de DJs
const djsData: DJData[] = [
{
id: 1,
name: 'James Hype',
country: 'UK',
image: djImage,
tracks: [
{
id: 'track1-1',
url: 'https://open.spotify.com/embed/track/1OcV53oesLQw3VTW9I3uD3?utm_source=generator',
title: 'James Hype Track 1'
},
{
id: 'track1-2',
url: 'https://open.spotify.com/embed/track/4zN21mbAuaD0WqtmaTZZeP?utm_source=generator',
title: 'James Hype Track 2'
},
{
id: 'track1-3',
url: 'https://open.spotify.com/embed/track/3sU1L9okYWbN61oHZNQTfh?utm_source=generator',
title: 'James Hype Track 3'
}
],
colorTheme: {
button: 'linear-gradient(to right, #14b8a6, #10b981)',
buttonHover: 'linear-gradient(to right, #0d9488, #059669)',
soundWaves: '#10b981'
}
},
{
id: 2,
name: 'Axwell',
country: 'Sweden',
image: djImage2,
tracks: [
{
id: 'track2-1',
url: 'https://open.spotify.com/embed/track/0PL26MnAqZtO9kEFyyvPvO?utm_source=generator',
title: 'Axwell Track 1'
},
{
id: 'track2-2',
url: 'https://open.spotify.com/embed/track/29Rdysued3nwLlQwmg9R46?utm_source=generator',
title: 'Axwell Track 2'
},
{
id: 'track2-3',
url: 'https://open.spotify.com/embed/track/6lL4XsdRrQ8XafBJbWDPV3?utm_source=generator',
title: 'Axwell Track 3'
}
],
colorTheme: {
button: 'linear-gradient(to right, #3b82f6, #8b5cf6)',
buttonHover: 'linear-gradient(to right, #2563eb, #7c3aed)',
soundWaves: '#8b5cf6'
}
},
{
id: 3,
name: 'Swedish House Mafia',
country: 'Sweden',
image: djImage3,
tracks: [
{
id: 'track3-1',
url: 'https://open.spotify.com/embed/track/4xr1Azj8mCbz6u8CITf8ef?utm_source=generator',
title: 'Swedish House Mafia Track 1'
},
{
id: 'track3-2',
url: 'https://open.spotify.com/embed/track/2V65y3PX4DkRhy1djlxd9p?utm_source=generator',
title: 'Swedish House Mafia Track 2'
},
{
id: 'track3-3',
url: 'https://open.spotify.com/embed/track/6gdDu39yYqPcaTgCwYEW8i?utm_source=generator',
title: 'Swedish House Mafia Track 3'
}
],
colorTheme: {
button: 'linear-gradient(to right, #f59e0b, #ef4444)',
buttonHover: 'linear-gradient(to right, #d97706, #dc2626)',
soundWaves: '#f59e0b'
}
},
{
id: 4,
name: 'Deadmau5',
country: 'Canada',
image: djImage4,
tracks: [
{
id: 'track4-1',
url: 'https://open.spotify.com/embed/track/4kJWtxDDNb9oAk3h7sX3N4?utm_source=generator',
title: 'Deadmau5 Track 1'
},
{
id: 'track4-2',
url: 'https://open.spotify.com/embed/track/5YaqbhEmoxSpIbdBTPG6KQ?utm_source=generator',
title: 'Deadmau5 Track 2'
},
{
id: 'track4-3',
url: 'https://open.spotify.com/embed/track/0F539Pcn7LDa2MbBhDe3Tw?utm_source=generator',
title: 'Deadmau5 Track 3'
}
],
colorTheme: {
button: 'linear-gradient(to right, #ec4899, #a855f7)',
buttonHover: 'linear-gradient(to right, #db2777, #9333ea)',
soundWaves: '#ec4899'
}
}
];

// Componente principal
function DjMotionCards(props: any) {
return (
<>
<div className="play-details">
<PlayHeader play={props} />

<div className="play-details-body flex justify-center gap-6">
{/* Your Code Starts Here */}

{djsData.map((dj) => (
<DJCard dj={dj} key={dj.id} />
))}

{/* Your Code Ends Here */}
</div>
</div>
</>
);
}

export default DjMotionCards;
Binary file added src/plays/dj-motion-cards/james-hype.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions src/plays/dj-motion-cards/play.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "DJ Motion Cards",
"description": "Animated DJ profile cards using React and Framer Motion.",
"author": "Iván",
"github": "https://github.com/ivandiaz333",
"tags": ["react", "motion", "animation", "cards", "music"]
}
8 changes: 8 additions & 0 deletions src/plays/dj-motion-cards/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* DJ Motion Card styles */

/* Tailwind classes handle most of the styling. */

.backface-hidden {
backface-visibility: hidden !important;
transform-style: preserve-3d;
}
Binary file added src/plays/dj-motion-cards/swedish-house-mafia.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.