diff --git a/frontend/index.html b/frontend/index.html index 5b242fb..da95e16 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -12,6 +12,7 @@ + diff --git a/frontend/src/components/Filters.css b/frontend/src/components/Filters.css index 27cdfe1..95a3405 100644 --- a/frontend/src/components/Filters.css +++ b/frontend/src/components/Filters.css @@ -1,14 +1,6 @@ .filters { padding: 1rem; - & .button { - background-color: var(--bg-secondary-light); - color: var(--accent-orange); - border: 1px solid var(--accent-orange); - border-radius: 6px; - padding: .5em .8em; - } - & .filters-container { display: flex; flex-wrap: wrap; @@ -52,7 +44,7 @@ & input { margin: .5em 0 0; - accent-color: var(--accent-orange); + accent-color: var(--led-orange); } & .slider { @@ -61,8 +53,8 @@ & select { background-color: var(--bg-secondary-light); - color: var(--accent-orange); - border: 1px solid var(--accent-orange); + color: var(--led-orange); + border: 1px solid var(--led-orange); border-radius: 6px; padding: .3em; } diff --git a/frontend/src/components/Filters.tsx b/frontend/src/components/Filters.tsx index af0108e..be81f60 100644 --- a/frontend/src/components/Filters.tsx +++ b/frontend/src/components/Filters.tsx @@ -213,7 +213,7 @@ export default function Filters({ onChange }: FiltersProps) { ))} - + ) } \ No newline at end of file diff --git a/frontend/src/components/Header.css b/frontend/src/components/Header.css index b8c9d3c..f52e161 100644 --- a/frontend/src/components/Header.css +++ b/frontend/src/components/Header.css @@ -2,74 +2,77 @@ display: flex; align-items: center; justify-content: space-between; - background-color: var(--bg-secondary-dark); + background: linear-gradient(180deg, #c9c9c4 0%, #a8a8a4 50%, #6e6e6a 100%); flex: 0 0 var(--header-height-lg); + gap: 1em; padding: 0.5em 2em; - border: 2px solid; - border-image-slice: 1; - border-image-source: linear-gradient( - to left, - var(--bg-secondary-dark), - var(--bg-secondary-light), - var(--bg-secondary-dark) - ); - border-top: 0; - border-left: 0; - border-right: 0; + border-style: solid; + border-width: 0 0 1px; + border-bottom-color: var(--text-engraved-soft); & > * { min-width: 12%; - margin: 0 0.5em; } & .logo { display: inline-block; - color: var(--text-primary); - font-family: "Orbitron", sans-serif; - font-size: var( --font-size-app-title); + color: var(--text-engraved); + font-family: var(--font-headings); + font-size: var(--font-size-app-title); font-weight: 700; letter-spacing: 0.05em; - text-shadow: 0px 0px 20px var(--accent-cyan-light); + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4); text-decoration: none; + + & .logo-line { + display: inline; + } + + & .logo-mender { + color: #006289; + } } & .search-box { flex: 0 1 600px; display: flex; + height: 2.6rem; + background: var(--bg-secondary); + border: 1px solid var(--bg-secondary-light); + border-radius: var(--r-panel); & input { + color: var(--led-cyan); + width: 100%; flex: 1; - height: 2.6rem; + height: 100%; padding: 0.5em 1em; - border: 1px solid var(--bg-secondary-light); - border-radius: 6px 0 0 6px; - font-size: 1em; - background: var(--bg-secondary-light); + border: none; + border-radius: var(--r-panel); + font-family: var(--font-mono); + font-size: 1rem; + background-color: transparent; transition: border-color 0.2s, box-shadow 0.2s; } - & input:focus { - outline: none; - box-shadow: var(--shadow-outline); + & input::placeholder { + color: var(--led-cyan-dim); } - & .button-search { - height: 2.6rem; - padding: 0.45rem 0.7rem; - border: 1px solid var(--bg-secondary); - border-radius: 0 6px 6px 0; - background: var(--bg-secondary); + & input:placeholder-shown { + text-overflow: ellipsis; } - & .button-search:hover { - border-color: var(--bg-secondary-light); - background: var(--bg-secondary-light); + & input:focus { + outline: none; + box-shadow: var(--shadow-glow-inset); } - & .button-search:focus { - outline: none; - box-shadow: var(--shadow-outline); + & .btn-search { + margin: 3px; + font-size: 1rem; + box-shadow: none; } } @@ -92,4 +95,24 @@ flex: 1 1 auto; } } +} + +@media (width < 768px) { + .header { + gap: .5em; + padding: 0.5em 1em; + + & > * { + min-width: initial; + } + + & .logo { + line-height: 1; + font-size: calc(var(--font-size-app-title) * .75); + + & .logo-line { + display: block; + } + } + } } \ No newline at end of file diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx index 3e65cf0..62a4231 100644 --- a/frontend/src/components/Header.tsx +++ b/frontend/src/components/Header.tsx @@ -15,7 +15,10 @@ export default function Header({onSearch}: { onSearch: (query: string) => void } return (
- TasteMender + + Taste + Mender +
void } />
- API + API {/* About */}
diff --git a/frontend/src/components/PlayButton.css b/frontend/src/components/PlayButton.css index ef4836c..6ee4cf8 100644 --- a/frontend/src/components/PlayButton.css +++ b/frontend/src/components/PlayButton.css @@ -1,7 +1,6 @@ .button-play { - --play-button-glow: rgba(46,211,230,0.5); - cursor: pointer; + color: var(--text-primary); display: flex; align-items: center; @@ -12,14 +11,14 @@ border-radius: 50%; background: radial-gradient(circle at 30% 30%, #2b3138, #1b2026); - border: 2px solid var(--accent-cyan); - box-shadow: 0 0 8px var(--play-button-glow); + border: 2px solid var(--led-cyan); + box-shadow: 0 0 8px var(--led-cyan-dim); transition: all 0.2s ease; &:hover { transform: scale(1.08); - box-shadow: 0 0 8px var(--accent-cyan), - 0 0 10px var(--play-button-glow); + box-shadow: 0 0 8px var(--led-cyan), + 0 0 10px var(--led-cyan-dim); } &:focus { @@ -30,6 +29,6 @@ &:active { outline: none; transform: scale(0.95); - box-shadow: 0 0 6px var(--play-button-glow); + box-shadow: 0 0 6px var(--led-cyan-dim); } } \ No newline at end of file diff --git a/frontend/src/components/Player.css b/frontend/src/components/Player.css index ba1cd97..44be8c5 100644 --- a/frontend/src/components/Player.css +++ b/frontend/src/components/Player.css @@ -1,6 +1,6 @@ .player { position: relative; - background-color: var(--bg-secondary); + background: linear-gradient(180deg, #b8b3a8 0%, #8a847a 55%, #5a564e 100%); flex: 0 0 var(--player-height-lg); &.empty { @@ -18,7 +18,7 @@ left: 0; right: 0; bottom: 100%; - background-color: var(--bg-primary); + background: linear-gradient(180deg, #4a4d52 0%, #2e3136 50%, #1a1c1f 100%); transition: height 0.4s cubic-bezier(0.4, 0, 0.2, 1); overflow: hidden; } @@ -98,29 +98,16 @@ } } - & .content { + & .footer-bar { padding: 10px; display: flex; gap: 1rem; justify-content: space-between; align-items: center; - /* Gradient border for footer top */ - border: 2px solid; - border-image-slice: 1; - border-image-source: linear-gradient( - to left, - var(--bg-secondary), - var(--bg-secondary-light), - var(--bg-secondary) - ); - border-bottom: 0; - border-left: 0; - border-right: 0; - - & .meta { - flex: 1; - } + border-style: solid; + border-width: 1px 0 0; + border-top-color: var(--text-engraved-soft); & .coverart { display: flex; @@ -150,9 +137,18 @@ color: var( --text-secondary); } } + + & .meta { + flex: 1 1; + overflow: hidden; + } + + & .controls { + flex-shrink: 0; + } } - & .content .title { + & .footer-bar .meta .title { font-weight: 600; line-height: 1.2; white-space: nowrap; @@ -160,7 +156,7 @@ text-overflow: ellipsis; } - & .content .artist-album { + & .footer-bar .meta .artist-album { margin-top: 0.15rem; font-size: 0.92rem; white-space: nowrap; @@ -172,11 +168,11 @@ } } - & .content button { + & .footer-bar .controls button { font-size: 1.4rem; } - & .content button + button { + & .footer-bar .controls button + button { margin-left: .5rem; } diff --git a/frontend/src/components/Player.tsx b/frontend/src/components/Player.tsx index d2b138a..45fa9ea 100644 --- a/frontend/src/components/Player.tsx +++ b/frontend/src/components/Player.tsx @@ -238,7 +238,7 @@ export default function Player({ ref }: PlayerProps) { const fallbackText = track.title?.charAt(0)?.toUpperCase() ?? "♪" return ( -
+
@@ -251,17 +251,17 @@ export default function Player({ ref }: PlayerProps) {
-
- - -
- {getRosamericaLabel(track.genre_rosamerica)} · r - {getDortmundLabel(track.genre_dortmund)} · d + {getRosamericaLabel(track.genre_rosamerica)} + {getDortmundLabel(track.genre_dortmund)} {track.submissions} subs
diff --git a/frontend/src/index.css b/frontend/src/index.css index b922590..89fb1cd 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -5,16 +5,25 @@ --bg-secondary-light: rgb(77, 81, 86); /* Inputs */ --bg-secondary-dark: #12151b; /* Sidebar */ - /* Accent colors */ - --accent-cyan: #2ED3E6; /* Main highlight */ - --accent-cyan-light: #7CE7F2; /* Hover / glow */ - --accent-orange: #F3A23A; /* Secondary highlight */ - - /* Text colors */ + /* LED display colors */ + --led-cyan: #2ED3E6; /* Main highlight */ + --led-cyan-dim: rgba(46, 211, 230, 0.5); /* Input placeholder */ + --led-cyan-glow: rgba(77, 217, 255, 0.55); + --led-cyan-light: #7CE7F2; /* Hover / glow */ + --led-orange: #F3A23A; /* Secondary highlight */ + + /* Body text colors */ --text-primary: #E6E6E6; /* Main text */ --text-secondary: #9CA3AF; /* Secondary text */ --text-heading: #c7e6f7; /* Headings */ + /* Panel text colors */ + --text-engraved: #2a2a28; + --text-engraved-soft: #4a4a48; + --text-on-display: #cfe9f2; + --text-on-metal: #1a1a18; + --text-label: #3a3a38; + /* Font sizes */ --font-size-app-title: 1.5rem; /* 24px - App Title */ --font-size-section-title: 1.125rem; /* 18px - Section Titles */ @@ -24,11 +33,23 @@ /* Effects */ --shadow-glow: 0 0 8px rgba(46, 211, 230, 0.5); + --shadow-glow-inset: inset 0 0 3px 4px var(--led-cyan); --shadow-outline: 0 0 0 .25rem rgba(46, 211, 230, 0.8); /* UI element sizes */ --header-height-lg: 80px; --player-height-lg: 80px; + + /* Fonts */ + --font-display: "Inter", sans-serif; + --font-headings: "Orbitron", sans-serif; + --font-mono: "JetBrains Mono", monospace; + + /* Radius */ + --r-panel: 6px; + --r-button: 4px; + --r-knob: 50%; + --r-recess: 3px; } * { @@ -38,13 +59,14 @@ html, body { margin: 0; padding: 0; - font-family: "Inter", sans-serif; + font-family: var(--font-display); color: var(--text-primary); scrollbar-width: thin; - scrollbar-color: var(--accent-orange) var(--bg-secondary); + scrollbar-color: var(--led-orange) var(--bg-secondary); } h1, h2, h3, h4, h5, h6 { + font-family: var(--font-headings); color: var(--text-heading); margin: 0 0 .5em; padding: 0; @@ -56,7 +78,7 @@ h1, h2, h3, h4, h5, h6 { } ::-webkit-scrollbar-thumb { - background: var(--accent-orange); + background: var(--led-orange); border-radius: 4px; } @@ -75,44 +97,38 @@ input { } } -button { - color: var(--text-primary); -} - input:focus { - outline: 2px solid var(--accent-cyan); + outline: 2px solid var(--led-cyan); outline-offset: 2px; } a { - color: var(--accent-cyan); + color: var(--led-cyan); text-decoration: none; &:hover { - color: var(--accent-cyan-light); + color: var(--led-cyan-light); } } -/* Reusable styles */ - -.button { - cursor: pointer; - background-color: transparent; +/* Reusable style classes */ + +/* Buttons */ + +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 6px; + font-size: 0.8rem; + font-family: var(--font-mono); + letter-spacing: 0.15em; + text-transform: uppercase; + padding: 8px 16px; + border-radius: var(--r-button); border: none; - font-size: 1rem; - font-weight: 600; - transition: background-color 120ms ease, - transform 60ms ease, - box-shadow 120ms ease; - - &:focus-visible { - outline: none; - box-shadow: var(--shadow-outline); - } - - &:active { - transform: translateY(1px); - } + cursor: pointer; + user-select: none; &[disabled] { opacity: .55; @@ -122,6 +138,92 @@ a { } } +.btn-metal { + background: linear-gradient(180deg, #d8d8d4 0%, #a8a8a4 50%, #8a8a86 100%); + color: var(--text-on-metal); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.7), + inset 0 -1px 0 rgba(0, 0, 0, 0.3), + 0 2px 3px rgba(0, 0, 0, 0.5), + 0 0 0 1px var(--text-engraved); + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4); + + &:hover { + color: var(--text-on-metal); + background: linear-gradient(180deg, #e8e8e4 0%, #b8b8b4 50%, #9a9a96 100%); + } + + &:active, + &.pressed { + color: var(--text-on-metal); + background: linear-gradient(180deg, #6a6a66 0%, #8a8a86 50%, #b8b8b4 100%); + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.5), + inset 0 -1px 0 rgba(255, 255, 255, 0.3), + 0 0 0 1px var(--text-engraved); + } +} + +.btn-amber { + background: linear-gradient(180deg, #ffd47a 0%, #ffb347 50%, #d68a20 100%); + color: #3a1d05; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6), + inset 0 -1px 0 rgba(0, 0, 0, 0.3), + 0 2px 3px rgba(0, 0, 0, 0.5), + 0 0 0 1px #4a2a08, + 0 0 12px rgba(255, 179, 71, 0.35); + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3); + font-weight: 700; + + &:hover { + color: #3a1d05; + background: linear-gradient(180deg, #ffe49a 0%, #ffc36a 50%, #e69a30 100%); + box-shadow: inset 0 1px 0 rgba(255,255,255,0.7), + inset 0 -1px 0 rgba(0,0,0,0.3), + 0 2px 3px rgba(0,0,0,0.5), + 0 0 0 1px #4a2a08, + 0 0 18px rgba(255,179,71,0.55); + } + + &:active, &.pressed { + background: linear-gradient(180deg, #b07020 0%, #d68a20 50%, #ffb347 100%); + color: #2a1404; + box-shadow: inset 0 2px 4px rgba(0,0,0,0.55), + inset 0 -1px 0 rgba(255,255,255,0.25), + 0 0 0 1px #4a2a08, + 0 0 8px rgba(255,179,71,0.25); + } +} + +.btn-dark { + background: linear-gradient(180deg, #4a4d52 0%, #2e3136 50%, #1a1c1f 100%); + color: #d8d8d4; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18), + inset 0 -1px 0 rgba(0, 0, 0, 0.5), + 0 2px 3px rgba(0, 0, 0, 0.5), + 0 0 0 1px #0a0c0e; + + &:hover { + background: linear-gradient(180deg, #5a5d62 0%, #3e4146 50%, #25272a 100%); + color: #f0f0ec; + } + + &:active { + background: linear-gradient(180deg, #15171a 0%, #2a2d31 50%, #3a3d42 100%); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.7), + 0 0 0 1px #0a0c0e; + } + + &.pressed { + background: linear-gradient(180deg, #15171a 0%, #2a2d31 50%, #3a3d42 100%); + color: var(--led-cyan); + text-shadow: 0 0 8px var(--led-cyan-glow); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.7), + inset 0 0 0 1px rgba(77, 217, 255, 0.3), + 0 0 0 1px #0a0c0e; + } +} + +/* Badges */ .badge { cursor: default; font-size: 0.75rem; @@ -130,6 +232,14 @@ a { padding: 0.15rem 0.5rem; border-radius: 1rem; box-shadow: 1px 1px 1px var(--bg-secondary-dark); + + &.badge-rosa { + color: var(--led-cyan); + } + + &.badge-dort { + color: var(--led-orange); + } } #root {