From fce2c6ff1e2d457ed95cc16c983dbd55762ba3bd Mon Sep 17 00:00:00 2001 From: Johan Lundberg Date: Wed, 2 Jul 2025 00:17:36 +0200 Subject: [PATCH] Implement comprehensive theme system and modernize UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add 8 themes: Gruvbox (default), Basic Blue, Dark, Gruvbox Dark, Dracula, Nord, Solarized, Arc - Replace emoji icons with clean SVG icons for search, reverse, and share buttons - Add Rolling Stone Magazine's supertitle to header - Implement theme selector in header with localStorage persistence - Simplify jump-to-rank placeholder text to "Jump to..." - Update all UI elements to use CSS custom properties for consistent theming - Improve mobile responsiveness for theme selector and controls 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- index.html | 29 +++- script.js | 46 ++++++- styles.css | 378 ++++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 401 insertions(+), 52 deletions(-) diff --git a/index.html b/index.html index e71a40a..075c607 100644 --- a/index.html +++ b/index.html @@ -12,8 +12,21 @@
+

Rolling Stone Magazine's

Top 500 Albums of All Time

The Greatest Albums Ever Made - 2023 Edition

+
+ +
@@ -22,7 +35,12 @@
- +
@@ -42,14 +60,19 @@
- +
diff --git a/script.js b/script.js index 9108d47..5062bcf 100644 --- a/script.js +++ b/script.js @@ -14,6 +14,7 @@ const sortBy = document.getElementById('sortBy'); const reverseButton = document.getElementById('reverseButton'); const jumpToRank = document.getElementById('jumpToRank'); const jumpButton = document.getElementById('jumpButton'); +const themeSelect = document.getElementById('themeSelect'); const loading = document.getElementById('loading'); const error = document.getElementById('error'); const stats = document.getElementById('stats'); @@ -23,6 +24,7 @@ document.addEventListener('DOMContentLoaded', function() { loadWikipediaMapping(); loadAlbumsData(); setupEventListeners(); + loadTheme(); handleUrlParams(); }); @@ -46,6 +48,7 @@ function setupEventListeners() { reverseButton.addEventListener('click', handleReverse); jumpButton.addEventListener('click', handleJumpToRank); jumpToRank.addEventListener('keypress', handleRankKeypress); + themeSelect.addEventListener('change', handleThemeChange); // Add scroll listener for infinite scroll window.addEventListener('scroll', handleScroll); @@ -98,6 +101,41 @@ function handleStatsCardClick(filterValue) { albumsGrid.scrollIntoView({ behavior: 'smooth' }); } +// Theme handling functions +function handleThemeChange(event) { + const theme = event.target.value; + setTheme(theme); + saveTheme(theme); +} + +function setTheme(theme) { + if (theme) { + document.documentElement.setAttribute('data-theme', theme); + } else { + document.documentElement.removeAttribute('data-theme'); + } +} + +function saveTheme(theme) { + try { + localStorage.setItem('selectedTheme', theme); + } catch (e) { + console.warn('Could not save theme to localStorage:', e); + } +} + +function loadTheme() { + try { + const savedTheme = localStorage.getItem('selectedTheme'); + if (savedTheme !== null) { + setTheme(savedTheme); + themeSelect.value = savedTheme; + } + } catch (e) { + console.warn('Could not load theme from localStorage:', e); + } +} + // Load albums data from CSV async function loadAlbumsData() { try { @@ -238,7 +276,13 @@ function createAlbumCard(album) { View on Wikipedia →
- + `; // Description is now always fully visible diff --git a/styles.css b/styles.css index 1bf54e0..3e63009 100644 --- a/styles.css +++ b/styles.css @@ -5,11 +5,165 @@ box-sizing: border-box; } +/* Theme Variables */ +:root { + /* Default theme - Gruvbox */ + --primary-color: #d79921; + --secondary-color: #b57614; + --accent-color: #d79921; + --background-gradient: linear-gradient(135deg, #282828 0%, #3c3836 100%); + --card-background: #32302f; + --text-primary: #ebdbb2; + --text-secondary: #d5c4a1; + --text-muted: #bdae93; + --text-light: #fbf1c7; + --border-color: #504945; + --shadow-color: rgba(0, 0, 0, 0.4); + --hover-background: rgba(215, 153, 33, 0.1); + --theme-name: 'Gruvbox'; +} + +/* Purple Theme */ +[data-theme="purple"] { + --primary-color: #667eea; + --secondary-color: #764ba2; + --accent-color: #667eea; + --background-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + --card-background: #ffffff; + --text-primary: #333333; + --text-secondary: #666666; + --text-muted: #888888; + --text-light: #ffffff; + --border-color: #eeeeee; + --shadow-color: rgba(0, 0, 0, 0.1); + --hover-background: rgba(102, 126, 234, 0.05); + --theme-name: 'Basic Blue'; +} + +/* Dark Theme */ +[data-theme="dark"] { + --primary-color: #bb86fc; + --secondary-color: #3700b3; + --accent-color: #bb86fc; + --background-gradient: linear-gradient(135deg, #121212 0%, #1f1f1f 100%); + --card-background: #1e1e1e; + --text-primary: #ffffff; + --text-secondary: #b3b3b3; + --text-muted: #808080; + --text-light: #ffffff; + --border-color: #333333; + --shadow-color: rgba(0, 0, 0, 0.3); + --hover-background: rgba(187, 134, 252, 0.1); + --theme-name: 'Dark'; +} + +/* Gruvbox Theme */ +[data-theme="gruvbox"] { + --primary-color: #d79921; + --secondary-color: #b57614; + --accent-color: #d79921; + --background-gradient: linear-gradient(135deg, #282828 0%, #3c3836 100%); + --card-background: #32302f; + --text-primary: #ebdbb2; + --text-secondary: #d5c4a1; + --text-muted: #bdae93; + --text-light: #fbf1c7; + --border-color: #504945; + --shadow-color: rgba(0, 0, 0, 0.4); + --hover-background: rgba(215, 153, 33, 0.1); + --theme-name: 'Gruvbox'; +} + +/* Gruvbox Dark Theme */ +[data-theme="gruvbox-dark"] { + --primary-color: #fabd2f; + --secondary-color: #d79921; + --accent-color: #fabd2f; + --background-gradient: linear-gradient(135deg, #1d2021 0%, #282828 100%); + --card-background: #1d2021; + --text-primary: #fbf1c7; + --text-secondary: #ebdbb2; + --text-muted: #a89984; + --text-light: #f9f5d7; + --border-color: #3c3836; + --shadow-color: rgba(0, 0, 0, 0.6); + --hover-background: rgba(250, 189, 47, 0.1); + --theme-name: 'Gruvbox Dark'; +} + +/* Dracula Theme */ +[data-theme="dracula"] { + --primary-color: #ff79c6; + --secondary-color: #bd93f9; + --accent-color: #ff79c6; + --background-gradient: linear-gradient(135deg, #282a36 0%, #44475a 100%); + --card-background: #282a36; + --text-primary: #f8f8f2; + --text-secondary: #f8f8f2; + --text-muted: #6272a4; + --text-light: #f8f8f2; + --border-color: #44475a; + --shadow-color: rgba(0, 0, 0, 0.5); + --hover-background: rgba(255, 121, 198, 0.1); + --theme-name: 'Dracula'; +} + +/* Nord Theme */ +[data-theme="nord"] { + --primary-color: #88c0d0; + --secondary-color: #81a1c1; + --accent-color: #88c0d0; + --background-gradient: linear-gradient(135deg, #2e3440 0%, #3b4252 100%); + --card-background: #3b4252; + --text-primary: #eceff4; + --text-secondary: #d8dee9; + --text-muted: #616e88; + --text-light: #eceff4; + --border-color: #434c5e; + --shadow-color: rgba(0, 0, 0, 0.4); + --hover-background: rgba(136, 192, 208, 0.1); + --theme-name: 'Nord'; +} + +/* Solarized Theme */ +[data-theme="solarized"] { + --primary-color: #268bd2; + --secondary-color: #2aa198; + --accent-color: #268bd2; + --background-gradient: linear-gradient(135deg, #002b36 0%, #073642 100%); + --card-background: #073642; + --text-primary: #839496; + --text-secondary: #657b83; + --text-muted: #586e75; + --text-light: #fdf6e3; + --border-color: #094450; + --shadow-color: rgba(0, 0, 0, 0.5); + --hover-background: rgba(38, 139, 210, 0.1); + --theme-name: 'Solarized'; +} + +/* Arc Theme */ +[data-theme="arc"] { + --primary-color: #5294e2; + --secondary-color: #4084d6; + --accent-color: #5294e2; + --background-gradient: linear-gradient(135deg, #383c4a 0%, #404552 100%); + --card-background: #404552; + --text-primary: #d3dae3; + --text-secondary: #bac5d0; + --text-muted: #8b9bad; + --text-light: #ffffff; + --border-color: #4b5162; + --shadow-color: rgba(0, 0, 0, 0.4); + --hover-background: rgba(82, 148, 226, 0.1); + --theme-name: 'Arc'; +} + body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; line-height: 1.6; - color: #333; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: var(--text-primary); + background: var(--background-gradient); min-height: 100vh; } @@ -26,22 +180,64 @@ body { border-bottom: 1px solid rgba(255, 255, 255, 0.2); padding: 2rem 0; text-align: center; + position: relative; +} + +.supertitle { + font-size: 1.5rem; + font-weight: 400; + color: var(--text-light); + opacity: 0.8; + margin: 0 0 0.5rem 0; + text-shadow: 1px 1px 2px rgba(0,0,0,0.3); + letter-spacing: 0.1em; } .title { font-size: 3rem; font-weight: 700; - color: white; + color: var(--text-light); margin-bottom: 0.5rem; text-shadow: 2px 2px 4px rgba(0,0,0,0.3); } .subtitle { font-size: 1.2rem; - color: rgba(255, 255, 255, 0.9); + color: var(--text-light); + opacity: 0.9; font-weight: 300; } +.header-theme-selector { + position: absolute; + bottom: 1rem; + right: 1rem; +} + +.theme-select-header { + padding: 0.5rem 0.75rem; + border: 2px solid rgba(255, 255, 255, 0.3); + border-radius: 6px; + background: rgba(255, 255, 255, 0.1); + color: var(--text-light); + font-size: 0.85rem; + backdrop-filter: blur(10px); + transition: all 0.3s ease; + cursor: pointer; + min-width: 100px; +} + +.theme-select-header:focus { + outline: none; + border-color: rgba(255, 255, 255, 0.6); + background: rgba(255, 255, 255, 0.2); +} + +.theme-select-header option { + background: var(--card-background); + color: var(--text-primary); +} + /* Main content */ .main { padding: 2rem 0; @@ -50,7 +246,7 @@ body { /* Controls */ .controls { - background: white; + background: var(--card-background); border-radius: 12px; padding: 1.5rem; margin-bottom: 2rem; @@ -83,17 +279,24 @@ body { .search-button { padding: 0.75rem 1rem; - background: #667eea; + background: var(--primary-color); color: white; border: none; border-radius: 0 8px 8px 0; cursor: pointer; font-size: 1rem; transition: background-color 0.3s ease; + display: flex; + align-items: center; + justify-content: center; +} + +.search-button svg { + color: white; } .search-button:hover { - background: #5a6fd8; + background: var(--secondary-color); } .filters { @@ -107,19 +310,26 @@ body { border: 2px solid #e1e5e9; border-radius: 8px; font-size: 1rem; - background: white; - cursor: pointer; outline: none; transition: border-color 0.3s ease; + cursor: pointer; + background: var(--card-background); + color: var(--text-primary); + min-width: 100px; } .filter-select:focus { - border-color: #667eea; + border-color: var(--primary-color); +} + +.filter-select option { + background: var(--card-background); + color: var(--text-primary); } .reverse-button { padding: 0.75rem 1rem; - background: #667eea; + background: var(--primary-color); color: white; border: none; border-radius: 8px; @@ -133,7 +343,7 @@ body { } .reverse-button:hover { - background: #5a6fd8; + background: var(--secondary-color); transform: translateY(-1px); } @@ -142,16 +352,23 @@ body { } .reverse-button.reversed { - background: #764ba2; + background: var(--secondary-color); } .reverse-button.reversed:hover { - background: #6a4190; + background: var(--secondary-color); } #reverseIcon { font-size: 1.2rem; transition: transform 0.3s ease; + display: flex; + align-items: center; + justify-content: center; +} + +#reverseIcon svg { + color: inherit; } .reverse-button.reversed #reverseIcon { @@ -183,12 +400,38 @@ body { } .rank-input:focus { - border-color: #667eea; + border-color: var(--primary-color); +} + +.theme-selector { + margin-left: 1rem; +} + +.theme-select { + padding: 0.75rem 1rem; + border: 2px solid #e1e5e9; + border-radius: 8px; + font-size: 1rem; + outline: none; + transition: border-color 0.3s ease; + cursor: pointer; + background: var(--card-background); + color: var(--text-primary); + min-width: 100px; +} + +.theme-select:focus { + border-color: var(--primary-color); +} + +.theme-select option { + background: var(--card-background); + color: var(--text-primary); } .jump-button { padding: 0.75rem 1.25rem; - background: #667eea; + background: var(--primary-color); color: white; border: none; border-radius: 8px; @@ -199,7 +442,7 @@ body { } .jump-button:hover { - background: #5a6fd8; + background: var(--secondary-color); transform: translateY(-1px); } @@ -217,7 +460,7 @@ body { } .stat-item { - background: white; + background: var(--card-background); padding: 1.5rem; border-radius: 12px; text-align: center; @@ -228,21 +471,21 @@ body { .stat-item:hover { transform: translateY(-2px); - box-shadow: 0 6px 24px rgba(0,0,0,0.15); - background: rgba(102, 126, 234, 0.05); + box-shadow: 0 6px 24px var(--shadow-color); + background: var(--hover-background); } .stat-number { display: block; font-size: 2.5rem; font-weight: 700; - color: #667eea; + color: var(--primary-color); margin-bottom: 0.5rem; } .stat-label { font-size: 0.9rem; - color: #666; + color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.5px; } @@ -256,7 +499,7 @@ body { } .album-card { - background: white; + background: var(--card-background); border-radius: 12px; padding: 1.5rem; box-shadow: 0 4px 20px rgba(0,0,0,0.1); @@ -331,8 +574,8 @@ body { position: absolute; top: 1rem; right: 1rem; - background: rgba(102, 126, 234, 0.9); - color: white; + background: var(--primary-color); + color: var(--text-light); border: none; border-radius: 50%; width: 36px; @@ -346,7 +589,11 @@ body { opacity: 0; transform: scale(0.8); backdrop-filter: blur(10px); - box-shadow: 0 2px 8px rgba(0,0,0,0.2); + box-shadow: 0 2px 8px var(--shadow-color); +} + +.album-share svg { + color: var(--text-light); } .album-card:hover .album-share { @@ -355,7 +602,7 @@ body { } .album-share:hover { - background: rgba(102, 126, 234, 1); + background: var(--secondary-color); transform: scale(1.1); } @@ -364,7 +611,8 @@ body { } .album-share.copied { - background: rgba(23, 162, 184, 0.9); + background: var(--secondary-color); + opacity: 1; } .album-card:hover { @@ -382,7 +630,7 @@ body { .album-rank { font-size: 2.5rem; font-weight: 700; - color: #667eea; + color: var(--primary-color); text-align: center; display: flex; align-items: center; @@ -400,20 +648,20 @@ body { .album-title { font-size: 1.3rem; font-weight: 600; - color: #333; + color: var(--text-primary); line-height: 1.3; margin: 0; } .album-artist { font-size: 1.1rem; - color: #666; + color: var(--text-secondary); margin: 0; } .album-info { font-size: 0.9rem; - color: #888; + color: var(--text-muted); margin: 0; } @@ -427,28 +675,34 @@ body { } .status-new { - background: #e8f5e8; - color: #2d8f47; + background: var(--hover-background); + color: var(--primary-color); + border: 1px solid var(--primary-color); + opacity: 0.8; } .status-improved { - background: #e3f2fd; - color: #1976d2; + background: var(--hover-background); + color: var(--primary-color); + border: 1px solid var(--primary-color); } .status-dropped { - background: #ffeaea; - color: #d32f2f; + background: var(--hover-background); + color: var(--text-muted); + border: 1px solid var(--text-muted); + opacity: 0.7; } .status-no-change { - background: #f5f5f5; - color: #666; + background: var(--hover-background); + color: var(--text-secondary); + border: 1px solid var(--border-color); } .album-description { font-size: 0.95rem; - color: #555; + color: var(--text-secondary); line-height: 1.7; margin-top: 1rem; text-align: justify; @@ -479,7 +733,7 @@ body { .wikipedia-link { display: inline-block; - color: #667eea; + color: var(--primary-color); text-decoration: none; font-size: 0.9rem; font-weight: 500; @@ -489,13 +743,10 @@ body { transition: all 0.3s ease; } -.wikipedia-link::before { - content: "🔗 "; - font-size: 0.8rem; -} .wikipedia-link:hover { - background: rgba(102, 126, 234, 0.2); + background: var(--hover-background); + opacity: 0.8; transform: translateY(-1px); } @@ -530,7 +781,7 @@ body { text-align: center; padding: 3rem 0; color: #ff6b6b; - background: white; + background: var(--card-background); border-radius: 12px; margin: 2rem 0; } @@ -546,6 +797,10 @@ body { /* Responsive design */ @media (max-width: 768px) { + .supertitle { + font-size: 1.2rem; + } + .title { font-size: 2rem; } @@ -554,6 +809,17 @@ body { font-size: 1rem; } + .header-theme-selector { + bottom: 0.5rem; + right: 0.5rem; + } + + .theme-select-header { + font-size: 0.8rem; + padding: 0.4rem 0.6rem; + min-width: 80px; + } + .controls { flex-direction: column; align-items: stretch; @@ -579,12 +845,18 @@ body { .bookmark-controls { width: 100%; justify-content: center; + flex-wrap: wrap; + gap: 1rem; } .rank-input { width: 120px; } + .theme-selector { + margin-left: 0; + } + .album-card { flex-direction: column; align-items: flex-start; @@ -606,6 +878,11 @@ body { font-size: 5rem; } + .album-share { + opacity: 1; + transform: scale(1); + } + .stats { grid-template-columns: repeat(2, 1fr); } @@ -636,4 +913,9 @@ body { .album-cover-icon { font-size: 4.5rem; } + + .album-share { + opacity: 1; + transform: scale(1); + } } \ No newline at end of file