|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Social Stats Dashboard</title> |
|
<style> |
|
:root { |
|
--bg: #f9f9f7; --card: #fff; --ink: #18181b; --muted: #71717a; --line: #e4e4e7; |
|
--ig: #c9538a; --tt: #2a78d6; --saves: #d98a00; --saves-bg: #fdf3e0; |
|
} |
|
@media (prefers-color-scheme: dark) { |
|
:root { --bg: #111; --card: #1b1b1a; --ink: #f4f3ee; --muted: #999; --line: #333; |
|
--ig: #d55181; --tt: #3987e5; --saves: #e0a52b; --saves-bg: #2a2110; } |
|
} |
|
* { box-sizing: border-box; } |
|
body { font-family: system-ui, sans-serif; background: var(--bg); color: var(--ink); |
|
max-width: 960px; margin: 0 auto; padding: 32px 20px; line-height: 1.45; } |
|
h1 { font-size: 22px; margin: 0 0 4px; } |
|
.sub { color: var(--muted); margin: 0 0 28px; } |
|
h2 { font-size: 15px; margin: 30px 0 12px; } |
|
/* saves + follower charts (plain CSS bars) */ |
|
.chart { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 18px; } |
|
.bar { display: grid; grid-template-columns: 100px 1fr 70px; align-items: center; gap: 12px; margin: 9px 0; font-size: 13px; } |
|
.bar .name { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } |
|
.bar .track { background: var(--line); border-radius: 5px; height: 14px; } |
|
.bar .fill { height: 14px; border-radius: 5px; } |
|
.bar .val { text-align: right; font-weight: 600; } |
|
/* post cards */ |
|
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; } |
|
.card { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 16px; } |
|
.tag { font-size: 11px; font-weight: 600; text-transform: uppercase; padding: 3px 8px; border-radius: 6px; color: #fff; } |
|
.tag.instagram { background: var(--ig); } .tag.tiktok { background: var(--tt); } |
|
.card h3 { font-size: 14px; margin: 10px 0 2px; } |
|
.card .date { color: var(--muted); font-size: 12px; } |
|
.saves { background: var(--saves-bg); border-radius: 10px; padding: 12px; margin: 12px 0; text-align: center; } |
|
.saves .n { font-size: 30px; font-weight: 700; color: var(--saves); } |
|
.saves .lbl { font-size: 11px; font-weight: 600; text-transform: uppercase; color: var(--saves); } |
|
.saves .rate { font-size: 12px; color: var(--muted); margin-top: 2px; } |
|
.row { display: flex; justify-content: space-between; font-size: 13px; padding: 3px 0; border-top: 1px solid var(--line); } |
|
.row .k { color: var(--muted); } |
|
@media (max-width: 640px) { .bar { grid-template-columns: 80px 1fr 60px; } } |
|
</style> |
|
</head> |
|
<body> |
|
<h1>📊 Social Stats Dashboard</h1> |
|
<p class="sub">Runs entirely in your browser. Your data never leaves your machine.</p> |
|
|
|
<h2 style="color:var(--saves)">💾 Saves per post</h2> |
|
<div class="chart" id="savesChart"></div> |
|
|
|
<h2>Follower growth</h2> |
|
<div class="chart" id="followerChart"></div> |
|
|
|
<h2>Posts</h2> |
|
<div class="cards" id="cards"></div> |
|
|
|
<script> |
|
// ============================================================ |
|
// EDIT YOUR DATA HERE — change these two lists, then save & refresh. |
|
// Post fields: platform ("instagram"|"tiktok"), title, date, views, |
|
// likes, comments, saves, shares. Follower fields: date, count. |
|
// (These are example numbers.) |
|
// ============================================================ |
|
const POSTS = [ |
|
{ platform: "instagram", title: "Studio tour reel", date: "2026-06-02", views: 42000, likes: 2100, comments: 180, saves: 1500, shares: 340 }, |
|
{ platform: "tiktok", title: "3 quick editing tips", date: "2026-06-05", views: 88000, likes: 5400, comments: 420, saves: 2600, shares: 910 }, |
|
{ platform: "instagram", title: "Behind the scenes", date: "2026-06-11", views: 15000, likes: 980, comments: 75, saves: 1900, shares: 120 }, |
|
{ platform: "tiktok", title: "Answering your questions", date: "2026-06-18", views: 31000, likes: 2200, comments: 510, saves: 640, shares: 200 } |
|
]; |
|
const FOLLOWERS = [ |
|
{ date: "2026-06-01", count: 5200 }, |
|
{ date: "2026-06-10", count: 6100 }, |
|
{ date: "2026-06-20", count: 7450 } |
|
]; |
|
// ============================================================ |
|
|
|
const fmt = n => (+n).toLocaleString(); |
|
const rate = p => p.views ? (p.saves / p.views * 100) : 0; |
|
const esc = s => String(s).replace(/[&<>"]/g, c => ({ "&":"&","<":"<",">":">",'"':""" }[c])); |
|
|
|
function bars(el, rows, max, color) { |
|
el.innerHTML = rows.map(r => ` |
|
<div class="bar"> |
|
<span class="name">${esc(r.name)}</span> |
|
<span class="track"><span class="fill" style="width:${Math.max(2, r.value / max * 100)}%;background:${color}"></span></span> |
|
<span class="val">${fmt(r.value)}</span> |
|
</div>`).join(""); |
|
} |
|
|
|
// saves per post (sorted, hero metric) |
|
const saves = [...POSTS].sort((a, b) => b.saves - a.saves); |
|
bars(document.getElementById("savesChart"), |
|
saves.map(p => ({ name: p.title, value: p.saves })), Math.max(...saves.map(p => p.saves), 1), "var(--saves)"); |
|
|
|
// follower growth over time |
|
const foll = [...FOLLOWERS].sort((a, b) => a.date.localeCompare(b.date)); |
|
bars(document.getElementById("followerChart"), |
|
foll.map(f => ({ name: f.date, value: f.count })), Math.max(...foll.map(f => f.count), 1), "var(--tt)"); |
|
|
|
// post cards — instagram + tiktok, saves as the hero |
|
document.getElementById("cards").innerHTML = [...POSTS].sort((a, b) => a.date.localeCompare(b.date)).map(p => ` |
|
<div class="card"> |
|
<span class="tag ${p.platform}">${esc(p.platform)}</span> |
|
<h3>${esc(p.title)}</h3> |
|
<div class="date">${esc(p.date)}</div> |
|
<div class="saves"> |
|
<div class="n">${fmt(p.saves)}</div> |
|
<div class="lbl">Saves</div> |
|
<div class="rate">${rate(p).toFixed(2)}% save rate</div> |
|
</div> |
|
<div class="row"><span class="k">Views</span><span>${fmt(p.views)}</span></div> |
|
<div class="row"><span class="k">Likes</span><span>${fmt(p.likes)}</span></div> |
|
<div class="row"><span class="k">Comments</span><span>${fmt(p.comments)}</span></div> |
|
<div class="row"><span class="k">Shares</span><span>${fmt(p.shares)}</span></div> |
|
</div>`).join(""); |
|
</script> |
|
</body> |
|
</html> |