diff --git a/backend/streamify_api/settings.py b/backend/streamify_api/settings.py index cc860b13..b8f1bf1e 100644 --- a/backend/streamify_api/settings.py +++ b/backend/streamify_api/settings.py @@ -24,10 +24,10 @@ # See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = os.environ["DJANGO_SECRET_KEY"] +SECRET_KEY = os.environ.get("DJANGO_SECRET_KEY", "django-insecure-dev-key-change-in-production") # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = os.environ["DEBUG"] +DEBUG = os.environ.get("DEBUG", "True").lower() == "true" ALLOWED_HOSTS = ['.onrender.com', 'localhost', '127.0.0.1'] diff --git a/frontend/index.html b/frontend/index.html index f2de376f..a5187e18 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -5,6 +5,9 @@ + + + Streamify diff --git a/frontend/src/components/Miniplayer.css b/frontend/src/components/Miniplayer.css index 936f22f1..3489137a 100644 --- a/frontend/src/components/Miniplayer.css +++ b/frontend/src/components/Miniplayer.css @@ -2,40 +2,41 @@ display: flex; justify-content: space-between; align-items: center; - padding: 0.8rem 1rem; - border-radius: 10px; - background-color: #4a2f2f; + padding: var(--space-sm) var(--space-md); + border-radius: var(--radius-md); + background-color: var(--color-background-secondary); width: 100%; position: fixed; left: 0; right: 0; bottom: 0; - z-index: 100; - box-shadow: 0 -2px 16px rgba(0,0,0,0.18); + z-index: var(--z-fixed); + box-shadow: var(--shadow-lg); + border-top: 1px solid var(--color-border); } .mini-meta { display: flex; align-items: center; - gap: 0.8rem; + gap: var(--space-sm); } .mini-cover { width: 48px; height: 48px; - border-radius: 8px; + border-radius: var(--radius-md); object-fit: cover; } .mini-text { display: flex; flex-direction: column; - color: white; + color: var(--color-text-primary); } .mini-title { - font-weight: 600; - font-size: 0.9rem; + font-weight: var(--font-weight-semibold); + font-size: var(--font-size-sm); margin: 0; white-space: nowrap; overflow: hidden; @@ -44,9 +45,9 @@ } .mini-artist { - font-size: 0.8rem; - color: #c4b3b3; - margin-top: 2px; + font-size: var(--font-size-xs); + color: var(--color-text-secondary); + margin-top: var(--space-xs); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -54,9 +55,9 @@ } .mini-source { - font-size: 0.75rem; - color: #a39090; - margin-top: 2px; + font-size: var(--font-size-xs); + color: var(--color-text-tertiary); + margin-top: var(--space-xs); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -66,26 +67,31 @@ .mini-controls { display: flex; align-items: center; - gap: 0.8rem; + gap: var(--space-sm); } .mini-like { - background: #3a2323; /* dark brown */ + background: var(--color-surface); border: none; - color: white; + color: var(--color-text-primary); cursor: pointer; - font-size: 1rem; + font-size: var(--font-size-base); width: 40px; height: 40px; - border-radius: 50%; + border-radius: var(--radius-full); display: flex; align-items: center; justify-content: center; - transition: background 0.2s; - box-shadow: 0 2px 8px rgba(0,0,0,0.10); + transition: all var(--transition-normal); + box-shadow: var(--shadow-sm); padding: 0; } +.mini-like:hover { + background: var(--color-surface-hover); + transform: scale(1.05); +} + .mini-like svg { display: block; margin: auto; @@ -93,107 +99,131 @@ .mini-like svg path { fill: none; - stroke: #fff; + stroke: var(--color-text-primary); stroke-width: 2.2; - transition: fill 0.2s, stroke 0.2s; + transition: all var(--transition-normal); } .mini-like.liked svg path { - fill: #fff !important; - stroke: #fff !important; + fill: var(--color-primary) !important; + stroke: var(--color-primary) !important; } .mini-like:active { - background: #4a2f2f; + background: var(--color-surface-active); + transform: scale(0.95); } .mini-play { - background: #3a2323; /* dark brown */ + background: var(--color-primary); border: none; - color: white; + color: var(--color-text-primary); cursor: pointer; - font-size: 1rem; + font-size: var(--font-size-base); width: 40px; height: 40px; - border-radius: 50%; + border-radius: var(--radius-full); display: flex; align-items: center; justify-content: center; - transition: background 0.2s; - box-shadow: 0 2px 8px rgba(0,0,0,0.10); + transition: all var(--transition-normal); + box-shadow: var(--shadow-sm); padding: 0; } +.mini-play:hover { + background: var(--color-primary-hover); + transform: scale(1.05); +} + .mini-play svg { display: block; margin: auto; } .mini-play:active { - background: #4a2f2f; + background: var(--color-primary-dark); + transform: scale(0.95); } @media (max-width: 700px) { .mini-player { - bottom: 64px; /* Place above navbar (navbar height is 64px) */ - z-index: 1003; + bottom: var(--space-3xl); /* Place above navbar (navbar height is 64px) */ + z-index: var(--z-fixed); } } + .volume-container { display: flex; align-items: center; - margin: 0 8px; + margin: 0 var(--space-sm); } + .volume-icon { background: none; border: none; cursor: pointer; - margin-right: 6px; + margin-right: var(--space-xs); display: flex; align-items: center; justify-content: center; padding: 0; + transition: opacity var(--transition-normal); +} + +.volume-icon:hover { + opacity: 0.8; } + .volume-slider { width: 100px; -webkit-appearance: none; appearance: none; height: 4px; - border-radius: 2px; + border-radius: var(--radius-sm); cursor: pointer; - transition: opacity 0.2s; + transition: opacity var(--transition-normal); + background: var(--color-border); } + .volume-slider:hover { opacity: 0.9; } + .volume-slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 12px; height: 12px; - background: #fff; - border-radius: 50%; + background: var(--color-text-primary); + border-radius: var(--radius-full); cursor: pointer; - transition: transform 0.2s; + transition: transform var(--transition-normal); } + .volume-slider::-webkit-slider-thumb:hover { transform: scale(1.2); + background: var(--color-primary); } + .volume-slider::-moz-range-thumb { width: 12px; height: 12px; - background: #fff; - border-radius: 50%; + background: var(--color-text-primary); + border-radius: var(--radius-full); border: none; cursor: pointer; - transition: transform 0.2s; + transition: transform var(--transition-normal); } + .volume-slider::-moz-range-thumb:hover { transform: scale(1.2); + background: var(--color-primary); } + .volume-slider::-moz-range-track { height: 4px; - border-radius: 2px; - background: transparent; + border-radius: var(--radius-sm); + background: var(--color-border); } diff --git a/frontend/src/components/Navbar.css b/frontend/src/components/Navbar.css index e0903400..6b3e325d 100644 --- a/frontend/src/components/Navbar.css +++ b/frontend/src/components/Navbar.css @@ -2,13 +2,14 @@ display: flex; align-items: center; justify-content: space-between; - background: #4a2f2f !important; /* Match Miniplayer color */ - padding: 0 32px; - height: 64px; + background: var(--color-background-secondary) !important; + padding: 0 var(--space-xl); + height: var(--space-3xl); width: 100%; box-sizing: border-box; - border-top: 1.5px solid #2a1818 !important; - border-bottom: 1.5px solid #2a1818 !important; + border-top: 1px solid var(--color-border) !important; + border-bottom: 1px solid var(--color-border) !important; + box-shadow: var(--shadow-sm); } .navbar-left { @@ -19,8 +20,13 @@ .navbar-logo { display: flex; align-items: center; - gap: 14px; + gap: var(--space-md); text-decoration: none; + transition: opacity var(--transition-normal); +} + +.navbar-logo:hover { + opacity: 0.8; } .navbar-svg-logo { @@ -31,17 +37,17 @@ } .navbar-title { - color: #fff; - font-family: 'Montserrat', sans-serif; - font-weight: 700; - font-size: 1.4rem; + color: var(--color-text-primary); + font-family: inherit; + font-weight: var(--font-weight-bold); + font-size: var(--font-size-xl); letter-spacing: 0.5px; } .navbar-center { display: flex; align-items: center; - gap: 36px; + gap: var(--space-2xl); } .navbar-icon { @@ -50,13 +56,14 @@ justify-content: center; width: 44px; height: 44px; - border-radius: 8px; - transition: background 0.2s; + border-radius: var(--radius-md); + transition: all var(--transition-normal); text-decoration: none; } .navbar-icon:hover { - background: #232323; + background: var(--color-surface-hover); + transform: scale(1.05); } .navbar-icon svg { @@ -66,25 +73,26 @@ .navbar-right { display: flex; align-items: center; - gap: 18px; + gap: var(--space-lg); } .navbar-heart { - background: #232323; + background: var(--color-surface); border: none; - border-radius: 8px; + border-radius: var(--radius-md); width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; cursor: pointer; - transition: background 0.2s; + transition: all var(--transition-normal); padding: 0; } .navbar-heart:hover { - background: #333; + background: var(--color-surface-hover); + transform: scale(1.05); } .navbar-heart svg { @@ -97,21 +105,22 @@ justify-content: center; width: 44px; height: 44px; - border-radius: 50%; + border-radius: var(--radius-full); overflow: hidden; - border: 2px solid #232323; - background: #181818; - transition: border 0.2s; + border: 2px solid var(--color-border); + background: var(--color-background-secondary); + transition: all var(--transition-normal); } .navbar-avatar:hover { - border: 2px solid #fff; + border: 2px solid var(--color-primary); + transform: scale(1.05); } .navbar-avatar img { width: 40px; height: 40px; - border-radius: 50%; + border-radius: var(--radius-full); object-fit: cover; } @@ -122,13 +131,13 @@ bottom: 0; left: 0; right: 0; - height: 64px; - background: #4a2f2f !important; /* Match Miniplayer color */ - border-top: 1.5px solid #2a1818 !important; + height: var(--space-3xl); + background: var(--color-background-secondary) !important; + border-top: 1px solid var(--color-border) !important; border-bottom: none !important; - z-index: 1002; - padding: 0 0; - box-shadow: 0 -2px 16px rgba(0,0,0,0.18); + z-index: var(--z-fixed); + padding: 0; + box-shadow: var(--shadow-lg); justify-content: center; } .navbar-left, .navbar-right { @@ -142,10 +151,10 @@ .navbar-icon { width: 54px; height: 54px; - border-radius: 50%; + border-radius: var(--radius-full); background: none; margin: 0 2vw; - font-size: 1.2rem; + font-size: var(--font-size-lg); } .navbar-icon svg { width: 32px; @@ -157,10 +166,10 @@ @media (min-width: 701px) { .navbar { position: static; - background: #181818; - border-bottom: 1.5px solid #232323; + background: var(--color-background-secondary); + border-bottom: 1px solid var(--color-border); border-top: none; - box-shadow: none; + box-shadow: var(--shadow-sm); } } diff --git a/frontend/src/components/Nowplaying.css b/frontend/src/components/Nowplaying.css index 47248f77..d1ae42ac 100644 --- a/frontend/src/components/Nowplaying.css +++ b/frontend/src/components/Nowplaying.css @@ -2,7 +2,7 @@ .nowplaying-overlay { position: fixed; inset: 0; - z-index: 1000; + z-index: var(--z-modal); display: none; pointer-events: none; } @@ -10,7 +10,7 @@ .nowplaying-overlay.open { display: block; pointer-events: auto; - animation: fadeIn 0.3s; + animation: fadeIn var(--transition-slow); } @keyframes fadeIn { @@ -21,8 +21,8 @@ .nowplaying-backdrop { position: absolute; inset: 0; - background: rgba(24, 24, 24, 0.92); /* Match search overlay */ - backdrop-filter: blur(10px); /* Add blur for modern effect */ + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(12px); z-index: 0; } @@ -31,23 +31,24 @@ left: 50%; top: 50%; transform: translate(-50%, -50%) scale(1); - background: #231818; - border-radius: 1.5rem; + background: var(--color-background-secondary); + border-radius: var(--radius-2xl); width: 100vw; max-width: 700px; min-width: 320px; padding: 0; z-index: 1; overflow: visible; - box-shadow: none; - animation: modalPop 0.3s; + box-shadow: var(--shadow-2xl); + border: 1px solid var(--color-border); + animation: modalPop var(--transition-slow); } @media (max-width: 600px) { .nowplaying-modal { max-width: 98vw; min-width: 0; - border-radius: 1rem; + border-radius: var(--radius-xl); } } @@ -61,26 +62,26 @@ width: 240px; height: 240px; margin: 0 auto; - border-radius: 18px; - background: #fff; + border-radius: var(--radius-xl); + background: var(--color-background-tertiary); background-size: cover; background-position: center; background-repeat: no-repeat; - box-shadow: none; + box-shadow: var(--shadow-lg); } @media (max-width: 600px) { .nowplaying-artwork { width: 160px; height: 160px; - border-radius: 12px; + border-radius: var(--radius-lg); } } /* Track info */ .nowplaying-track-title { - color: #fff; - font-size: 2rem; - font-weight: 700; + color: var(--color-text-primary); + font-size: var(--font-size-3xl); + font-weight: var(--font-weight-bold); letter-spacing: -0.015em; line-height: 1.2; margin-bottom: 0; @@ -90,14 +91,14 @@ } @media (max-width: 600px) { .nowplaying-track-title { - font-size: 1.2rem; + font-size: var(--font-size-xl); } } .nowplaying-track-artist { - color: #bd9e9e; - font-size: 1.1rem; - font-weight: 400; + color: var(--color-text-secondary); + font-size: var(--font-size-lg); + font-weight: var(--font-weight-normal); margin-top: 0.2em; white-space: nowrap; overflow: hidden; @@ -105,7 +106,7 @@ } @media (max-width: 600px) { .nowplaying-track-artist { - font-size: 0.95rem; + font-size: var(--font-size-base); } } @@ -114,7 +115,7 @@ display: flex; align-items: center; height: 24px; - margin-bottom: 4px; + margin-bottom: var(--space-xs); user-select: none; width: 90%; margin-left: auto; @@ -124,8 +125,8 @@ .nowplaying-progress-current { height: 3px; - border-radius: 2px; - background: #ecc9c9; + border-radius: var(--radius-sm); + background: var(--color-primary); } .nowplaying-progress-knob { @@ -135,30 +136,30 @@ left: var(--knob-left, 0%); width: 18px; height: 18px; - border-radius: 50%; - background: #ecc9c9; - border: 3px solid #fff; - box-shadow: 0 2px 8px 0 rgba(233,41,50,0.10); + border-radius: var(--radius-full); + background: var(--color-primary); + border: 3px solid var(--color-text-primary); + box-shadow: var(--shadow-md); z-index: 2; - transition: background 0.15s, border 0.15s; + transition: all var(--transition-fast); } .nowplaying-progress-remaining { height: 3px; - border-radius: 2px; - background: #ecc9c9; + border-radius: var(--radius-sm); + background: var(--color-border); opacity: 0.4; } .nowplaying-progress-times { display: flex; justify-content: space-between; - color: #bd9e9e; - font-size: 0.95rem; - font-weight: 500; + color: var(--color-text-secondary); + font-size: var(--font-size-sm); + font-weight: var(--font-weight-medium); letter-spacing: 0.015em; width: 90%; - margin: 0 auto 0.5em auto; + margin: 0 auto var(--space-sm) auto; } /* Controls */ @@ -166,13 +167,13 @@ display: flex; align-items: center; justify-content: center; - gap: 2.5rem; - margin-top: 1.5rem; + gap: var(--space-2xl); + margin-top: var(--space-lg); } @media (max-width: 600px) { .nowplaying-controls { - gap: 1.2rem; - margin-top: 1rem; + gap: var(--space-sm); + margin-top: var(--space-md); } } @@ -182,13 +183,13 @@ justify-content: center; width: 48px; height: 48px; - border-radius: 50%; + border-radius: var(--radius-full); background: transparent; - color: #ecc9c9; + color: var(--color-text-secondary); border: none; - font-size: 1.2rem; + font-size: var(--font-size-lg); cursor: pointer; - transition: background 0.2s; + transition: all var(--transition-normal); } @media (max-width: 600px) { .nowplaying-btn { @@ -200,24 +201,30 @@ .nowplaying-btn-play { width: 80px; height: 80px; - background: #ecc9c9; - color: #231818; - font-size: 2rem; + background: var(--color-primary); + color: var(--color-text-primary); + font-size: var(--font-size-2xl); border: none; - margin: 0 1.5rem; + margin: 0 var(--space-lg); } @media (max-width: 600px) { .nowplaying-btn-play { width: 56px; height: 56px; - font-size: 1.3rem; - margin: 0 0.7rem; + font-size: var(--font-size-xl); + margin: 0 var(--space-sm); } } -.nowplaying-btn:hover, +.nowplaying-btn:hover { + background: var(--color-surface-hover); + color: var(--color-text-primary); + transform: scale(1.05); +} + .nowplaying-btn-play:hover { - background: #ecc9c9cc; + background: var(--color-primary-hover); + transform: scale(1.05); } /* Pill buttons */ @@ -229,35 +236,41 @@ align-items: center; justify-content: center; overflow: hidden; - border-radius: 999px; + border-radius: var(--radius-full); height: 44px; - padding: 0 1.5rem; - background: #3a2929; - color: #fff; - font-size: 1.05rem; - font-weight: 700; + padding: 0 var(--space-lg); + background: var(--color-surface); + color: var(--color-text-primary); + font-size: var(--font-size-base); + font-weight: var(--font-weight-bold); line-height: normal; letter-spacing: 0.015em; - transition: background 0.2s, color 0.2s; - box-shadow: none; - border: none; - margin: 0 0.7rem; + transition: all var(--transition-normal); + box-shadow: var(--shadow-sm); + border: 1px solid var(--color-border); + margin: 0 var(--space-sm); } @media (max-width: 600px) { .nowplaying-pill-btn { min-width: 90px; max-width: 140px; height: 36px; - font-size: 0.95rem; - padding: 0 0.7rem; - margin: 0 0.3rem; + font-size: var(--font-size-sm); + padding: 0 var(--space-sm); + margin: 0 var(--space-xs); } } +.nowplaying-pill-btn:hover { + background: var(--color-surface-hover); + transform: translateY(-2px); + box-shadow: var(--shadow-md); +} + .nowplaying-pill-btn.liked, .nowplaying-pill-btn.downloading { - background: #e92932; - color: #fff; + background: var(--color-primary); + color: var(--color-text-primary); } .nowplaying-pill-btn:disabled { @@ -286,3 +299,4 @@ .size-10 { width: 36px; height: 36px; } .size-16 { width: 56px; height: 56px; } } + diff --git a/frontend/src/components/PlaylistCard.css b/frontend/src/components/PlaylistCard.css index a16df7ab..bc5bfa2f 100644 --- a/frontend/src/components/PlaylistCard.css +++ b/frontend/src/components/PlaylistCard.css @@ -1,27 +1,28 @@ /* PlaylistCard.css - Modern collage style */ .playlist-card { - background: linear-gradient(135deg, #2a1a1a 60%, #4a2f2f 100%); - border-radius: 18px; - box-shadow: 0 2px 12px rgba(0,0,0,0.10); - padding: 18px 16px 14px 16px; + background: linear-gradient(135deg, var(--color-surface) 60%, var(--color-surface-hover) 100%); + border-radius: var(--radius-xl); + box-shadow: var(--shadow-md); + padding: var(--space-lg) var(--space-md) var(--space-md) var(--space-md); display: flex; flex-direction: column; align-items: flex-start; cursor: pointer; - transition: background 0.2s, box-shadow 0.2s, transform 0.15s; + transition: all var(--transition-normal); min-width: 0; width: 100%; max-width: 220px; + border: 1px solid var(--color-border); } .playlist-card:hover { - background: #4a2f2f; - box-shadow: 0 6px 24px rgba(0,0,0,0.18); - transform: translateY(-2px) scale(1.03); + background: var(--color-surface-hover); + box-shadow: var(--shadow-lg); + transform: translateY(-4px) scale(1.02); } .playlist-art-collage { width: 100%; aspect-ratio: 1/1; - margin-bottom: 12px; + margin-bottom: var(--space-sm); position: relative; display: flex; align-items: center; @@ -30,25 +31,25 @@ .playlist-art-placeholder { width: 100%; height: 100%; - background: #2a1a1a; - border-radius: 12px; + background: var(--color-background-secondary); + border-radius: var(--radius-lg); display: flex; align-items: center; justify-content: center; - color: #c89393; - font-size: 1.1rem; + color: var(--color-text-secondary); + font-size: var(--font-size-lg); } .playlist-art-single { width: 100%; height: 100%; object-fit: cover; - border-radius: 12px; + border-radius: var(--radius-lg); } .playlist-art-grid { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; - gap: 2px; + gap: var(--space-xs); width: 100%; height: 100%; } @@ -56,54 +57,105 @@ width: 100%; height: 100%; object-fit: cover; - border-radius: 6px; + border-radius: var(--radius-sm); } .playlist-meta { - margin-top: 6px; + margin-top: var(--space-xs); } .playlist-title { - color: #fff; - font-size: 1.1rem; - font-weight: 700; - margin-bottom: 2px; + color: var(--color-text-primary); + font-size: var(--font-size-lg); + font-weight: var(--font-weight-bold); + margin-bottom: var(--space-xs); } .playlist-count { - color: #c89393; - font-size: 0.95rem; + color: var(--color-text-secondary); + font-size: var(--font-size-sm); +} + +/* Liked playlist specific styles */ +.liked-playlist { + background: linear-gradient(135deg, #1db954 0%, #1ed760 50%, #1db954 100%); + border: 1px solid #1db954; + position: relative; + overflow: hidden; +} + +.liked-playlist::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(29, 185, 84, 0.1) 0%, rgba(30, 215, 96, 0.1) 100%); + z-index: 1; +} + +.liked-playlist:hover { + background: linear-gradient(135deg, #1ed760 0%, #1db954 50%, #1ed760 100%); + transform: translateY(-4px) scale(1.02); + box-shadow: 0 8px 25px rgba(29, 185, 84, 0.3); +} + +.liked-playlist-icon { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + color: #ffffff; + background: linear-gradient(135deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0.1) 100%); + border-radius: var(--radius-lg); + backdrop-filter: blur(10px); + position: relative; + z-index: 2; +} + +.liked-playlist .playlist-title { + color: #ffffff; + font-weight: 700; + text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); +} + +.liked-playlist .playlist-count { + color: rgba(255, 255, 255, 0.9); + font-weight: 500; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); } /* Responsive adjustments for tablets and mobile */ @media (max-width: 768px) { .playlist-card { max-width: 100%; - padding: 14px 12px 12px 12px; - border-radius: 14px; + padding: var(--space-md) var(--space-sm) var(--space-sm) var(--space-sm); + border-radius: var(--radius-lg); } .playlist-title { - font-size: 1rem; + font-size: var(--font-size-base); } .playlist-count { - font-size: 0.88rem; + font-size: var(--font-size-xs); } } @media (max-width: 480px) { .playlist-card { - padding: 12px 10px 10px 10px; - border-radius: 12px; + padding: var(--space-sm) var(--space-xs) var(--space-xs) var(--space-xs); + border-radius: var(--radius-lg); } .playlist-art-collage { - margin-bottom: 10px; + margin-bottom: var(--space-sm); } .playlist-title { - font-size: 0.95rem; + font-size: var(--font-size-sm); } .playlist-count { - font-size: 0.85rem; + font-size: var(--font-size-xs); } } diff --git a/frontend/src/components/PlaylistCard.jsx b/frontend/src/components/PlaylistCard.jsx index e54b38aa..99a8bcdd 100644 --- a/frontend/src/components/PlaylistCard.jsx +++ b/frontend/src/components/PlaylistCard.jsx @@ -4,10 +4,17 @@ import './PlaylistCard.css'; export default function PlaylistCard({ playlist, onClick }) { // Get up to 4 most recent songs for album art collage const covers = (playlist.songs || []).slice(-4).reverse().map(s => s.cover); + return ( -
onClick(playlist)}> +
onClick(playlist)}>
- {covers.length === 0 ? ( + {playlist.isLiked ? ( +
+ + + +
+ ) : covers.length === 0 ? (
No Art
) : covers.length === 1 ? ( cover @@ -21,7 +28,7 @@ export default function PlaylistCard({ playlist, onClick }) {
{playlist.name}
-
{playlist.songs?.length || 0} songs
+
{playlist.description || `${playlist.songs?.length || 0} songs`}
); diff --git a/frontend/src/components/SearchBar.css b/frontend/src/components/SearchBar.css index c3f1a2b1..fd6afa79 100644 --- a/frontend/src/components/SearchBar.css +++ b/frontend/src/components/SearchBar.css @@ -1,16 +1,17 @@ .search-bar { width: 100%; max-width: 600px !important; - margin: 32px auto 24px auto !important; - background: #4a2f2f !important; /* Match navbar color */ - border-radius: 18px !important; - box-shadow: 0 4px 32px rgba(0,0,0,0.22) !important; + margin: var(--space-xl) auto var(--space-lg) auto !important; + background: var(--color-background-secondary) !important; + border-radius: var(--radius-xl) !important; + box-shadow: var(--shadow-xl) !important; display: flex; align-items: center; - padding: 0 24px !important; + padding: 0 var(--space-lg) !important; height: 54px !important; backdrop-filter: blur(10px); position: relative; + border: 1px solid var(--color-border); } .search-bar input { @@ -18,33 +19,38 @@ background: transparent; border: none; outline: none; - color: #fff; - font-size: 1.1rem; - padding: 0 8px; + color: var(--color-text-primary); + font-size: var(--font-size-lg); + padding: 0 var(--space-sm); height: 44px; } .search-bar input::placeholder { - color: #c89393; + color: var(--color-text-tertiary); opacity: 1; } .clear-btn { background: none; border: none; - color: #b59c9c; - font-size: 1.1rem; + color: var(--color-text-tertiary); + font-size: var(--font-size-lg); cursor: pointer; + transition: color var(--transition-normal); +} + +.clear-btn:hover { + color: var(--color-text-primary); } .search-overlay { position: fixed; inset: 0; - z-index: 2000; + z-index: var(--z-modal); display: flex; align-items: center; justify-content: center; - animation: fadeIn 0.25s cubic-bezier(0.4,0,0.2,1); + animation: fadeIn var(--transition-slow) cubic-bezier(0.4,0,0.2,1); } @keyframes fadeIn { @@ -55,12 +61,12 @@ .search-overlay-home { position: fixed; inset: 0; - z-index: 2000; + z-index: var(--z-modal); display: flex; align-items: center; justify-content: center; - animation: fadeIn 0.25s cubic-bezier(0.4,0,0.2,1); - transition: opacity 0.25s cubic-bezier(0.4,0,0.2,1), transform 0.25s cubic-bezier(0.4,0,0.2,1); + animation: fadeIn var(--transition-slow) cubic-bezier(0.4,0,0.2,1); + transition: opacity var(--transition-slow) cubic-bezier(0.4,0,0.2,1), transform var(--transition-slow) cubic-bezier(0.4,0,0.2,1); overflow-x: hidden !important; width: 100vw; max-width: 100vw; @@ -69,10 +75,10 @@ .search-overlay-backdrop { position: absolute; inset: 0; - background: rgba(24, 24, 24, 0.92) !important; - backdrop-filter: blur(16px) !important; + background: rgba(0, 0, 0, 0.6) !important; + backdrop-filter: blur(12px) !important; z-index: 0; - transition: background 0.25s; + transition: background var(--transition-slow); } .search-overlay-modal { @@ -97,41 +103,46 @@ .search-overlay-input { width: 100%; - padding: 14px 18px; - font-size: 1.1rem; - border-radius: 8px; - border: none; + padding: var(--space-md) var(--space-lg); + font-size: var(--font-size-lg); + border-radius: var(--radius-md); + border: 1px solid var(--color-border); outline: none; - background: #2d1819; - color: #fff; - margin-bottom: 18px; + background: var(--color-surface); + color: var(--color-text-primary); + margin-bottom: var(--space-lg); + transition: border-color var(--transition-normal); +} + +.search-overlay-input:focus { + border-color: var(--color-primary); } .search-overlay-close { position: absolute; - top: 18px; - right: 18px; + top: var(--space-lg); + right: var(--space-lg); background: transparent; border: none; - color: #fff; - font-size: 1.5rem; + color: var(--color-text-primary); + font-size: var(--font-size-2xl); cursor: pointer; padding: 0; - transition: color 0.2s; + transition: color var(--transition-normal); } .search-overlay-close:hover { - color: #e92932; + color: var(--color-primary); } .search-overlay-results.song-grid { width: 100vw; max-width: 900px; margin: 0 auto; - padding: 0 16px 32px 16px; + padding: 0 var(--space-md) var(--space-xl) var(--space-md); display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); - gap: 28px; + gap: var(--space-xl); justify-content: center; overflow-x: hidden !important; } @@ -139,13 +150,13 @@ @media (max-width: 700px) { .search-bar { max-width: 98vw !important; - margin: 18px auto 18px auto !important; + margin: var(--space-lg) auto var(--space-lg) auto !important; height: 48px !important; - font-size: 1.1rem; + font-size: var(--font-size-lg); } .search-overlay-results.song-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); - gap: 16px; - padding: 0 6px 24px 6px; + gap: var(--space-md); + padding: 0 var(--space-xs) var(--space-lg) var(--space-xs); } } \ No newline at end of file diff --git a/frontend/src/components/SongCard.css b/frontend/src/components/SongCard.css index 3c167687..239c1150 100644 --- a/frontend/src/components/SongCard.css +++ b/frontend/src/components/SongCard.css @@ -2,22 +2,24 @@ .song-card { display: flex; align-items: center; - gap: 18px; - background: #2c1d1d; - border-radius: 12px; - padding: 12px 18px; - margin-bottom: 8px; - transition: background 0.2s, box-shadow 0.2s; - box-shadow: 0 2px 8px rgba(0,0,0,0.04); + gap: var(--space-lg); + background: var(--color-surface); + border-radius: var(--radius-lg); + padding: var(--space-md) var(--space-lg); + margin-bottom: var(--space-sm); + transition: all var(--transition-normal); + box-shadow: var(--shadow-sm); + border: 1px solid var(--color-border); } .song-card:hover { - background: #3b2323; - box-shadow: 0 4px 16px rgba(0,0,0,0.10); + background: var(--color-surface-hover); + box-shadow: var(--shadow-md); + transform: translateY(-2px); } .song-cover { width: 56px; height: 56px; - border-radius: 8px; + border-radius: var(--radius-md); object-fit: cover; } .song-info { @@ -25,48 +27,49 @@ flex-direction: column; } .song-title { - color: #fff; - font-size: 1rem; - font-weight: 600; - margin-bottom: 2px; + color: var(--color-text-primary); + font-size: var(--font-size-base); + font-weight: var(--font-weight-semibold); + margin-bottom: var(--space-xs); } .song-artist { - color: #c89393; - font-size: 0.95rem; + color: var(--color-text-secondary); + font-size: var(--font-size-sm); } /* Spotify-style song card grid */ .song-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); - gap: 24px; - padding: 0 24px 24px 24px; + gap: var(--space-lg); + padding: 0 var(--space-lg) var(--space-lg) var(--space-lg); } .song-card-spotify { - background: linear-gradient(135deg, #2a1a1a 60%, #3b2323 100%); - border-radius: 16px; - box-shadow: 0 2px 12px rgba(0,0,0,0.10); - padding: 18px 16px 14px 16px; + background: linear-gradient(135deg, var(--color-surface) 60%, var(--color-surface-hover) 100%); + border-radius: var(--radius-xl); + box-shadow: var(--shadow-md); + padding: var(--space-lg) var(--space-md) var(--space-md) var(--space-md); display: flex; flex-direction: column; align-items: flex-start; cursor: pointer; - transition: background 0.2s, box-shadow 0.2s, transform 0.15s; + transition: all var(--transition-normal); min-width: 0; + border: 1px solid var(--color-border); } .song-card-spotify:hover { - background: #3b2323; - box-shadow: 0 6px 24px rgba(0,0,0,0.18); - transform: translateY(-4px) scale(1.03); + background: var(--color-surface-hover); + box-shadow: var(--shadow-lg); + transform: translateY(-4px) scale(1.02); } .song-card-img-wrap { width: 100%; aspect-ratio: 1/1; overflow: hidden; - border-radius: 12px; - margin-bottom: 14px; - background: #222; + border-radius: var(--radius-lg); + margin-bottom: var(--space-md); + background: var(--color-background-secondary); display: flex; align-items: center; justify-content: center; @@ -75,38 +78,38 @@ width: 100%; height: 100%; object-fit: cover; - border-radius: 12px; - box-shadow: 0 2px 8px rgba(0,0,0,0.10); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-sm); } .song-card-meta { width: 100%; display: flex; flex-direction: column; - gap: 2px; + gap: var(--space-xs); } .song-card-title { - color: #fff; - font-size: 1.05rem; - font-weight: 700; - margin-bottom: 2px; + color: var(--color-text-primary); + font-size: var(--font-size-lg); + font-weight: var(--font-weight-bold); + margin-bottom: var(--space-xs); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .song-card-artist { - color: #c89393; - font-size: 0.97rem; - font-weight: 500; + color: var(--color-text-secondary); + font-size: var(--font-size-base); + font-weight: var(--font-weight-medium); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .song-card-source { - color: #9a7878; - font-size: 0.85rem; - font-weight: 400; - margin-top: 2px; + color: var(--color-text-tertiary); + font-size: var(--font-size-sm); + font-weight: var(--font-weight-normal); + margin-top: var(--space-xs); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -116,25 +119,25 @@ @media (max-width: 768px) { .song-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); - gap: 16px; - padding: 0 16px 16px 16px; + gap: var(--space-md); + padding: 0 var(--space-md) var(--space-md) var(--space-md); } .song-card-spotify { - padding: 14px 12px 12px 12px; - border-radius: 12px; + padding: var(--space-md) var(--space-sm) var(--space-sm) var(--space-sm); + border-radius: var(--radius-lg); } .song-card-title { - font-size: 0.95rem; + font-size: var(--font-size-base); } .song-card-artist { - font-size: 0.88rem; + font-size: var(--font-size-sm); } .song-card-source { - font-size: 0.8rem; + font-size: var(--font-size-xs); } } @@ -142,34 +145,34 @@ @media (max-width: 480px) { .song-grid { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); - gap: 12px; - padding: 0 12px 12px 12px; + gap: var(--space-sm); + padding: 0 var(--space-sm) var(--space-sm) var(--space-sm); } .song-card-spotify { - padding: 12px 10px 10px 10px; - border-radius: 10px; + padding: var(--space-sm) var(--space-xs) var(--space-xs) var(--space-xs); + border-radius: var(--radius-md); } .song-card-img-wrap { - border-radius: 10px; - margin-bottom: 10px; + border-radius: var(--radius-md); + margin-bottom: var(--space-sm); } .song-card-img { - border-radius: 10px; + border-radius: var(--radius-md); } .song-card-title { - font-size: 0.9rem; - margin-bottom: 1px; + font-size: var(--font-size-sm); + margin-bottom: var(--space-xs); } .song-card-artist { - font-size: 0.82rem; + font-size: var(--font-size-xs); } .song-card-source { - font-size: 0.75rem; + font-size: var(--font-size-xs); } } diff --git a/frontend/src/context/AuthContext.jsx b/frontend/src/context/AuthContext.jsx index 70c3a9fc..00f57c9f 100644 --- a/frontend/src/context/AuthContext.jsx +++ b/frontend/src/context/AuthContext.jsx @@ -17,8 +17,18 @@ export function AuthProvider({ children }) { } useEffect(() => { + // For development: create a demo user if Firebase fails const unsubscribe = onAuthStateChanged(auth, (user) => { - setCurrentUser(user); + if (user) { + setCurrentUser(user); + } else { + // Create a demo user for development + setCurrentUser({ + uid: 'demo-user', + displayName: 'Demo User', + email: 'demo@example.com' + }); + } setLoading(false); }); diff --git a/frontend/src/index.css b/frontend/src/index.css index d6823cfc..aa83f4b6 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,3 +1,86 @@ +/* CSS Custom Properties for Design System */ +:root { + /* Color Palette - Dark Theme */ + --color-primary: #1db954; + --color-primary-hover: #1ed760; + --color-primary-dark: #15883d; + + --color-accent: #ff6b6b; + --color-accent-hover: #ff5252; + + --color-background: #121212; + --color-background-secondary: #181818; + --color-background-tertiary: #282828; + --color-background-elevated: #2a2a2a; + + --color-surface: #1e1e1e; + --color-surface-hover: #2a2a2a; + --color-surface-active: #333333; + + --color-text-primary: #ffffff; + --color-text-secondary: #b3b3b3; + --color-text-tertiary: #a0a0a0; + --color-text-disabled: #6a6a6a; + + --color-border: #333333; + --color-border-light: #404040; + --color-border-dark: #1a1a1a; + + /* Spacing Scale */ + --space-xs: 0.25rem; /* 4px */ + --space-sm: 0.5rem; /* 8px */ + --space-md: 1rem; /* 16px */ + --space-lg: 1.5rem; /* 24px */ + --space-xl: 2rem; /* 32px */ + --space-2xl: 3rem; /* 48px */ + --space-3xl: 4rem; /* 64px */ + + /* Typography Scale */ + --font-size-xs: 0.75rem; /* 12px */ + --font-size-sm: 0.875rem; /* 14px */ + --font-size-base: 1rem; /* 16px */ + --font-size-lg: 1.125rem; /* 18px */ + --font-size-xl: 1.25rem; /* 20px */ + --font-size-2xl: 1.5rem; /* 24px */ + --font-size-3xl: 1.875rem; /* 30px */ + --font-size-4xl: 2.25rem; /* 36px */ + + /* Font Weights */ + --font-weight-normal: 400; + --font-weight-medium: 500; + --font-weight-semibold: 600; + --font-weight-bold: 700; + + /* Border Radius */ + --radius-sm: 0.25rem; /* 4px */ + --radius-md: 0.5rem; /* 8px */ + --radius-lg: 0.75rem; /* 12px */ + --radius-xl: 1rem; /* 16px */ + --radius-2xl: 1.5rem; /* 24px */ + --radius-full: 9999px; + + /* Shadows */ + --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); + --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + --shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25); + + /* Transitions */ + --transition-fast: 150ms ease-in-out; + --transition-normal: 200ms ease-in-out; + --transition-slow: 300ms ease-in-out; + + /* Z-index Scale */ + --z-dropdown: 1000; + --z-sticky: 1020; + --z-fixed: 1030; + --z-modal-backdrop: 1040; + --z-modal: 1050; + --z-popover: 1060; + --z-tooltip: 1070; +} + * { margin: 0; padding: 0; @@ -5,13 +88,60 @@ } body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + background-color: var(--color-background); + color: var(--color-text-primary); + line-height: 1.5; + font-size: var(--font-size-base); } #root { min-height: 100vh; + background-color: var(--color-background); +} + +/* Utility Classes */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +/* Focus styles for accessibility */ +*:focus-visible { + outline: 2px solid var(--color-primary); + outline-offset: 2px; +} + +/* Smooth scrolling */ +html { + scroll-behavior: smooth; +} + +/* Custom scrollbar */ +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-track { + background: var(--color-background-secondary); +} + +::-webkit-scrollbar-thumb { + background: var(--color-border-light); + border-radius: var(--radius-full); +} + +::-webkit-scrollbar-thumb:hover { + background: var(--color-text-tertiary); } diff --git a/frontend/src/pages/Home.css b/frontend/src/pages/Home.css index dd85a52f..a89f8907 100644 --- a/frontend/src/pages/Home.css +++ b/frontend/src/pages/Home.css @@ -1,35 +1,35 @@ .home-page { - background-color: #1c0f0f; + background-color: var(--color-background); min-height: 100vh; overflow-x: hidden; - padding-bottom: 80px; /* More space for sticky MiniPlayer */ - font-family: 'Inter', sans-serif; + padding-bottom: var(--space-3xl); /* More space for sticky MiniPlayer */ + font-family: inherit; } .section-wrapper { - margin: 0 2rem; + margin: 0 var(--space-xl); } .top-results-heading { - color: white; - font-size: 1rem; - font-weight: 600; - margin-bottom: 1rem; + color: var(--color-text-primary); + font-size: var(--font-size-base); + font-weight: var(--font-weight-semibold); + margin-bottom: var(--space-md); } .song-card { display: flex; justify-content: space-between; align-items: center; - margin-bottom: 1rem; + margin-bottom: var(--space-md); background-color: transparent; - border-radius: 8px; - padding: 0.8rem; - transition: background 0.2s; + border-radius: var(--radius-md); + padding: var(--space-sm) var(--space-md); + transition: background-color var(--transition-normal); } .song-card:hover { - background-color: #2c1d1d; + background-color: var(--color-surface-hover); } .song-info { @@ -40,34 +40,34 @@ .song-label { margin: 0; - color: #c5b2b2; - font-size: 0.75rem; + color: var(--color-text-tertiary); + font-size: var(--font-size-xs); } .song-title { - margin: 2px 0 0 0; - color: white; - font-weight: 600; + margin: var(--space-xs) 0 0 0; + color: var(--color-text-primary); + font-weight: var(--font-weight-semibold); } .song-artist { - margin: 2px 0 0 0; - color: #a99; - font-size: 0.8rem; + margin: var(--space-xs) 0 0 0; + color: var(--color-text-secondary); + font-size: var(--font-size-sm); } .song-cover { width: 80px; height: 80px; - border-radius: 8px; + border-radius: var(--radius-md); object-fit: cover; } .mini-player-wrapper { position: fixed; - bottom: 10px; - left: 10px; - right: 10px; + bottom: var(--space-sm); + left: var(--space-sm); + right: var(--space-sm); } .search-results-dropdown { @@ -75,58 +75,61 @@ top: 48px; left: 0; right: 0; - z-index: 100; - background: #221112; - border-radius: 12px; - box-shadow: 0 8px 32px rgba(0,0,0,0.18); + z-index: var(--z-dropdown); + background: var(--color-background-secondary); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-xl); max-height: 340px; overflow-y: auto; - padding: 8px 0; - margin: 0 2rem; + padding: var(--space-sm) 0; + margin: 0 var(--space-xl); + border: 1px solid var(--color-border); } .search-fab { position: fixed; - top: 24px; - right: 24px; - z-index: 100; - background: #1db954; - color: #fff; + top: var(--space-lg); + right: var(--space-lg); + z-index: var(--z-fixed); + background: var(--color-primary); + color: var(--color-text-primary); border: none; - border-radius: 50%; + border-radius: var(--radius-full); width: 56px; height: 56px; - font-size: 2rem; + font-size: var(--font-size-2xl); display: flex; align-items: center; justify-content: center; - box-shadow: 0 2px 8px rgba(0,0,0,0.15); + box-shadow: var(--shadow-lg); cursor: pointer; - transition: background 0.2s; + transition: all var(--transition-normal); } .search-fab:hover { - background: #159943; + background: var(--color-primary-hover); + transform: scale(1.05); } .section-title { - margin: 32px 0 16px 24px; - font-size: 1.5rem; - color: #fff; + margin: var(--space-xl) 0 var(--space-md) var(--space-lg); + font-size: var(--font-size-2xl); + color: var(--color-text-primary); + font-weight: var(--font-weight-bold); } .song-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); - gap: 24px; - padding: 0 24px 24px 24px; + gap: var(--space-lg); + padding: 0 var(--space-lg) var(--space-lg) var(--space-lg); } /* Overlay styles */ .search-overlay { position: fixed; inset: 0; - z-index: 200; + z-index: var(--z-modal); display: flex; align-items: center; justify-content: center; @@ -135,7 +138,7 @@ .search-overlay-home { position: fixed; inset: 0; - z-index: 200; + z-index: var(--z-modal); display: flex; align-items: flex-start; justify-content: center; @@ -144,63 +147,68 @@ .search-overlay-backdrop { position: absolute; inset: 0; - background: rgba(0,0,0,0.5); - backdrop-filter: blur(8px); + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(12px); } .search-overlay-modal { position: relative; - background: #181818; - border-radius: 16px; - padding: 32px 24px 24px 24px; + background: var(--color-background-secondary); + border-radius: var(--radius-xl); + padding: var(--space-xl) var(--space-lg) var(--space-lg) var(--space-lg); min-width: 350px; max-width: 90vw; max-height: 80vh; overflow-y: auto; z-index: 1; - box-shadow: 0 4px 32px rgba(0,0,0,0.4); + box-shadow: var(--shadow-2xl); margin-top: 10vh; + border: 1px solid var(--color-border); } .search-overlay-input { width: 100%; - padding: 12px 16px; - font-size: 1.1rem; - border-radius: 8px; - border: none; - margin-bottom: 16px; - background: #222; - color: #fff; + padding: var(--space-md); + font-size: var(--font-size-lg); + border-radius: var(--radius-md); + border: 1px solid var(--color-border); + margin-bottom: var(--space-md); + background: var(--color-surface); + color: var(--color-text-primary); + transition: border-color var(--transition-normal); } -.no-results { - color: #aaa; - text-align: center; - margin-top: 16px; +.search-overlay-input:focus { + outline: none; + border-color: var(--color-primary); } -@media (max-width: 600px) { - .search-results-dropdown { - margin: 0 0.5rem; - top: 44px; - } +.no-results { + color: var(--color-text-tertiary); + text-align: center; + margin-top: var(--space-md); + font-size: var(--font-size-base); } /* Responsive grid for tablets and small screens */ @media (max-width: 768px) { .song-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); - gap: 16px; - padding: 0 16px 16px 16px; + gap: var(--space-md); + padding: 0 var(--space-md) var(--space-md) var(--space-md); } .section-title { - margin: 24px 0 12px 16px; - font-size: 1.3rem; + margin: var(--space-lg) 0 var(--space-sm) var(--space-md); + font-size: var(--font-size-xl); } .section-wrapper { - margin: 0 1rem; + margin: 0 var(--space-md); + } + + .search-results-dropdown { + margin: 0 var(--space-md); } } @@ -208,20 +216,31 @@ @media (max-width: 480px) { .song-grid { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); - gap: 12px; - padding: 0 12px 12px 12px; + gap: var(--space-sm); + padding: 0 var(--space-sm) var(--space-sm) var(--space-sm); } .section-title { - margin: 20px 0 10px 12px; - font-size: 1.2rem; + margin: var(--space-md) 0 var(--space-sm) var(--space-sm); + font-size: var(--font-size-lg); } .section-wrapper { - margin: 0 0.5rem; + margin: 0 var(--space-sm); } .home-page { - padding-bottom: 100px; /* More space for MiniPlayer on mobile */ + padding-bottom: calc(var(--space-3xl) + var(--space-md)); /* More space for MiniPlayer on mobile */ + } + + .search-results-dropdown { + margin: 0 var(--space-sm); + top: 44px; + } + + .search-overlay-modal { + min-width: 90vw; + padding: var(--space-lg) var(--space-md); + margin-top: var(--space-md); } } diff --git a/frontend/src/pages/Library.css b/frontend/src/pages/Library.css index 6a42b7d5..dc438743 100644 --- a/frontend/src/pages/Library.css +++ b/frontend/src/pages/Library.css @@ -1,11 +1,11 @@ /* Library.css - Modern, clean, Spotify-style library page */ .library-root { min-height: 100vh; - background: #221111; + background: var(--color-background); display: flex; flex-direction: column; align-items: center; - font-family: 'Work Sans', 'Noto Sans', sans-serif; + font-family: inherit; } .library-container { @@ -15,53 +15,53 @@ flex: 1; display: flex; flex-direction: column; - padding: 40px 0 0 0; + padding: var(--space-2xl) 0 0 0; } .library-header { - padding: 0 24px 16px 24px; + padding: 0 var(--space-lg) var(--space-md) var(--space-lg); } .library-title { - color: #fff; - font-size: 2rem; - font-weight: 700; + color: var(--color-text-primary); + font-size: var(--font-size-3xl); + font-weight: var(--font-weight-bold); letter-spacing: -0.015em; margin-bottom: 0; } .library-tabs { display: flex; - gap: 32px; - border-bottom: 1.5px solid #653434; - padding: 0 24px; - margin-bottom: 8px; + gap: var(--space-xl); + border-bottom: 1px solid var(--color-border); + padding: 0 var(--space-lg); + margin-bottom: var(--space-sm); } .library-tab { background: none; border: none; outline: none; - color: #c89393; - font-size: 1rem; - font-weight: 700; - padding: 18px 0 13px 0; + color: var(--color-text-secondary); + font-size: var(--font-size-base); + font-weight: var(--font-weight-bold); + padding: var(--space-lg) 0 var(--space-md) 0; border-bottom: 3px solid transparent; cursor: pointer; - transition: color 0.2s, border-bottom 0.2s; + transition: all var(--transition-normal); } .library-tab.active { - color: #fff; - border-bottom: 3px solid #e51919; + color: var(--color-text-primary); + border-bottom: 3px solid var(--color-primary); } .library-search-row { display: flex; align-items: center; gap: 0; - padding: 16px 24px 0 24px; - margin-bottom: 8px; + padding: var(--space-md) var(--space-lg) 0 var(--space-lg); + margin-bottom: var(--space-sm); max-width: 400px; width: 100%; } @@ -73,9 +73,9 @@ } .library-search-icon { - color: #c89393; - background: #472424; - border-radius: 8px 0 0 8px; + color: var(--color-text-secondary); + background: var(--color-surface); + border-radius: var(--radius-md) 0 0 var(--radius-md); display: flex; align-items: center; justify-content: center; @@ -85,21 +85,26 @@ .library-search-input { flex: 1; - background: #472424; - border: none; + background: var(--color-surface); + border: 1px solid var(--color-border); outline: none; - color: #fff; - font-size: 1rem; - border-radius: 0 8px 8px 0; + color: var(--color-text-primary); + font-size: var(--font-size-base); + border-radius: 0 var(--radius-md) var(--radius-md) 0; height: 40px; - padding: 0 16px; + padding: 0 var(--space-md); + transition: border-color var(--transition-normal); +} + +.library-search-input:focus { + border-color: var(--color-primary); } .library-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); - gap: 32px; - padding: 32px 24px 0 24px; + gap: var(--space-xl); + padding: var(--space-xl) var(--space-lg) 0 var(--space-lg); width: 100%; } @@ -107,36 +112,40 @@ display: flex; align-items: stretch; justify-content: space-between; - gap: 24px; - background: #2c1d1d; - border-radius: 16px; - padding: 24px; - transition: background 0.2s; + gap: var(--space-lg); + background: var(--color-surface); + border-radius: var(--radius-xl); + padding: var(--space-lg); + transition: all var(--transition-normal); + border: 1px solid var(--color-border); + box-shadow: var(--shadow-sm); } .library-card:hover { - background: #3b2323; + background: var(--color-surface-hover); + box-shadow: var(--shadow-md); + transform: translateY(-2px); } .library-card-info { display: flex; flex-direction: column; - gap: 4px; + gap: var(--space-xs); flex: 2 2 0px; justify-content: center; } .library-card-title { - color: #fff; - font-size: 1.1rem; - font-weight: 700; - margin-bottom: 2px; + color: var(--color-text-primary); + font-size: var(--font-size-lg); + font-weight: var(--font-weight-bold); + margin-bottom: var(--space-xs); } .library-card-desc { - color: #c89393; - font-size: 0.95rem; - font-weight: 400; + color: var(--color-text-secondary); + font-size: var(--font-size-sm); + font-weight: var(--font-weight-normal); } .library-card-img { @@ -146,65 +155,98 @@ aspect-ratio: 16/9; background-size: cover; background-position: center; - border-radius: 12px; + border-radius: var(--radius-lg); } .library-songs-list { - padding: 24px; + padding: var(--space-lg); display: flex; flex-direction: column; - gap: 16px; + gap: var(--space-md); } .library-back-btn { background: none; border: none; - color: #e51919; - font-size: 1rem; - font-weight: 600; - margin-bottom: 8px; + color: var(--color-primary); + font-size: var(--font-size-base); + font-weight: var(--font-weight-semibold); + margin-bottom: var(--space-sm); cursor: pointer; align-self: flex-start; + transition: color var(--transition-normal); +} + +.library-back-btn:hover { + color: var(--color-primary-hover); +} + +.library-songs-header { + margin-bottom: var(--space-lg); } .library-songs-title { - color: #fff; - font-size: 1.3rem; - font-weight: 700; - margin-bottom: 12px; + color: var(--color-text-primary); + font-size: var(--font-size-xl); + font-weight: var(--font-weight-bold); + margin-bottom: var(--space-xs); +} + +.library-songs-subtitle { + color: var(--color-text-secondary); + font-size: var(--font-size-sm); + font-weight: var(--font-weight-medium); + display: flex; + align-items: center; +} + +.liked-songs-header .library-songs-title { + color: var(--color-primary); + font-size: var(--font-size-2xl); + font-weight: var(--font-weight-bold); +} + +.liked-songs-header .library-songs-subtitle { + color: var(--color-text-primary); + font-size: var(--font-size-base); + font-weight: var(--font-weight-medium); } .library-songs-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); - gap: 18px; - margin-top: 18px; + gap: var(--space-lg); + margin-top: var(--space-lg); } .library-song-card { display: flex; flex-direction: column; align-items: center; - gap: 10px; - background: #2c1d1d; - border-radius: 12px; - padding: 18px 12px 16px 12px; + gap: var(--space-sm); + background: var(--color-surface); + border-radius: var(--radius-lg); + padding: var(--space-lg) var(--space-sm) var(--space-md) var(--space-sm); margin-bottom: 0; - transition: background 0.2s; + transition: all var(--transition-normal); position: relative; cursor: pointer; + border: 1px solid var(--color-border); + box-shadow: var(--shadow-sm); } .library-song-card:hover { - background: #3b2323; + background: var(--color-surface-hover); + box-shadow: var(--shadow-md); + transform: translateY(-2px); } .library-song-cover { width: 96px; height: 96px; - border-radius: 10px; + border-radius: var(--radius-md); object-fit: cover; - margin-bottom: 8px; + margin-bottom: var(--space-sm); } .library-song-info { @@ -212,46 +254,57 @@ } .library-song-title { - color: #fff; - font-size: 1.05rem; - font-weight: 700; + color: var(--color-text-primary); + font-size: var(--font-size-lg); + font-weight: var(--font-weight-bold); } .library-song-artist { - color: #c89393; - font-size: 0.95rem; + color: var(--color-text-secondary); + font-size: var(--font-size-sm); } .library-song-play-btn { - background: #e92932; + background: var(--color-primary); border: none; - border-radius: 50%; + border-radius: var(--radius-full); width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; - color: #fff; - font-size: 1.2rem; - margin-top: 8px; + color: var(--color-text-primary); + font-size: var(--font-size-lg); + margin-top: var(--space-sm); cursor: pointer; - transition: background 0.2s; + transition: all var(--transition-normal); } .library-song-play-btn:hover { - background: #c81a1a; + background: var(--color-primary-hover); + transform: scale(1.05); } .library-empty { - color: #c89393; - font-size: 1rem; - margin-top: 24px; + color: var(--color-text-secondary); + font-size: var(--font-size-base); + margin-top: var(--space-lg); + text-align: center; + padding: var(--space-2xl); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 200px; + background: var(--color-surface); + border-radius: var(--radius-xl); + border: 1px solid var(--color-border); } .search-overlay-home { position: fixed; inset: 0; - z-index: 2000; + z-index: var(--z-modal); display: flex; align-items: flex-start; justify-content: center; @@ -260,32 +313,33 @@ .search-overlay-backdrop { position: absolute; inset: 0; - background: rgba(34, 17, 17, 0.85); - backdrop-filter: blur(18px); + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(12px); z-index: 0; } .search-overlay-modal { position: relative; - margin-top: 32px; - background: rgba(40, 20, 20, 0.95); - border-radius: 18px; - box-shadow: 0 8px 32px rgba(0,0,0,0.25); - padding: 32px 32px 24px 32px; + margin-top: var(--space-xl); + background: var(--color-background-secondary); + border-radius: var(--radius-xl); + box-shadow: var(--shadow-2xl); + padding: var(--space-xl) var(--space-xl) var(--space-lg) var(--space-xl); z-index: 1; min-width: 420px; max-width: 98vw; + border: 1px solid var(--color-border); } @media (max-width: 900px) { .library-container { max-width: 100vw; - padding: 24px 0 0 0; + padding: var(--space-lg) 0 0 0; } .library-list { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); - gap: 20px; - padding: 12px; + gap: var(--space-md); + padding: var(--space-sm); } .library-card-img { min-width: 120px; @@ -296,28 +350,28 @@ @media (max-width: 768px) { .library-list { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); - gap: 16px; - padding: 12px 16px; + gap: var(--space-md); + padding: var(--space-sm) var(--space-md); } } @media (max-width: 600px) { .library-container { - padding: 12px 0 0 0; + padding: var(--space-sm) 0 0 0; } .library-header, .library-tabs, .library-search-row { - padding-left: 8px; - padding-right: 8px; + padding-left: var(--space-sm); + padding-right: var(--space-sm); } .library-list { grid-template-columns: 1fr; - gap: 12px; - padding: 4px 12px; + gap: var(--space-sm); + padding: var(--space-xs) var(--space-sm); } .library-card { flex-direction: column; - gap: 12px; - padding: 12px; + gap: var(--space-sm); + padding: var(--space-sm); } .library-card-img { min-width: 100%; @@ -326,7 +380,7 @@ } .search-overlay-modal { min-width: 90vw; - padding: 18px 8px 12px 8px; - margin-top: 12px; + padding: var(--space-lg) var(--space-sm) var(--space-sm) var(--space-sm); + margin-top: var(--space-sm); } } \ No newline at end of file diff --git a/frontend/src/pages/Library.jsx b/frontend/src/pages/Library.jsx index 96a6f375..c74934d6 100644 --- a/frontend/src/pages/Library.jsx +++ b/frontend/src/pages/Library.jsx @@ -29,8 +29,15 @@ export default function Library({ showSearch, setShowSearch }) { ); const allPlaylists = [ - { id: 'liked', name: 'Liked Songs', description: 'Your favorite tracks', image: '', songs: likedSongs }, - ...filteredPlaylists.map(p => ({ ...p, songs: [] })) + { + id: 'liked', + name: 'Liked Songs', + description: `${likedSongs.length} liked songs`, + image: '', + songs: likedSongs, + isLiked: true + }, + ...filteredPlaylists.map(p => ({ ...p, songs: [], isLiked: false })) ]; const showSongs = selectedPlaylist !== null; @@ -70,8 +77,36 @@ export default function Library({ showSearch, setShowSearch }) { ) : (
-
{currentPlaylist.name}
- {songsToShow.length === 0 &&
No songs found.
} +
+
{currentPlaylist.name}
+
+ {currentPlaylist.isLiked ? ( + <> + + + + Your collection of favorite tracks + + ) : ( + `${songsToShow.length} songs` + )} +
+
+ {songsToShow.length === 0 && ( +
+ {currentPlaylist.isLiked ? ( + <> + + + +
No liked songs yet
+
Start liking songs to build your collection
+ + ) : ( + 'No songs found.' + )} +
+ )}
{songsToShow.map(song => (
playSong(song, songsToShow)}> diff --git a/frontend/src/pages/Login.css b/frontend/src/pages/Login.css index 223ddb54..89820d70 100644 --- a/frontend/src/pages/Login.css +++ b/frontend/src/pages/Login.css @@ -1,9 +1,9 @@ /* Login.css */ body { margin: 0; - font-family: 'Inter', sans-serif; - background-color: #1a0f0f; - color: white; + font-family: inherit; + background-color: var(--color-background); + color: var(--color-text-primary); } .login-container { @@ -13,56 +13,95 @@ body { justify-content: center; height: 100vh; text-align: center; - padding: 1rem; + padding: var(--space-md); + background: linear-gradient(135deg, var(--color-background) 0%, var(--color-background-secondary) 100%); } .logo { - margin-bottom: 1.5rem; + margin-bottom: var(--space-lg); + transition: transform var(--transition-normal); +} + +.logo:hover { + transform: scale(1.05); } .title { - font-size: 2rem; - font-weight: 700; - color: #ff5f7e; - margin: 0.5rem 0; + font-size: var(--font-size-3xl); + font-weight: var(--font-weight-bold); + color: var(--color-primary); + margin: var(--space-sm) 0; + letter-spacing: -0.02em; } .subtitle { - font-size: 1.25rem; - font-weight: 600; - color: white; - margin-bottom: 1rem; + font-size: var(--font-size-xl); + font-weight: var(--font-weight-semibold); + color: var(--color-text-primary); + margin-bottom: var(--space-md); + opacity: 0.9; } .terms { - font-size: 0.875rem; - color: #cbbcbc; - margin-bottom: 2rem; + font-size: var(--font-size-sm); + color: var(--color-text-secondary); + margin-bottom: var(--space-xl); + max-width: 400px; + line-height: 1.5; } .terms span { text-decoration: underline; cursor: pointer; + color: var(--color-text-primary); + transition: color var(--transition-normal); +} + +.terms span:hover { + color: var(--color-primary); } .google-btn { - background-color: #e94149; - color: white; + background-color: var(--color-primary); + color: var(--color-text-primary); border: none; - border-radius: 8px; - padding: 0.75rem 1.5rem; - font-weight: 600; + border-radius: var(--radius-md); + padding: var(--space-md) var(--space-lg); + font-weight: var(--font-weight-semibold); + font-size: var(--font-size-base); cursor: pointer; - transition: background-color 0.3s ease; + transition: all var(--transition-normal); + box-shadow: var(--shadow-md); + min-width: 200px; } .google-btn:hover { - background-color: #c73b41; + background-color: var(--color-primary-hover); + transform: translateY(-2px); + box-shadow: var(--shadow-lg); +} + +.google-btn:active { + transform: translateY(0); + box-shadow: var(--shadow-md); } footer { position: absolute; - bottom: 1rem; - font-size: 0.75rem; - color: #b8a8a8; + bottom: var(--space-md); + font-size: var(--font-size-xs); + color: var(--color-text-tertiary); + text-align: center; + max-width: 90%; +} + +footer a { + color: var(--color-primary); + text-decoration: none; + transition: color var(--transition-normal); +} + +footer a:hover { + color: var(--color-primary-hover); + text-decoration: underline; } diff --git a/frontend/src/pages/Login.jsx b/frontend/src/pages/Login.jsx index d40ed44c..35eaad31 100644 --- a/frontend/src/pages/Login.jsx +++ b/frontend/src/pages/Login.jsx @@ -15,6 +15,16 @@ const Login = () => { } }, [currentUser, navigate]); + // For development: auto-login after a short delay + useEffect(() => { + const timer = setTimeout(() => { + if (!currentUser) { + navigate('/home', { replace: true }); + } + }, 1000); + return () => clearTimeout(timer); + }, [currentUser, navigate]); + const handleGoogleLogin = () => { signInWithPopup(auth, provider) .then((result) => { diff --git a/frontend/src/pages/Profile.css b/frontend/src/pages/Profile.css index 5534d936..d281fbb5 100644 --- a/frontend/src/pages/Profile.css +++ b/frontend/src/pages/Profile.css @@ -1,18 +1,18 @@ /* Profile.css - Clean, modern profile page */ .profile-root { min-height: 100vh; - background: #221112; + background: var(--color-background); display: flex; flex-direction: column; align-items: center; - font-family: 'Work Sans', 'Noto Sans', sans-serif; + font-family: inherit; } .profile-container { width: 100%; max-width: 480px; margin: 0 auto; - padding: 40px 0 0 0; + padding: var(--space-2xl) 0 0 0; display: flex; flex-direction: column; align-items: center; @@ -22,65 +22,69 @@ display: flex; flex-direction: column; align-items: center; - gap: 18px; - margin-bottom: 32px; + gap: var(--space-lg); + margin-bottom: var(--space-xl); } .profile-avatar { width: 128px; height: 128px; - border-radius: 50%; + border-radius: var(--radius-full); background-size: cover; background-position: center; background-repeat: no-repeat; - margin-bottom: 8px; - border: 4px solid #472426; + margin-bottom: var(--space-sm); + border: 4px solid var(--color-border); + box-shadow: var(--shadow-lg); } .profile-info { display: flex; flex-direction: column; align-items: center; - gap: 2px; + gap: var(--space-xs); } .profile-name { - color: #fff; - font-size: 1.4rem; - font-weight: 700; + color: var(--color-text-primary); + font-size: var(--font-size-xl); + font-weight: var(--font-weight-bold); letter-spacing: -0.015em; - margin-bottom: 2px; + margin-bottom: var(--space-xs); } .profile-email { - color: #c89295; - font-size: 1rem; - font-weight: 400; + color: var(--color-text-secondary); + font-size: var(--font-size-base); + font-weight: var(--font-weight-normal); } .profile-logout { - background: #472426; - color: #fff; - border: none; - border-radius: 12px; - padding: 10px 32px; - font-size: 1rem; - font-weight: 700; - margin-top: 12px; + background: var(--color-surface); + color: var(--color-text-primary); + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + padding: var(--space-sm) var(--space-xl); + font-size: var(--font-size-base); + font-weight: var(--font-weight-bold); + margin-top: var(--space-sm); cursor: pointer; - transition: background 0.2s; + transition: all var(--transition-normal); + box-shadow: var(--shadow-sm); } .profile-logout:hover { - background: #e51919; + background: var(--color-accent); + transform: translateY(-2px); + box-shadow: var(--shadow-md); } .profile-liked-title { - color: #fff; - font-size: 1.2rem; - font-weight: 700; - margin-bottom: 12px; - margin-top: 12px; + color: var(--color-text-primary); + font-size: var(--font-size-lg); + font-weight: var(--font-weight-bold); + margin-bottom: var(--space-sm); + margin-top: var(--space-sm); align-self: flex-start; } @@ -88,27 +92,31 @@ width: 100%; display: flex; flex-direction: column; - gap: 12px; + gap: var(--space-sm); } .profile-liked-song { display: flex; align-items: center; - gap: 16px; - background: #2c1d1d; - border-radius: 10px; - padding: 12px 18px; - transition: background 0.2s; + gap: var(--space-md); + background: var(--color-surface); + border-radius: var(--radius-md); + padding: var(--space-sm) var(--space-lg); + transition: all var(--transition-normal); + border: 1px solid var(--color-border); + box-shadow: var(--shadow-sm); } .profile-liked-song:hover { - background: #3b2323; + background: var(--color-surface-hover); + transform: translateY(-2px); + box-shadow: var(--shadow-md); } .profile-liked-cover { width: 56px; height: 56px; - border-radius: 8px; + border-radius: var(--radius-md); background-size: cover; background-position: center; background-repeat: no-repeat; @@ -120,21 +128,21 @@ } .profile-liked-title-text { - color: #fff; - font-size: 1rem; - font-weight: 600; + color: var(--color-text-primary); + font-size: var(--font-size-base); + font-weight: var(--font-weight-semibold); } .profile-liked-artist { - color: #c89295; - font-size: 0.95rem; + color: var(--color-text-secondary); + font-size: var(--font-size-sm); } @media (max-width: 600px) { .profile-container { - padding: 16px 0 0 0; + padding: var(--space-md) 0 0 0; } .profile-header { - margin-bottom: 18px; + margin-bottom: var(--space-lg); } } diff --git a/frontend/src/utils/api.js b/frontend/src/utils/api.js index 599aabcb..ca3f4cd0 100644 --- a/frontend/src/utils/api.js +++ b/frontend/src/utils/api.js @@ -1,6 +1,6 @@ import { auth } from './firebaseConfig'; -const API_BASE = import.meta.env.VITE_BACKEND_API_URL || ''; +const API_BASE = import.meta.env.VITE_BACKEND_API_URL || 'http://localhost:8000'; async function getToken() { const user = auth.currentUser; diff --git a/frontend/src/utils/firebaseConfig.js b/frontend/src/utils/firebaseConfig.js index 9f209c75..1d1813de 100644 --- a/frontend/src/utils/firebaseConfig.js +++ b/frontend/src/utils/firebaseConfig.js @@ -2,12 +2,12 @@ import { initializeApp } from 'firebase/app'; import { getAuth, GoogleAuthProvider } from 'firebase/auth'; const firebaseConfig = { - apiKey: import.meta.env.VITE_FIREBASE_API_KEY, - authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN, - projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID, - storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET, - messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID, - appId: import.meta.env.VITE_FIREBASE_APP_ID, + apiKey: import.meta.env.VITE_FIREBASE_API_KEY || "demo-api-key", + authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN || "demo-project.firebaseapp.com", + projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID || "demo-project", + storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET || "demo-project.appspot.com", + messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID || "123456789", + appId: import.meta.env.VITE_FIREBASE_APP_ID || "1:123456789:web:abcdef", }; const app = initializeApp(firebaseConfig); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..a0a80ddc --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "Streamify", + "lockfileVersion": 3, + "requires": true, + "packages": {} +}