Skip to content

Instantly share code, notes, and snippets.

@wilhelmklopp
Created June 5, 2026 21:47
Show Gist options
  • Select an option

  • Save wilhelmklopp/38e815c42a6a8e37e37054af7fdc6aab to your computer and use it in GitHub Desktop.

Select an option

Save wilhelmklopp/38e815c42a6a8e37e37054af7fdc6aab to your computer and use it in GitHub Desktop.
Primavera Sound Barcelona 2026 — Stage Tier List
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Primavera Sound Barcelona 2026 — Stage Tier List</title>
<style>
:root {
--bg: #0e0e12;
--panel: #16171d;
--line: #24262e;
--ink: #f4f4f6;
--muted: #9aa0ab;
--s: #ff4d4d;
--a: #ff9b3a;
--b: #ffd23a;
--c: #7ed957;
--d: #4fc3f7;
--e: #ab7df8;
--f: #6b6b75;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", Inter, system-ui, sans-serif;
background: var(--bg);
color: var(--ink);
padding: 32px 24px 80px;
}
h1 { margin: 0 0 4px; font-size: 26px; letter-spacing: -0.02em; }
.sub { color: var(--muted); margin-bottom: 24px; font-size: 14px; }
.tier {
display: flex;
align-items: stretch;
border: 1px solid var(--line);
border-top: none;
background: var(--panel);
min-height: 80px;
}
.tier:first-of-type { border-top: 1px solid var(--line); border-radius: 8px 8px 0 0; }
.tier:last-of-type { border-radius: 0 0 8px 8px; }
.label {
width: 80px;
flex: 0 0 80px;
display: flex;
align-items: center;
justify-content: center;
font-weight: 800;
font-size: 28px;
color: #1a1a1a;
text-shadow: 0 1px 0 rgba(255,255,255,0.25);
}
.S .label { background: var(--s); }
.A .label { background: var(--a); }
.B .label { background: var(--b); }
.C .label { background: var(--c); }
.D .label { background: var(--d); }
.E .label { background: var(--e); }
.F .label { background: var(--f); color: #fff; text-shadow: none; }
.drop {
flex: 1;
display: flex;
flex-wrap: wrap;
gap: 8px;
padding: 10px 12px;
align-content: flex-start;
min-height: 80px;
}
.drop.over { background: rgba(255,255,255,0.04); }
.chip {
background: #2a2c36;
border: 1px solid #34374380;
color: var(--ink);
padding: 8px 12px;
border-radius: 6px;
font-size: 14px;
cursor: grab;
user-select: none;
white-space: nowrap;
transition: transform 0.08s ease, box-shadow 0.08s ease;
}
.chip:hover { transform: translateY(-1px); box-shadow: 0 2px 8px rgba(0,0,0,0.4); }
.chip.dragging { opacity: 0.4; cursor: grabbing; }
.pool {
margin-top: 24px;
border: 1px dashed #383a45;
border-radius: 8px;
padding: 12px;
background: #111218;
}
.pool h2 { margin: 0 0 10px; font-size: 14px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600; }
.pool .drop { padding: 0; }
.toolbar { margin-top: 20px; display: flex; gap: 10px; flex-wrap: wrap; }
button {
background: #2a2c36;
color: var(--ink);
border: 1px solid #34374380;
padding: 8px 14px;
border-radius: 6px;
font-size: 13px;
cursor: pointer;
font-family: inherit;
}
button:hover { background: #34374380; }
.hint { color: var(--muted); font-size: 12px; margin-top: 14px; }
</style>
</head>
<body>
<h1>Primavera Sound Barcelona 2026 — Stage Tier List</h1>
<div class="sub">Drag each stage into a tier. Right-click a chip to remove it. State auto-saves to localStorage.</div>
<div id="tiers">
<div class="tier S"><div class="label">S</div><div class="drop" data-tier="S"></div></div>
<div class="tier A"><div class="label">A</div><div class="drop" data-tier="A"></div></div>
<div class="tier B"><div class="label">B</div><div class="drop" data-tier="B"></div></div>
<div class="tier C"><div class="label">C</div><div class="drop" data-tier="C"></div></div>
<div class="tier D"><div class="label">D</div><div class="drop" data-tier="D"></div></div>
<div class="tier E"><div class="label">E</div><div class="drop" data-tier="E"></div></div>
<div class="tier F"><div class="label">F</div><div class="drop" data-tier="F"></div></div>
</div>
<div class="pool">
<h2>Unranked stages</h2>
<div class="drop" data-tier="pool" id="pool"></div>
</div>
<div class="toolbar">
<button onclick="resetAll()">Reset</button>
<button onclick="exportJSON()">Copy ranking as text</button>
</div>
<div class="hint">Tip: ⌘+Shift+4 → Space → click window to screenshot the whole tier list.</div>
<script>
const STAGES = [
"Estrella Damm",
"Revolut",
"Plenitude",
"Occident",
"CUPRA",
"Schwarzkopf",
"Port",
"CUPRA Pulse",
"Levi's Plaza",
"501 Club (COLORSxSTUDIOS)",
"Backstage by Schwarzkopf",
"Barcelona Sona by Estrella Damm",
"Aperol Island of Joy",
"The Adidas Yard",
"Disney",
"Auditori Rockdelux",
"Boiler Room"
];
const KEY = "primavera-tierlist-2026-v1";
function load() {
try { return JSON.parse(localStorage.getItem(KEY)) || null; } catch { return null; }
}
function save() {
const state = {};
document.querySelectorAll(".drop").forEach(d => {
state[d.dataset.tier] = [...d.querySelectorAll(".chip")].map(c => c.textContent);
});
localStorage.setItem(KEY, JSON.stringify(state));
}
function makeChip(name) {
const el = document.createElement("div");
el.className = "chip";
el.draggable = true;
el.textContent = name;
el.addEventListener("dragstart", e => {
el.classList.add("dragging");
e.dataTransfer.setData("text/plain", name);
e.dataTransfer.effectAllowed = "move";
});
el.addEventListener("dragend", () => el.classList.remove("dragging"));
el.addEventListener("contextmenu", e => {
e.preventDefault();
document.getElementById("pool").appendChild(el);
save();
});
return el;
}
function init() {
const saved = load();
if (saved) {
Object.entries(saved).forEach(([tier, names]) => {
const target = document.querySelector(`.drop[data-tier="${tier}"]`);
if (!target) return;
names.forEach(n => target.appendChild(makeChip(n)));
});
} else {
const pool = document.getElementById("pool");
STAGES.forEach(s => pool.appendChild(makeChip(s)));
}
document.querySelectorAll(".drop").forEach(zone => {
zone.addEventListener("dragover", e => {
e.preventDefault();
zone.classList.add("over");
e.dataTransfer.dropEffect = "move";
});
zone.addEventListener("dragleave", () => zone.classList.remove("over"));
zone.addEventListener("drop", e => {
e.preventDefault();
zone.classList.remove("over");
const dragging = document.querySelector(".chip.dragging");
if (dragging) {
zone.appendChild(dragging);
save();
}
});
});
}
function resetAll() {
if (!confirm("Reset all rankings?")) return;
localStorage.removeItem(KEY);
document.querySelectorAll(".drop").forEach(d => d.innerHTML = "");
const pool = document.getElementById("pool");
STAGES.forEach(s => pool.appendChild(makeChip(s)));
}
function exportJSON() {
const out = [];
["S","A","B","C","D","E","F"].forEach(t => {
const items = [...document.querySelectorAll(`.drop[data-tier="${t}"] .chip`)].map(c => c.textContent);
out.push(`${t}: ${items.join(", ") || "—"}`);
});
navigator.clipboard.writeText(out.join("\n"));
alert("Ranking copied to clipboard.");
}
init();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment