website v3

This commit is contained in:
zoop 2026-09-08 19:08:35 -04:00
parent 9a0c184515
commit 1f6b5aef3d
No known key found for this signature in database
GPG Key ID: 15C0D336C9E08F3F
3 changed files with 212 additions and 49 deletions

View File

@ -4,55 +4,197 @@
--- ---
<section class="section" id="np-section" hidden> <div class="music">
<h2 class="label">now playing</h2> <button
<div class="np" id="np"> class="music__trigger"
<div class="np__body"> type="button"
<img class="np__art np__art--none" id="np-art" alt="" width="64" height="64" decoding="async" /> data-music-open
<div class="np__meta"> aria-haspopup="dialog"
<div class="np__title" id="np-title">&mdash;</div> data-umami-event="music-open"
<div class="np__artist" id="np-artist">&mdash;</div> hidden
<div class="np__foot"> >
<span class="np__tx" aria-hidden="true"> <span class="music__note" aria-hidden="true">&#9835;</span>
<svg viewBox="0 0 24 24" width="11" height="11" fill="currentColor" <span class="music__label" id="music-label">now playing</span>
><path d="M6 5h2v14H6zM20 5v14L9 12z"></path></svg <span class="music__arw" aria-hidden="true">&#8594;</span>
> </button>
<svg viewBox="0 0 24 24" width="12" height="12" fill="currentColor"
><path d="M6 4l14 8-14 8z"></path></svg <dialog class="music-wall" data-music-dialog aria-label="music">
> <div class="music-wall__bar">
<svg viewBox="0 0 24 24" width="11" height="11" fill="currentColor" <span class="music-wall__title">music</span>
><path d="M16 5h2v14h-2zM4 5l11 7-11 7z"></path></svg <button
> class="music-wall__close"
</span> type="button"
<span class="np__dur" id="np-dur"></span> data-music-close
<span class="np__ago" id="np-ago"></span> aria-label="close"
<a class="np__src" id="np-src" href="#" target="_blank" rel="noopener"> autofocus>&#10005;</button>
<svg class="np__logo" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" </div>
><path
d="M12 0C5.4 0 0 5.4 0 12s5.4 12 12 12 12-5.4 12-12S18.66 0 12 0zm5.521 17.34c-.24.359-.66.48-1.021.24-2.82-1.74-6.36-2.101-10.561-1.141-.418.122-.779-.179-.899-.539-.12-.421.18-.78.54-.9 4.56-1.021 8.52-.6 11.64 1.32.42.18.479.659.301 1.02zm1.44-3.3c-.301.42-.841.6-1.262.3-3.239-1.98-8.159-2.58-11.939-1.38-.479.12-1.02-.12-1.14-.6-.12-.48.12-1.021.6-1.141C9.6 9.9 15 10.561 18.72 12.84c.361.181.54.78.241 1.2zm.12-3.36C15.24 8.4 8.82 8.16 5.16 9.301c-.6.179-1.2-.16-1.38-.72-.18-.601.18-1.2.72-1.381 4.26-1.26 11.28-1.02 15.721 1.621.539.3.719 1.02.419 1.56-.299.421-1.02.599-1.559.3z" <div class="music-wall__body">
></path></svg <div class="music-block" id="np-section" hidden>
><span>spotify</span> <h2 class="label">now playing</h2>
</a> <div class="np" id="np">
<div class="np__body">
<img class="np__art np__art--none" id="np-art" alt="" width="64" height="64" decoding="async" />
<div class="np__meta">
<div class="np__title" id="np-title">&mdash;</div>
<div class="np__artist" id="np-artist">&mdash;</div>
<div class="np__foot">
<span class="np__tx" aria-hidden="true">
<svg viewBox="0 0 24 24" width="11" height="11" fill="currentColor"
><path d="M6 5h2v14H6zM20 5v14L9 12z"></path></svg
>
<svg viewBox="0 0 24 24" width="12" height="12" fill="currentColor"
><path d="M6 4l14 8-14 8z"></path></svg
>
<svg viewBox="0 0 24 24" width="11" height="11" fill="currentColor"
><path d="M16 5h2v14h-2zM4 5l11 7-11 7z"></path></svg
>
</span>
<span class="np__dur" id="np-dur"></span>
<span class="np__ago" id="np-ago"></span>
<a class="np__src" id="np-src" href="#" target="_blank" rel="noopener">
<svg class="np__logo" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"
><path
d="M12 0C5.4 0 0 5.4 0 12s5.4 12 12 12 12-5.4 12-12S18.66 0 12 0zm5.521 17.34c-.24.359-.66.48-1.021.24-2.82-1.74-6.36-2.101-10.561-1.141-.418.122-.779-.179-.899-.539-.12-.421.18-.78.54-.9 4.56-1.021 8.52-.6 11.64 1.32.42.18.479.659.301 1.02zm1.44-3.3c-.301.42-.841.6-1.262.3-3.239-1.98-8.159-2.58-11.939-1.38-.479.12-1.02-.12-1.14-.6-.12-.48.12-1.021.6-1.141C9.6 9.9 15 10.561 18.72 12.84c.361.181.54.78.241 1.2zm.12-3.36C15.24 8.4 8.82 8.16 5.16 9.301c-.6.179-1.2-.16-1.38-.72-.18-.601.18-1.2.72-1.381 4.26-1.26 11.28-1.02 15.721 1.621.539.3.719 1.02.419 1.56-.299.421-1.02.599-1.559.3z"
></path></svg
><span>spotify</span>
</a>
</div>
</div>
<div class="np__prog" id="np-prog" hidden><div class="np__prog-fill" id="np-prog-fill"></div></div>
</div>
</div> </div>
</div> </div>
<div class="np__prog" id="np-prog" hidden><div class="np__prog-fill" id="np-prog-fill"></div></div>
</div>
</div>
</section>
<section class="section top" id="top-section" hidden> <div class="music-block top" id="top-section" hidden>
<h2 class="label">top tracks <span class="label__sub">last 4 weeks</span></h2> <h2 class="label">top tracks <span class="label__sub">last 4 weeks</span></h2>
<div class="mosaic" id="mosaic"></div> <div class="mosaic" id="mosaic"></div>
<div class="mosaic__caps" id="mosaic-caps"></div> <div class="mosaic__caps" id="mosaic-caps"></div>
</section> </div>
</div>
</dialog>
</div>
<style is:global> <style is:global>
.music {
margin-top: var(--gap);
}
.music__trigger {
display: inline-flex;
align-items: center;
gap: 0.55rem;
padding: 0.35rem 0;
background: none;
border: 0;
cursor: pointer;
font-family: var(--mono);
font-size: 0.78rem;
letter-spacing: 0.02em;
color: var(--muted);
transition: color 0.18s ease;
}
.music__trigger:hover {
color: var(--accent-2);
}
.music__trigger:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 4px;
border-radius: 3px;
}
.music__note {
color: var(--accent);
font-size: 0.9rem;
}
.music__trigger--live .music__note {
animation: music-pulse 1.6s ease-in-out infinite;
}
@keyframes music-pulse {
50% {
opacity: 0.35;
}
}
.music__label {
max-width: 22ch;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.music__arw {
color: var(--faint);
transition: transform 0.22s cubic-bezier(0.16, 1, 0.3, 1);
}
.music__trigger:hover .music__arw {
transform: translateX(0.25em);
}
.music-wall {
margin: auto;
width: min(24rem, calc(100vw - 1.5rem));
max-height: min(82vh, 40rem);
padding: 0;
border: 1px solid var(--line);
border-radius: 8px;
background: var(--paper-2);
color: var(--ink);
box-shadow: 0 24px 60px -20px rgba(0, 0, 0, 0.75);
overflow: hidden;
}
.music-wall[open] {
animation: music-in 0.28s cubic-bezier(0.17, 0.89, 0.32, 1.28);
}
@keyframes music-in {
from {
opacity: 0;
transform: translateY(8px) scale(0.96);
}
}
.music-wall::backdrop {
background: rgba(6, 5, 4, 0.68);
backdrop-filter: blur(3px);
}
.music-wall__bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.6rem 0.85rem;
border-bottom: 1px solid var(--line);
}
.music-wall__title {
font-family: var(--mono);
font-size: 0.72rem;
letter-spacing: 0.1em;
color: var(--faint);
}
.music-wall__close {
background: none;
border: 0;
color: var(--muted);
cursor: pointer;
font-size: 0.85rem;
line-height: 1;
padding: 0.3rem;
}
.music-wall__close:hover {
color: var(--accent);
}
.music-wall__body {
padding: 1rem;
overflow-y: auto;
max-height: calc(min(82vh, 40rem) - 2.6rem);
}
.music-block + .music-block {
margin-top: 1.4rem;
}
.music-block .label {
margin-bottom: 0.7rem;
}
.np { .np {
width: 100%; width: 100%;
max-width: 22rem;
border: 1px solid var(--faint); border: 1px solid var(--faint);
background: #151412; background: #151412;
} }
@ -161,7 +303,6 @@
gap: 3px; gap: 3px;
aspect-ratio: 4 / 3; aspect-ratio: 4 / 3;
width: 100%; width: 100%;
max-width: 268px;
} }
.mosaic__cell { .mosaic__cell {
position: relative; position: relative;
@ -205,7 +346,7 @@
opacity: 0; opacity: 0;
transition: opacity 0.14s ease; transition: opacity 0.14s ease;
pointer-events: none; pointer-events: none;
white-space: nowrap; white-space: normal;
} }
.mosaic__rank { .mosaic__rank {
color: var(--faint); color: var(--faint);
@ -257,16 +398,23 @@
} }
} }
@media (max-width: 560px) { @media (prefers-reduced-motion: reduce) {
.np { .music__trigger--live .music__note {
max-width: none; animation: none;
}
.mosaic__cap {
white-space: normal;
} }
} }
</style> </style>
<script> <script>
import "../scripts/music.ts"; import "../scripts/music.ts";
const dialog = document.querySelector<HTMLDialogElement>("[data-music-dialog]");
const open = document.querySelector<HTMLButtonElement>("[data-music-open]");
const close = document.querySelector<HTMLButtonElement>("[data-music-close]");
open?.addEventListener("click", () => dialog?.showModal());
close?.addEventListener("click", () => dialog?.close());
dialog?.addEventListener("click", (e) => {
if (e.target === dialog) dialog.close();
});
</script> </script>

View File

@ -12,8 +12,6 @@ import { site, elsewhere } from "../data/site";
<p class="bio">{site.bio}</p> <p class="bio">{site.bio}</p>
</section> </section>
<Music />
<section class="section"> <section class="section">
<h2 class="label">elsewhere</h2> <h2 class="label">elsewhere</h2>
<nav class="pagenav" aria-label="more"> <nav class="pagenav" aria-label="more">
@ -35,4 +33,5 @@ import { site, elsewhere } from "../data/site";
<Webrings /> <Webrings />
<Buttons /> <Buttons />
<Music />
</Base> </Base>

View File

@ -142,6 +142,22 @@ async function load(): Promise<void> {
if (!data || !data.configured) return; if (!data || !data.configured) return;
if (data.nowPlaying) renderNowPlaying(data.nowPlaying as NP); if (data.nowPlaying) renderNowPlaying(data.nowPlaying as NP);
if (Array.isArray(data.top)) renderTop(data.top as Track[]); if (Array.isArray(data.top)) renderTop(data.top as Track[]);
const trigger = document.querySelector<HTMLButtonElement>(".music__trigger");
if (trigger && (data.nowPlaying || (Array.isArray(data.top) && data.top.length))) {
trigger.hidden = false;
const label = $("music-label");
const np = data.nowPlaying as NP | null;
if (label && np) {
if (np.isPlaying) {
trigger.classList.add("music__trigger--live");
label.textContent = np.title;
} else if (np.playedAt) {
label.textContent = "last played";
}
}
}
} }
load(); load();