206 lines
6.1 KiB
HTML
206 lines
6.1 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>reviews — eweng.space</title>
|
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>☆</text></svg>">
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=IM+Fell+English:ital@0;1&display=swap" rel="stylesheet">
|
|
<style>
|
|
body {
|
|
font-family: 'IM Fell English', serif;
|
|
font-size: 20px;
|
|
}
|
|
|
|
@keyframes colorshift {
|
|
0% { color: hsl(0, 60%, 70%); }
|
|
25% { color: hsl(90, 60%, 70%); }
|
|
50% { color: hsl(180, 60%, 70%); }
|
|
75% { color: hsl(270, 60%, 70%); }
|
|
100% { color: hsl(360, 60%, 70%); }
|
|
}
|
|
|
|
@keyframes levite {
|
|
0% { transform: translateY(0px); }
|
|
50% { transform: translateY(-5px); }
|
|
100% { transform: translateY(0px); }
|
|
}
|
|
|
|
.fenetre {
|
|
max-width: 600px;
|
|
border: 1px solid black;
|
|
padding: 0.3rem 1.5rem 0 1.5rem;
|
|
padding-bottom: 0;
|
|
margin-top: 0px;
|
|
position: relative;
|
|
overflow-wrap: break-word;
|
|
}
|
|
.fenetre-nom {
|
|
margin-bottom: 1px;
|
|
margin-left: 2px;
|
|
}
|
|
.fenetre-contenu {
|
|
padding: 0 1rem 1.5rem 1rem;
|
|
min-width: 0;
|
|
}
|
|
|
|
.retour {
|
|
font-family: 'IM Fell English', serif;
|
|
font-size: 20px;
|
|
text-decoration: none;
|
|
color: inherit;
|
|
display: inline-block;
|
|
margin-bottom: 1px;
|
|
margin-right: 0.4rem;
|
|
}
|
|
.retour:hover { text-decoration: underline; }
|
|
|
|
/* ── grille de covers ── */
|
|
.grille {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 1rem;
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
.carte {
|
|
width: 150px;
|
|
cursor: pointer;
|
|
transition: transform 0.2s ease, opacity 0.25s ease;
|
|
position: relative;
|
|
}
|
|
|
|
.carte img {
|
|
width: 150px;
|
|
height: 150px;
|
|
object-fit: cover;
|
|
display: block;
|
|
border: 1px solid #111;
|
|
}
|
|
|
|
.carte .placeholder {
|
|
width: 150px;
|
|
height: 150px;
|
|
background: #eee;
|
|
border: 1px solid #111;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-style: italic;
|
|
color: #999;
|
|
font-size: 0.8rem;
|
|
text-align: center;
|
|
padding: 0.5rem;
|
|
}
|
|
|
|
.carte .nom {
|
|
margin-top: 0.3rem;
|
|
font-size: 0.8rem;
|
|
line-height: 1.3;
|
|
}
|
|
.carte .nom .artiste {
|
|
font-style: italic;
|
|
color: #555;
|
|
}
|
|
|
|
/* hover : assombrir les autres */
|
|
.grille.hovering .carte { opacity: 0.15; }
|
|
.grille.hovering .carte.actif {
|
|
opacity: 1;
|
|
transform: scale(1.07);
|
|
z-index: 10;
|
|
}
|
|
|
|
.badge-fav {
|
|
position: absolute;
|
|
top: 4px;
|
|
right: 4px;
|
|
font-size: 0.7rem;
|
|
background: #fff;
|
|
border: 1px solid #111;
|
|
padding: 1px 4px;
|
|
pointer-events: none;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div>
|
|
<a href="../index.html" class="retour">←</a><span class="fenetre-nom" id="titre-nav">eweng.space ☆</span>
|
|
</div>
|
|
|
|
<div class="fenetre">
|
|
<div class="fenetre-contenu">
|
|
<h1>reviews</h1>
|
|
<p>ce que j'écoute, ce que j'en pense.</p>
|
|
|
|
<div class="grille" id="grille"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function toSlug(titre) {
|
|
return titre
|
|
.toLowerCase()
|
|
.normalize('NFD').replace(/[\u0300-\u036f]/g, '')
|
|
.replace(/\s+/g, '-')
|
|
.replace(/[^a-z0-9-]/g, '');
|
|
}
|
|
|
|
fetch('data/albums.json')
|
|
.then(r => r.json())
|
|
.then(albums => {
|
|
const grille = document.getElementById('grille');
|
|
|
|
albums.forEach(album => {
|
|
const carte = document.createElement('div');
|
|
carte.className = 'carte';
|
|
|
|
const media = album.cover
|
|
? `<img src="${album.cover}" alt="${album.titre}" onerror="this.outerHTML='<div class=\\'placeholder\\'>${album.titre}</div>'">`
|
|
: `<div class="placeholder">${album.titre}</div>`;
|
|
|
|
const fav = album.favoris ? '<span class="badge-fav">♥</span>' : '';
|
|
|
|
carte.innerHTML = `
|
|
${fav}
|
|
${media}
|
|
<div class="nom">
|
|
${album.titre}<br>
|
|
<span class="artiste">${album.artiste}</span>
|
|
</div>
|
|
`;
|
|
|
|
carte.addEventListener('mouseenter', () => {
|
|
grille.classList.add('hovering');
|
|
carte.classList.add('actif');
|
|
});
|
|
carte.addEventListener('mouseleave', () => {
|
|
grille.classList.remove('hovering');
|
|
carte.classList.remove('actif');
|
|
});
|
|
|
|
carte.addEventListener('click', () => {
|
|
window.location.href = `albums/${toSlug(album.titre)}.html`;
|
|
});
|
|
|
|
grille.appendChild(carte);
|
|
});
|
|
})
|
|
.catch(() => {
|
|
document.getElementById('grille').textContent = 'impossible de charger les albums.';
|
|
});
|
|
|
|
const el = document.getElementById('titre-nav');
|
|
el.innerHTML = el.textContent.split('').map((c, i) => {
|
|
if (c === ' ') return ' ';
|
|
const delayColor = (i * 0.3).toFixed(1);
|
|
const delayLevite = (Math.random() * 4).toFixed(2);
|
|
const duree = (3 + Math.random() * 3).toFixed(2);
|
|
return `<span style="display:inline-block;animation:colorshift 12s ease-in-out infinite,levite ${duree}s ease-in-out infinite;animation-delay:-${delayColor}s,-${delayLevite}s">${c}</span>`;
|
|
}).join('');
|
|
</script>
|
|
</body>
|
|
</html> |