Created
June 14, 2026 09:14
-
-
Save juanmackie/407e6c8d5f7c598dedc7b53b283b3e6b to your computer and use it in GitHub Desktop.
Nothing Style Last FM Widget
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ##ENSURE TO UPDATE WITH YOUR DETAILS | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Space+Mono:wght=400;700&display=swap'); | |
| #lfm-container, #lfm-container *, #lfm-container *::before, #lfm-container *::after { | |
| margin: 0; padding: 0; box-sizing: border-box; | |
| } | |
| #lfm-container { | |
| --bg: #000000; | |
| --surface: #060606; | |
| --border: rgba(255,255,255,0.07); | |
| --text: #FFFFFF; | |
| --text-dim: rgba(255,255,255,0.45); | |
| --text-muted: rgba(255,255,255,0.18); | |
| --accent: #ffe927; | |
| --accent-glow: rgba(255,233,39,0.35); | |
| --mono: 'Space Mono', monospace; | |
| width: 100% !important; | |
| max-width: 460px; | |
| margin: 0 auto; | |
| position: relative; | |
| border: 1px solid var(--border); | |
| background: var(--bg); | |
| background-image: radial-gradient(circle, rgba(255,255,255,0.025) 1px, transparent 1px) !important; | |
| background-size: 20px 20px; | |
| color: var(--text); | |
| font-family: var(--mono); | |
| line-height: 1.6; | |
| } | |
| /* Target registration corners */ | |
| #lfm-container .reg-mark { position: absolute; width: 10px; height: 10px; pointer-events: none; } | |
| #lfm-container .reg-mark::before, #lfm-container .reg-mark::after { content: ''; position: absolute; background: var(--text-muted); } | |
| #lfm-container .reg-mark.tl { top: -1px; left: -1px; } #lfm-container .reg-mark.tl::before { width: 10px; height: 1px; top: 0; left: 0; } #lfm-container .reg-mark.tl::after { width: 1px; height: 10px; top: 0; left: 0; } | |
| #lfm-container .reg-mark.tr { top: -1px; right: -1px; } #lfm-container .reg-mark.tr::before { width: 10px; height: 1px; top: 0; right: 0; } #lfm-container .reg-mark.tr::after { width: 1px; height: 10px; top: 0; right: 0; } | |
| #lfm-container .reg-mark.bl { bottom: -1px; left: -1px; } #lfm-container .reg-mark.bl::before { width: 10px; height: 1px; bottom: 0; left: 0; } #lfm-container .reg-mark.bl::after { width: 1px; height: 10px; bottom: 0; left: 0; } | |
| #lfm-container .reg-mark.br { bottom: -1px; right: -1px; } #lfm-container .reg-mark.br::before { width: 10px; height: 1px; bottom: 0; right: 0; } #lfm-container .reg-mark.br::after { width: 1px; height: 10px; bottom: 0; right: 0; } | |
| #lfm-container .scan-line { | |
| position: absolute; inset: 0; height: 1px; | |
| background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.05) 40%, rgba(255,255,255,0.05) 60%, transparent 100%); | |
| animation: scan 7s ease-in-out infinite; pointer-events: none; z-index: 5; | |
| } | |
| @keyframes scan { 0% { top: -1px; opacity: 0; } 5% { opacity: 1; } 95% { opacity: 1; } 100% { top: calc(100% + 1px); opacity: 0; } } | |
| #lfm-container .lfm-header { padding: 28px 28px 14px; display: flex; align-items: flex-end; justify-content: space-between; } | |
| #lfm-container .lfm-logo-fallback { font-weight: 700; font-size: 16px; letter-spacing: 0.12em; color: var(--text); } | |
| #lfm-container .lfm-user { | |
| font-size: 9px; | |
| letter-spacing: 0.12em; | |
| text-transform: uppercase; | |
| color: var(--text-muted); | |
| line-height: 1; | |
| text-decoration: none; | |
| transition: color 0.2s ease; | |
| } | |
| #lfm-container .lfm-user:hover { | |
| color: var(--accent); | |
| } | |
| #lfm-container .glyph-bar { padding: 0 28px; display: flex; align-items: center; gap: 5px; height: 24px; } | |
| #lfm-container .glyph-seg { height: 3px; border-radius: 1.5px; background: var(--border); } | |
| #lfm-container .glyph-seg:nth-child(1) { width: 10px; } #lfm-container .glyph-seg:nth-child(2) { width: 28px; } #lfm-container .glyph-seg:nth-child(3) { width: 52px; } #lfm-container .glyph-seg:nth-child(4) { width: 80px; } #lfm-container .glyph-seg:nth-child(5) { width: 52px; } #lfm-container .glyph-seg:nth-child(6) { width: 28px; } #lfm-container .glyph-seg:nth-child(7) { width: 10px; } | |
| #lfm-container.is-active .glyph-seg { background: var(--accent); box-shadow: 0 0 6px var(--accent-glow); } | |
| #lfm-container .divider { margin: 0 28px; border: none; border-top: 1px dashed var(--border); } | |
| #lfm-container #now-playing { display: flex; align-items: center; gap: 14px; padding: 18px 28px; min-height: 56px; } | |
| #lfm-container .led { width: 8px; height: 8px; border-radius: 50%; background: var(--text-muted); flex-shrink: 0; } | |
| #lfm-container.is-active .led { background: var(--accent); box-shadow: 0 0 14px var(--accent-glow); animation: pulse-led 2.2s ease-in-out infinite; } | |
| @keyframes pulse-led { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } } | |
| #lfm-container .np-info { min-width: 0; flex: 1; } | |
| #lfm-container .np-label { font-size: 9px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--text-muted); margin-bottom: 3px; } | |
| #lfm-container.is-active .np-label { color: var(--accent); } | |
| #lfm-container .np-text { font-size: 13px; color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.5; } | |
| #lfm-container.is-active .np-text { color: var(--text); } | |
| #lfm-container #track-list { list-style: none !important; padding: 0 28px !important; margin: 0 !important; } | |
| #lfm-container .track-row { display: flex; align-items: center; gap: 12px; padding: 8px 0; border-bottom: 1px dotted var(--border); } | |
| #lfm-container .track-row:last-child { border-bottom: none; } | |
| #lfm-container .t-idx { font-size: 9px; color: var(--text-muted); min-width: 20px; text-align: right; font-variant-numeric: tabular-nums; flex-shrink: 0; } | |
| #lfm-container .t-art { width: 34px; height: 34px; border: 1px solid var(--border); object-fit: cover; flex-shrink: 0; background: var(--surface); display: block; } | |
| #lfm-container .t-art-placeholder { width: 34px; height: 34px; border: 1px solid var(--border); flex-shrink: 0; background: var(--surface); position: relative; } | |
| #lfm-container .t-art-placeholder::after { content: ''; position: absolute; top: 50%; left: 50%; width: 4px; height: 4px; border-radius: 50%; background: var(--text-muted); transform: translate(-50%, -50%); } | |
| #lfm-container .t-meta { flex: 1; min-width: 0; } | |
| #lfm-container .t-name { font-size: 12px; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.5; } | |
| #lfm-container .t-artist { font-size: 11px; color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.4; } | |
| #lfm-container .lfm-footer { padding: 14px 28px 22px; display: flex; justify-content: space-between; align-items: center; font-size: 9px; color: var(--text-muted); letter-spacing: 0.18em; text-transform: uppercase; } | |
| #lfm-container .lfm-dot { display: inline-block; width: 3px; height: 3px; border-radius: 50%; background: var(--text-muted); margin: 0 10px; vertical-align: middle; } | |
| @media (max-width: 500px) { | |
| #lfm-container .lfm-header { padding: 22px 18px 12px; } | |
| #lfm-container .glyph-bar { padding: 0 18px; } | |
| #lfm-container .divider { margin: 0 18px; } | |
| #lfm-container #now-playing { padding: 14px 18px; } | |
| #lfm-container #track-list { padding: 0 18px !important; } | |
| #lfm-container .lfm-footer { padding: 12px 18px 18px; } | |
| } | |
| </style> | |
| <div id="lfm-container"> | |
| <div class="reg-mark tl"></div><div class="reg-mark tr"></div><div class="reg-mark bl"></div><div class="reg-mark br"></div> | |
| <div class="scan-line"></div> | |
| <header class="lfm-header"> | |
| <div class="lfm-logo-fallback">LAST.FM</div> | |
| <a class="lfm-user" href="http://last.fm/user/juanmackie" target="_blank" rel="noopener noreferrer">full history of @juanmackie</a> | |
| </header> | |
| <div class="glyph-bar"> | |
| <div class="glyph-seg"></div><div class="glyph-seg"></div><div class="glyph-seg"></div> | |
| <div class="glyph-seg"></div><div class="glyph-seg"></div><div class="glyph-seg"></div><div class="glyph-seg"></div> | |
| </div> | |
| <hr class="divider"> | |
| <div id="now-playing"> | |
| <div class="led"></div> | |
| <div class="np-info"> | |
| <div class="np-label" id="np-status-label">OFFLINE</div> | |
| <div class="np-text" id="np-status-text">Currently Offline</div> | |
| </div> | |
| </div> | |
| <hr class="divider"> | |
| <div id="recent-tracks"> | |
| <ul id="track-list"></ul> | |
| </div> | |
| <footer class="lfm-footer"> | |
| <span>Data Stream</span> | |
| <span class="lfm-dot"></span> | |
| <span id="track-count">0 tracks</span> | |
| </footer> | |
| </div> | |
| <script> | |
| (function() { | |
| const username = 'juanmackie'; | |
| const apiKey = '29db0717585301fa01228bda7b30002e'; | |
| const url = `https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=${username}&api_key=${apiKey}&format=json&limit=11`; | |
| fetch(url) | |
| .then(response => response.json()) | |
| .then(data => { | |
| if (!data || !data.recenttracks || !data.recenttracks.track) return; | |
| const tracks = data.recenttracks.track; | |
| const container = document.getElementById('lfm-container'); | |
| const list = document.getElementById('track-list'); | |
| const statusLabel = document.getElementById('np-status-label'); | |
| const statusText = document.getElementById('np-status-text'); | |
| const totalCount = document.getElementById('track-count'); | |
| let displayCount = 0; | |
| tracks.forEach((track, index) => { | |
| const isNowPlaying = track['@attr'] && track['@attr'].nowplaying === 'true'; | |
| const artistName = track.artist ? track.artist['#text'] : 'Unknown Artist'; | |
| const trackName = track.name || 'Unknown Track'; | |
| if (isNowPlaying && index === 0) { | |
| if (container) container.classList.add('is-active'); | |
| if (statusLabel) statusLabel.textContent = "NOW PLAYING"; | |
| if (statusText) statusText.textContent = `${artistName} — ${trackName}`; | |
| } else if (list && displayCount < 10) { | |
| displayCount++; | |
| const li = document.createElement('li'); | |
| li.className = 'track-row'; | |
| const imgs = track.image || []; | |
| const smallImg = imgs.length > 0 ? imgs[0]['#text'] : ''; | |
| const artHtml = (smallImg && !smallImg.includes('2a96cbd8b46e442fc41c2b86b821562f')) | |
| ? `<img class="t-art" src="${smallImg}" alt="">` | |
| : '<div class="t-art-placeholder"></div>'; | |
| li.innerHTML = ` | |
| <span class="t-idx">${String(displayCount).padStart(2, '0')}</span> | |
| ${artHtml} | |
| <div class="t-meta"> | |
| <div class="t-name">${trackName}</div> | |
| <div class="t-artist">${artistName}</div> | |
| </div> | |
| `; | |
| list.appendChild(li); | |
| } | |
| }); | |
| if (totalCount) totalCount.textContent = `${displayCount} tracks`; | |
| }) | |
| .catch(err => { | |
| console.error('Last.fm Error:', err); | |
| const text = document.getElementById('np-status-text'); | |
| if (text) text.textContent = 'Unable to connect to stream.'; | |
| }); | |
| })(); | |
| </script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment