Podcast — Host Bios
A dark, audio-first host bios section for a podcast site — glowing host cards with photo, name, role badge, a punchy bio, and social links. Each card lifts with a violet-to-cyan gradient glow on hover, a small waveform accent pulses under the avatar, and social buttons reveal tooltips. Filter chips let visitors jump between hosts, producers, and guest voices, with a live count and a copy-to-clipboard handle action.
MCP
Code
:root {
--bg: #0c0c10;
--surface: #15151c;
--surface-2: #1d1d27;
--violet: #8b5cf6;
--violet-d: #7c3aed;
--cyan: #22d3ee;
--pink: #f472b6;
--ink: #f4f4f8;
--muted: #9a9aab;
--line: rgba(255, 255, 255, 0.1);
--line-2: rgba(255, 255, 255, 0.18);
--white: #fff;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 20px;
}
* {
box-sizing: border-box;
}
html {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
margin: 0;
min-height: 100vh;
background:
radial-gradient(1100px 600px at 12% -10%, rgba(139, 92, 246, 0.18), transparent 60%),
radial-gradient(900px 520px at 100% 0%, rgba(34, 211, 238, 0.12), transparent 55%),
var(--bg);
color: var(--ink);
font-family: "Inter", system-ui, -apple-system, sans-serif;
line-height: 1.5;
}
.wrap {
max-width: 1120px;
margin: 0 auto;
padding: 56px 24px 80px;
}
/* ---------- Header ---------- */
.head {
margin-bottom: 40px;
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--cyan);
background: rgba(34, 211, 238, 0.08);
border: 1px solid var(--line);
padding: 7px 12px;
border-radius: 999px;
}
.eyebrow .dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--cyan);
box-shadow: 0 0 0 0 rgba(34, 211, 238, 0.6);
animation: pulse 2s ease-out infinite;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(34, 211, 238, 0.55);
}
70% {
box-shadow: 0 0 0 9px rgba(34, 211, 238, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(34, 211, 238, 0);
}
}
.head h1 {
margin: 18px 0 10px;
font-size: clamp(28px, 5vw, 44px);
font-weight: 800;
letter-spacing: -0.02em;
line-height: 1.1;
}
.head h1 em {
font-style: normal;
background: linear-gradient(100deg, var(--violet), var(--cyan));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.lede {
margin: 0;
max-width: 56ch;
color: var(--muted);
font-size: 16px;
}
/* ---------- Controls ---------- */
.controls {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 14px;
margin-top: 28px;
}
.chips {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.chip {
font: inherit;
font-size: 13px;
font-weight: 600;
color: var(--muted);
background: var(--surface);
border: 1px solid var(--line);
padding: 8px 16px;
border-radius: 999px;
cursor: pointer;
transition:
color 0.18s ease,
background 0.18s ease,
border-color 0.18s ease,
transform 0.12s ease;
}
.chip:hover {
color: var(--ink);
border-color: var(--line-2);
transform: translateY(-1px);
}
.chip.is-active {
color: var(--white);
border-color: transparent;
background: linear-gradient(100deg, var(--violet-d), var(--violet));
box-shadow: 0 6px 18px rgba(124, 58, 237, 0.4);
}
.chip:focus-visible {
outline: 2px solid var(--cyan);
outline-offset: 2px;
}
.count {
margin: 0;
font-size: 13px;
color: var(--muted);
}
.count span {
color: var(--ink);
font-weight: 700;
}
/* ---------- Grid ---------- */
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
@media (max-width: 900px) {
.grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* ---------- Card ---------- */
.card {
position: relative;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 26px 22px 20px;
overflow: hidden;
transition:
transform 0.25s cubic-bezier(0.2, 0.7, 0.2, 1),
border-color 0.25s ease,
box-shadow 0.25s ease;
animation: rise 0.5s both;
}
@keyframes rise {
from {
opacity: 0;
transform: translateY(14px);
}
}
.card::before {
content: "";
position: absolute;
inset: -1px;
border-radius: inherit;
padding: 1px;
background: linear-gradient(140deg, var(--violet), var(--cyan));
-webkit-mask:
linear-gradient(#000 0 0) content-box,
linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
opacity: 0;
transition: opacity 0.25s ease;
pointer-events: none;
}
.card:hover {
transform: translateY(-6px);
border-color: transparent;
box-shadow:
0 20px 44px rgba(0, 0, 0, 0.5),
0 0 40px rgba(139, 92, 246, 0.25);
}
.card:hover::before {
opacity: 1;
}
.card.is-hidden {
display: none;
}
/* Avatar */
.avatar-wrap {
position: relative;
width: 92px;
margin-bottom: 16px;
}
.avatar {
width: 84px;
height: 84px;
border-radius: 50%;
object-fit: cover;
display: block;
border: 2px solid var(--line-2);
background: var(--surface-2);
}
.card:hover .avatar {
border-color: var(--cyan);
}
.live {
position: absolute;
top: 2px;
right: 0;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--white);
background: var(--pink);
padding: 3px 8px;
border-radius: 999px;
border: 2px solid var(--surface);
}
/* Waveform accent */
.wave {
display: flex;
align-items: flex-end;
gap: 3px;
height: 18px;
margin-bottom: 14px;
}
.wave span {
width: 3px;
border-radius: 2px;
background: linear-gradient(180deg, var(--cyan), var(--violet));
height: 30%;
opacity: 0.5;
transition: opacity 0.25s ease;
}
.card:hover .wave span {
opacity: 1;
animation: bar 1.1s ease-in-out infinite;
}
.card:hover .wave span:nth-child(2) { animation-delay: 0.1s; }
.card:hover .wave span:nth-child(3) { animation-delay: 0.2s; }
.card:hover .wave span:nth-child(4) { animation-delay: 0.3s; }
.card:hover .wave span:nth-child(5) { animation-delay: 0.15s; }
.card:hover .wave span:nth-child(6) { animation-delay: 0.25s; }
.card:hover .wave span:nth-child(7) { animation-delay: 0.05s; }
@keyframes bar {
0%, 100% { height: 25%; }
50% { height: 100%; }
}
/* Name + role */
.name-row {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.name {
margin: 0;
font-size: 19px;
font-weight: 700;
letter-spacing: -0.01em;
}
.badge {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
padding: 4px 9px;
border-radius: 999px;
border: 1px solid var(--line);
}
.badge[data-role="host"] {
color: var(--violet);
background: rgba(139, 92, 246, 0.12);
}
.badge[data-role="producer"] {
color: var(--cyan);
background: rgba(34, 211, 238, 0.12);
}
.badge[data-role="guest"] {
color: var(--pink);
background: rgba(244, 114, 182, 0.12);
}
.handle {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 8px 0 0;
font-size: 13px;
font-weight: 500;
color: var(--muted);
background: none;
border: none;
padding: 0;
font-family: inherit;
cursor: pointer;
transition: color 0.18s ease;
}
.handle:hover {
color: var(--cyan);
}
.handle:focus-visible {
outline: 2px solid var(--cyan);
outline-offset: 3px;
border-radius: 4px;
}
.handle svg {
width: 13px;
height: 13px;
opacity: 0.7;
}
.bio {
margin: 14px 0 18px;
font-size: 14px;
color: var(--muted);
line-height: 1.6;
}
/* Socials */
.socials {
display: flex;
gap: 8px;
padding-top: 16px;
border-top: 1px solid var(--line);
}
.social {
position: relative;
display: grid;
place-items: center;
width: 38px;
height: 38px;
border-radius: var(--r-sm);
color: var(--muted);
background: var(--surface-2);
border: 1px solid var(--line);
cursor: pointer;
transition:
color 0.18s ease,
background 0.18s ease,
transform 0.14s ease,
border-color 0.18s ease;
}
.social:hover {
color: var(--white);
background: linear-gradient(140deg, var(--violet-d), var(--violet));
border-color: transparent;
transform: translateY(-2px);
}
.social:focus-visible {
outline: 2px solid var(--cyan);
outline-offset: 2px;
}
.social svg {
width: 17px;
height: 17px;
}
.social::after {
content: attr(data-tip);
position: absolute;
bottom: calc(100% + 8px);
left: 50%;
transform: translateX(-50%) translateY(4px);
white-space: nowrap;
font-size: 11px;
font-weight: 600;
color: var(--ink);
background: var(--surface-2);
border: 1px solid var(--line-2);
padding: 4px 8px;
border-radius: 6px;
opacity: 0;
pointer-events: none;
transition:
opacity 0.16s ease,
transform 0.16s ease;
}
.social:hover::after,
.social:focus-visible::after {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
/* ---------- Toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 28px;
transform: translateX(-50%) translateY(24px);
background: var(--surface-2);
color: var(--ink);
border: 1px solid var(--line-2);
padding: 12px 18px;
border-radius: var(--r-md);
font-size: 14px;
font-weight: 600;
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5);
opacity: 0;
pointer-events: none;
transition:
opacity 0.22s ease,
transform 0.22s ease;
z-index: 50;
}
.toast.show {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
.toast::before {
content: "✓";
color: var(--cyan);
margin-right: 8px;
font-weight: 800;
}
/* ---------- Responsive ---------- */
@media (max-width: 520px) {
.wrap {
padding: 40px 16px 64px;
}
.grid {
grid-template-columns: 1fr;
}
.controls {
align-items: flex-start;
flex-direction: column;
}
}
@media (prefers-reduced-motion: reduce) {
* {
animation: none !important;
transition-duration: 0.01ms !important;
}
}// Podcast — Host Bios
// Vanilla JS: renders host cards, role filtering, copy-handle, tooltips.
(function () {
"use strict";
var HOSTS = [
{
name: "Maya Okonkwo",
role: "host",
roleLabel: "Host",
handle: "@mayaonair",
live: true,
photo:
"https://images.unsplash.com/photo-1494790108377-be9c29b29330?auto=format&fit=crop&w=200&q=80",
bio: "Founding host and lead interviewer. Ten years chasing the story between the pauses — she can pull a confession out of dead air.",
},
{
name: "Dev Ramachandran",
role: "host",
roleLabel: "Host",
handle: "@devtalksback",
live: false,
photo:
"https://images.unsplash.com/photo-1500648767791-00dcc994a43e?auto=format&fit=crop&w=200&q=80",
bio: "Co-host and resident skeptic. Turns every hot take into a two-way debate and keeps the segment honest when the mics get warm.",
},
{
name: "Priya Anand",
role: "producer",
roleLabel: "Producer",
handle: "@priyacuts",
live: false,
photo:
"https://images.unsplash.com/photo-1487412720507-e7ab37603c6f?auto=format&fit=crop&w=200&q=80",
bio: "Senior producer and sound designer. She shapes the arc, layers the score, and decides which 40 seconds actually make the cut.",
},
{
name: "Marcus Bell",
role: "producer",
roleLabel: "Producer",
handle: "@bellonthebeat",
live: false,
photo:
"https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?auto=format&fit=crop&w=200&q=80",
bio: "Field producer and booker. If a guest is worth hearing, Marcus already has their number saved and a car booked to the studio.",
},
{
name: "Sofia Reyes",
role: "guest",
roleLabel: "Guest Voice",
handle: "@sofiareyes",
live: false,
photo:
"https://images.unsplash.com/photo-1534528741775-53994a69daeb?auto=format&fit=crop&w=200&q=80",
bio: "Recurring guest voice and culture critic. Drops in each month to argue about the internet and leave everyone rethinking their timeline.",
},
{
name: "Theo Nakamura",
role: "guest",
roleLabel: "Guest Voice",
handle: "@theonakamura",
live: false,
photo:
"https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?auto=format&fit=crop&w=200&q=80",
bio: "Guest correspondent from the tech beat. Explains the unexplainable in plain language and never once says the word disrupt.",
},
];
var SOCIALS = {
twitter: {
tip: "X / Twitter",
path:
"M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z",
},
instagram: {
tip: "Instagram",
path:
"M12 2.16c3.2 0 3.58.01 4.85.07 1.17.05 1.8.25 2.23.41.56.22.96.48 1.38.9.42.42.68.82.9 1.38.16.42.36 1.06.41 2.23.06 1.27.07 1.65.07 4.85s-.01 3.58-.07 4.85c-.05 1.17-.25 1.8-.41 2.23-.22.56-.48.96-.9 1.38-.42.42-.82.68-1.38.9-.42.16-1.06.36-2.23.41-1.27.06-1.65.07-4.85.07s-3.58-.01-4.85-.07c-1.17-.05-1.8-.25-2.23-.41a3.7 3.7 0 0 1-1.38-.9 3.7 3.7 0 0 1-.9-1.38c-.16-.42-.36-1.06-.41-2.23C2.17 15.58 2.16 15.2 2.16 12s.01-3.58.07-4.85c.05-1.17.25-1.8.41-2.23.22-.56.48-.96.9-1.38.42-.42.82-.68 1.38-.9.42-.16 1.06-.36 2.23-.41C8.42 2.17 8.8 2.16 12 2.16zm0 1.62c-3.15 0-3.5.01-4.74.07-1.14.05-1.76.24-2.17.4-.55.21-.94.47-1.35.88-.41.41-.67.8-.88 1.35-.16.41-.35 1.03-.4 2.17-.06 1.24-.07 1.59-.07 4.74s.01 3.5.07 4.74c.05 1.14.24 1.76.4 2.17.21.55.47.94.88 1.35.41.41.8.67 1.35.88.41.16 1.03.35 2.17.4 1.24.06 1.59.07 4.74.07s3.5-.01 4.74-.07c1.14-.05 1.76-.24 2.17-.4.55-.21.94-.47 1.35-.88.41-.41.67-.8.88-1.35.16-.41.35-1.03.4-2.17.06-1.24.07-1.59.07-4.74s-.01-3.5-.07-4.74c-.05-1.14-.24-1.76-.4-2.17a3.6 3.6 0 0 0-.88-1.35 3.6 3.6 0 0 0-1.35-.88c-.41-.16-1.03-.35-2.17-.4-1.24-.06-1.59-.07-4.74-.07zm0 2.76a5.3 5.3 0 1 1 0 10.6 5.3 5.3 0 0 1 0-10.6zm0 1.62a3.68 3.68 0 1 0 0 7.36 3.68 3.68 0 0 0 0-7.36zm5.5-1.44a1.24 1.24 0 1 1-2.48 0 1.24 1.24 0 0 1 2.48 0z",
},
linkedin: {
tip: "LinkedIn",
path:
"M20.45 20.45h-3.56v-5.57c0-1.33-.02-3.04-1.85-3.04-1.85 0-2.13 1.44-2.13 2.94v5.67H9.35V9h3.42v1.56h.05c.48-.9 1.64-1.85 3.37-1.85 3.6 0 4.27 2.37 4.27 5.46zM5.34 7.43a2.07 2.07 0 1 1 0-4.14 2.07 2.07 0 0 1 0 4.14zM7.12 20.45H3.55V9h3.57zM22.22 0H1.77C.79 0 0 .77 0 1.72v20.56C0 23.23.79 24 1.77 24h20.45c.98 0 1.78-.77 1.78-1.72V1.72C24 .77 23.2 0 22.22 0z",
},
mic: {
tip: "Listen",
path:
"M12 15a3 3 0 0 0 3-3V6a3 3 0 0 0-6 0v6a3 3 0 0 0 3 3zm5-3a1 1 0 0 1 2 0 7 7 0 0 1-6 6.93V22a1 1 0 0 1-2 0v-1.07A7 7 0 0 1 5 12a1 1 0 0 1 2 0 5 5 0 0 0 10 0z",
},
};
var grid = document.getElementById("grid");
var toastEl = document.getElementById("toast");
var shownEl = document.getElementById("shown");
var totalEl = document.getElementById("total");
var toastTimer = null;
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2200);
}
function icon(path) {
return (
'<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="' +
path +
'"/></svg>'
);
}
function socialButton(kind, name) {
var s = SOCIALS[kind];
return (
'<button class="social" type="button" data-tip="' +
s.tip +
'" aria-label="' +
s.tip +
" — " +
name +
'" data-social="' +
kind +
'">' +
icon(s.path) +
"</button>"
);
}
function wave() {
var bars = "";
for (var i = 0; i < 7; i++) bars += "<span></span>";
return '<div class="wave" aria-hidden="true">' + bars + "</div>";
}
function cardHTML(h, idx) {
return (
'<article class="card" data-role="' +
h.role +
'" style="animation-delay:' +
idx * 60 +
'ms">' +
'<div class="avatar-wrap">' +
'<img class="avatar" src="' +
h.photo +
'" alt="Portrait of ' +
h.name +
'" loading="lazy" />' +
(h.live ? '<span class="live">On air</span>' : "") +
"</div>" +
wave() +
'<div class="name-row">' +
'<h2 class="name">' +
h.name +
"</h2>" +
'<span class="badge" data-role="' +
h.role +
'">' +
h.roleLabel +
"</span>" +
"</div>" +
'<button class="handle" type="button" data-handle="' +
h.handle +
'" aria-label="Copy handle ' +
h.handle +
'">' +
icon(
"M16 1H4a2 2 0 0 0-2 2v12h2V3h12zm3 4H8a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h11a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2zm0 16H8V7h11z"
) +
"<span>" +
h.handle +
"</span>" +
"</button>" +
'<p class="bio">' +
h.bio +
"</p>" +
'<div class="socials">' +
socialButton("twitter", h.name) +
socialButton("instagram", h.name) +
socialButton("linkedin", h.name) +
socialButton("mic", h.name) +
"</div>" +
"</article>"
);
}
function render() {
grid.innerHTML = HOSTS.map(cardHTML).join("");
totalEl.textContent = String(HOSTS.length);
shownEl.textContent = String(HOSTS.length);
}
function applyFilter(filter) {
var cards = grid.querySelectorAll(".card");
var shown = 0;
cards.forEach(function (card) {
var match = filter === "all" || card.getAttribute("data-role") === filter;
card.classList.toggle("is-hidden", !match);
if (match) shown++;
});
shownEl.textContent = String(shown);
}
function copyHandle(text) {
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(
function () {
toast("Copied " + text + " to clipboard");
},
function () {
toast("Handle: " + text);
}
);
} else {
toast("Handle: " + text);
}
}
// ----- Events -----
render();
var chips = document.querySelectorAll(".chip");
chips.forEach(function (chip) {
chip.addEventListener("click", function () {
chips.forEach(function (c) {
c.classList.remove("is-active");
c.setAttribute("aria-selected", "false");
});
chip.classList.add("is-active");
chip.setAttribute("aria-selected", "true");
applyFilter(chip.getAttribute("data-filter"));
});
});
grid.addEventListener("click", function (e) {
var handleBtn = e.target.closest(".handle");
if (handleBtn) {
copyHandle(handleBtn.getAttribute("data-handle"));
return;
}
var social = e.target.closest(".social");
if (social) {
var card = social.closest(".card");
var name = card.querySelector(".name").textContent;
toast(SOCIALS[social.getAttribute("data-social")].tip + " · " + name);
}
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Podcast — Host Bios</title>
<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=Inter:wght@400;500;600;700;800&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="wrap">
<header class="head">
<span class="eyebrow">
<span class="dot" aria-hidden="true"></span> Frequency Studios
</span>
<h1>Meet the voices behind <em>Signal & Noise</em></h1>
<p class="lede">
The hosts, producers and guest voices who turn raw tape into the show you press play on
every Thursday.
</p>
<div class="controls">
<div class="chips" role="tablist" aria-label="Filter cast by role">
<button class="chip is-active" role="tab" aria-selected="true" data-filter="all">
All
</button>
<button class="chip" role="tab" aria-selected="false" data-filter="host">Hosts</button>
<button class="chip" role="tab" aria-selected="false" data-filter="producer">
Producers
</button>
<button class="chip" role="tab" aria-selected="false" data-filter="guest">
Guest voices
</button>
</div>
<p class="count" aria-live="polite">
Showing <span id="shown">6</span> of <span id="total">6</span>
</p>
</div>
</header>
<section class="grid" id="grid" aria-label="Host bios"></section>
</main>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>A meet-the-hosts section built for a modern podcast, styled in the show’s dark, audio-first palette. Each host lives on a glassy dark card with a circular avatar, a name, a role badge, a short spoken-word bio, and a row of social buttons. On hover the card lifts and picks up a violet-to-cyan glow, while a tiny animated waveform pulses beneath the avatar to keep the audio theme front and center.
The layout is a responsive grid that collapses to a single column below 520px. A row of filter chips at the top lets visitors narrow the cast to hosts, producers, or guest voices, updating a live “showing N of M” count as they go. Social buttons show a tooltip on focus and hover, and a copy-handle button drops the host’s @handle onto the clipboard with a toast confirmation.
Everything is vanilla HTML, CSS, and JavaScript — no frameworks or build step. Controls are keyboard-operable with visible focus rings, cards expose accessible labels, and the animated waveform respects reduced-motion preferences so the section stays comfortable for every listener.