Free
Newsletter
Postwave Newsletter
A newsletter block extracted from the Postwave template, available in Tailwind CSS and Bootstrap.
0
2
A newsletter block extracted from the Postwave template, available in Tailwind CSS and Bootstrap.
<!DOCTYPE html>
<html lang="en">
<head>
<script>
window.FontAwesomeConfig = { autoReplaceSvg: 'nest' };
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Postwave Newsletter | Tailwind CSS UI Block</title>
<meta name="description" content="A newsletter block extracted from the Postwave template, available in Tailwind CSS and Bootstrap.">
<meta name="keywords" content="Postwave, Tailwind CSS template, HTML website template, blog template, creator economy blog, influencer marketing, resources page, article template, newsletter signup, dark blog design">
<meta name="author" content="Let's templates">
<link rel="canonical" href="">
<meta property="og:type" content="product">
<meta property="og:site_name" content="Let's templates">
<meta property="og:locale" content="en_US">
<meta property="og:url" content="">
<meta property="og:title" content="Postwave | Tailwind CSS Blog Template">
<meta property="og:description" content="A dark, editorial Tailwind CSS blog template for creator platforms, with live search, image-led post cards and a personal reading list.">
<meta property="og:image" content="">
<meta property="og:image:alt" content="Postwave template preview">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:url" content="">
<meta name="twitter:title" content="Postwave | Tailwind CSS Blog Template">
<meta name="twitter:description" content="A dark, editorial Tailwind CSS blog template for creator platforms, with live search, image-led post cards and a personal reading list.">
<meta name="twitter:image" content="">
<meta name="twitter:image:alt" content="Postwave template preview">
<script src="https://cdn.tailwindcss.com/3.4.17"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Antonio:wght@500;600;700&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
colors: { night: { DEFAULT: '#0a0a0a', page: '#1b1b1d', card: '#141416', line: '#2c2c30' }, volt: { DEFAULT: '#c6ff3d', soft: '#e6ffb0' } },
fontFamily: { sans: ['Inter', 'sans-serif'], display: ['Antonio', 'sans-serif'] }
}
}
}
</script>
<style>
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: 180ms ease-out both page-fade-out; }
::view-transition-new(root) { animation: 220ms ease-in both page-fade-in; }
@keyframes page-fade-out { to { opacity: 0; } }
@keyframes page-fade-in { from { opacity: 0; } }
html { scroll-behavior: smooth; }
[hidden] { display: none !important; }
.chips { scrollbar-width: none; }
.chips::-webkit-scrollbar { display: none; }
.ticker { animation: ticker 32s linear infinite; }
.ticker:hover { animation-play-state: paused; }
@keyframes ticker { to { transform: translateX(-50%); } }
.ticket { -webkit-mask: radial-gradient(circle 14px at 0 50%, transparent 98%, #000) left / 51% 100% no-repeat, radial-gradient(circle 14px at 100% 50%, transparent 98%, #000) right / 51% 100% no-repeat; mask: radial-gradient(circle 14px at 0 50%, transparent 98%, #000) left / 51% 100% no-repeat, radial-gradient(circle 14px at 100% 50%, transparent 98%, #000) right / 51% 100% no-repeat; }
.reveal-ready [data-reveal] { opacity: 0; transform: translateY(24px); transition: opacity .6s ease, transform .6s ease; transition-delay: var(--reveal-delay, 0ms); }
.reveal-ready [data-reveal].is-visible { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
.ticker { animation: none; }
::view-transition-old(root), ::view-transition-new(root) { animation: none; }
}
</style>
</head>
<body class="bg-night-page font-sans text-white antialiased">
<% active_page = "index" %>
<% nav = { "brands" => "For brands", "ambassadors" => "For creators", "about" => "About", "cases" => "Cases", "index" => "Resources" } %>
<% nav.each do |slug, label| %>
<% active = active_page == slug %>
<% end %>
<% nav.merge("contact" => "Request a demo").each do |slug, label| %>
<% active = active_page == slug %>
<% end %>
<% categories = ["Creator economy", "Brand deals", "Short video", "Analytics", "Community", "Tools"] %>
<% posts = [["How three creators turned a side channel into a full-time studio", "Creator economy", "18/03/2026", "Noor Haddad", "1589156280159-27698a70f29e", "Woman looking up at city lights at dusk", "creator-studio"], ["The brief template brands keep reusing", "Brand deals", "14/03/2026", "Leo Brandt", "1550684848-fac1c5b4e853", "Macro shot of colourful oil bubbles", "brief-template"], ["Hooks that hold: what the first two seconds really decide", "Short video", "11/03/2026", "Mina Sato", "1504593811423-6dd665756598", "Man in a waistcoat standing on a city street", "first-two-seconds"], ["Reading your analytics without losing your mind", "Analytics", "07/03/2026", "Kai Osei", "1550745165-9bc0b252726f", "Retro consoles lit with pink and blue neon", "brief-template"], ["Why micro communities beat big follower counts", "Community", "04/03/2026", "Ana Ruiz", "1517841905240-472988babdf9", "Smiling young woman in a denim jacket", "creator-studio"], ["Building a content calendar you'll actually keep", "Tools", "28/02/2026", "Sam Ito", "1581291518857-4e27b48ff24e", "Hands sketching layouts on paper", "first-two-seconds"], ["Rate cards in 2026: what creators charge now", "Brand deals", "24/02/2026", "Noor Haddad", "1600096194534-95cf5ece04cf", "Red like-notification sign painted on a white wall", "brief-template"], ["Loops, cuts and captions: an editing cheat sheet", "Short video", "20/02/2026", "Mina Sato", "1563089145-599997674d42", "Purple and red abstract light", "first-two-seconds"], ["What brands look for before the first DM", "Creator economy", "16/02/2026", "Leo Brandt", "1529626455594-4ff0802cfb7e", "Woman with red hair looking to the side", "creator-studio"], ["Tools we use to plan a month of posts in an hour", "Tools", "12/02/2026", "Sam Ito", "1557672172-298e090bd0f1", "Swirls of pink and blue paint", "brief-template"], ["Comments are the new reach: running a reply sprint", "Community", "08/02/2026", "Ana Ruiz", "1524504388940-b1c1722653e1", "Woman with long hair in a dark top", "creator-studio"]] %>
<% (["All"] + categories).each do |category| %>
<% end %>
<% title, category, date, author, photo, alt, slug = posts.first %>
<% posts.drop(1).each_with_index do |(title, category, date, author, photo, alt, slug), index| %>
<% end %>
<% 2.times do %>
<% categories.each do |category| %>
<% end %>
<% end %>
<% events = [["APR", "02", "Live: pricing your first brand deal", "Noor Haddad & guests", "45 min"], ["APR", "16", "Short video teardown: what hooked 1M viewers", "Mina Sato", "60 min"], ["MAY", "07", "Ask a brand manager anything", "Leo Brandt", "50 min"]] %>
<% events.each_with_index do |(month, day, title, host, length), index| %>
<% end %>
<!-- block:start newsletter -->
<section class="px-3 py-12 md:px-4 md:py-16">
<div class="ticket mx-auto grid max-w-4xl overflow-hidden rounded-2xl bg-volt text-night md:grid-cols-[1fr_15rem]">
<div class="p-8 md:p-10">
<p class="text-xs font-semibold uppercase tracking-widest">Admit one · every Friday</p>
<h2 class="mt-3 font-display text-5xl font-bold leading-none md:text-6xl">The Friday drop</h2>
<p class="mt-3 max-w-md text-sm text-night/75">Five links, one trend and a creator worth following — straight to your inbox.</p>
<form class="mt-6 flex flex-col gap-2 sm:flex-row" data-newsletter novalidate>
<label for="newsletter-email" class="sr-only">Email</label>
<input id="newsletter-email" name="email" type="email" required autocomplete="email" placeholder="you@studio.com" class="min-w-0 flex-1 rounded-lg border-2 border-night bg-transparent px-4 py-3 text-sm placeholder:text-night/50 focus:outline-none focus:ring-2 focus:ring-night">
<button type="submit" class="rounded-lg bg-night px-6 py-3 text-sm font-semibold text-volt transition hover:bg-white hover:text-night">Get the drop</button>
</form>
<p class="mt-3 text-xs" data-form-status role="status"></p>
</div>
<div class="flex flex-row items-center justify-between gap-4 border-t-2 border-dashed border-night/40 p-8 md:flex-col md:justify-center md:border-l-2 md:border-t-0">
<p class="font-display text-7xl font-bold leading-none">No.<br>148</p>
<i class="fa-solid fa-barcode text-6xl" aria-hidden="true"></i>
</div>
</div>
</section>
<!-- block:end newsletter -->
<script>
const reducedMotion = matchMedia('(prefers-reduced-motion: reduce)').matches;
const watchMotion = !reducedMotion && !navigator.webdriver && 'IntersectionObserver' in window;
document.querySelectorAll('[data-menu-toggle]').forEach((button) => {
const menu = document.getElementById(button.getAttribute('aria-controls'));
button.addEventListener('click', () => {
const open = button.getAttribute('aria-expanded') === 'true';
button.setAttribute('aria-expanded', String(!open));
menu.classList.toggle('hidden', open);
});
});
document.querySelectorAll('[data-search-root]').forEach((root) => {
const input = root.querySelector('[data-search]');
const chips = [...root.querySelectorAll('[data-chip]')];
const posts = [...document.querySelectorAll('[data-post]')];
const empty = document.querySelector('[data-empty]');
let category = 'All';
const paint = () => {
const query = input.value.trim().toLowerCase();
let shown = 0;
posts.forEach((post) => {
const match = (category === 'All' || post.dataset.category === category) && (!query || post.dataset.title.toLowerCase().includes(query) || post.dataset.category.toLowerCase().includes(query));
post.hidden = !match;
if (match) shown += 1;
});
root.querySelector('[data-result-count]').textContent = shown;
if (empty) empty.hidden = shown > 0;
};
chips.forEach((chip) => chip.addEventListener('click', () => {
category = chip.dataset.chip;
chips.forEach((other) => other.setAttribute('aria-pressed', String(other === chip)));
paint();
}));
input.addEventListener('input', paint);
document.querySelector('[data-reset]')?.addEventListener('click', () => { input.value = ''; chips[0].click(); });
});
document.querySelectorAll('[data-reading]').forEach((root) => {
const toggle = root.querySelector('[data-reading-toggle]');
const panel = document.getElementById(toggle.getAttribute('aria-controls'));
const list = root.querySelector('[data-reading-items]');
const read = () => { try { return JSON.parse(localStorage.getItem('postwave-reading') || '[]'); } catch (error) { return []; } };
const write = (items) => { try { localStorage.setItem('postwave-reading', JSON.stringify(items)); } catch (error) { /* storage unavailable */ } };
let saved = read();
const paint = () => {
root.querySelector('[data-reading-count]').textContent = saved.length;
root.querySelector('[data-reading-empty]').hidden = saved.length > 0;
list.replaceChildren(...saved.map((item) => {
const li = document.createElement('li');
const link = document.createElement('a');
link.href = item.href; link.textContent = item.title; link.className = 'block rounded-lg bg-white/5 px-3 py-2 hover:text-volt';
li.append(link);
return li;
}));
document.querySelectorAll('[data-save]').forEach((button) => button.setAttribute('aria-pressed', String(saved.some((item) => item.id === button.dataset.save))));
};
document.querySelectorAll('[data-save]').forEach((button) => button.addEventListener('click', () => {
const id = button.dataset.save;
saved = saved.some((item) => item.id === id) ? saved.filter((item) => item.id !== id) : [...saved, { id, title: button.dataset.saveTitle, href: button.dataset.saveHref }];
write(saved);
paint();
}));
toggle.addEventListener('click', () => { const open = panel.hidden; panel.hidden = !open; toggle.setAttribute('aria-expanded', String(open)); });
document.addEventListener('keydown', (event) => { if (event.key === 'Escape' && !panel.hidden) { panel.hidden = true; toggle.setAttribute('aria-expanded', 'false'); toggle.focus(); } });
paint();
});
document.querySelectorAll('[data-remind]').forEach((button) => button.addEventListener('click', () => {
const on = button.getAttribute('aria-pressed') !== 'true';
button.setAttribute('aria-pressed', String(on));
button.querySelector('[data-remind-label]').textContent = on ? 'Reminder set' : 'Remind me';
}));
document.querySelectorAll('[data-newsletter]').forEach((form) => {
const status = form.parentElement.querySelector('[data-form-status]');
form.addEventListener('submit', (event) => {
event.preventDefault();
const email = form.elements.email;
if (!email.checkValidity()) { status.textContent = 'Please enter a valid email address.'; email.focus(); return; }
status.textContent = "You're in. First drop lands this Friday.";
form.reset();
});
});
if (watchMotion) {
document.documentElement.classList.add('reveal-ready');
const observer = new IntersectionObserver((entries) => entries.forEach((entry) => {
if (entry.isIntersecting) { entry.target.classList.add('is-visible'); observer.unobserve(entry.target); }
}), { threshold: 0.15 });
document.querySelectorAll('[data-reveal]').forEach((el) => observer.observe(el));
}
</script>
</body>
</html>
Blog
Unlock the entire library of premium templates and blocks. Renews automatically, cancel anytime.
Tailwind CSS · HTML download