Free
0
1
Ascendia
By Jonah Hale
Ascendia is a bold salmon-and-teal blog template for an online magazine about women founders, with a vertical side navigation, an offset photo hero, a topic ticker, filterable stories, a day-rate calculator, columnists, events with RSVP and stories, events, about, contact and article pages.
Download Codebase
Commercial License<%# letstemplates: access=premium category=blog niches=blog,consulting,online-courses %>
<!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>Ascendia | Tailwind CSS Blog Template</title>
<meta name="description" content="Ascendia is a bold salmon-and-teal Tailwind CSS blog template for an online magazine about women founders, with a vertical side navigation, an offset photo hero, a topic ticker, filterable stories, a day-rate calculator, columnists, events with RSVP and stories, events, about, contact and article pages.">
<meta name="keywords" content="Ascendia, Tailwind CSS template, HTML website template, blog template, online magazine, women founders, entrepreneurship blog, editorial, events, newsletter">
<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="Ascendia | Tailwind CSS Blog Template">
<meta property="og:description" content="A bold editorial Tailwind CSS magazine template with a side navigation, filterable stories, a day-rate calculator and article pages.">
<meta property="og:image" content="">
<meta property="og:image:alt" content="Ascendia template preview">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:url" content="">
<meta name="twitter:title" content="Ascendia | Tailwind CSS Blog Template">
<meta name="twitter:description" content="A bold editorial Tailwind CSS magazine template with a side navigation, filterable stories, a day-rate calculator and article pages.">
<meta name="twitter:image" content="">
<meta name="twitter:image:alt" content="Ascendia 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=League+Spartan:wght@500;700;800&family=DM+Sans:wght@400;500;700&display=swap" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
colors: { salmon: { DEFAULT: '#f7979a', soft: '#fde3df' }, teal: { DEFAULT: '#06292c', soft: '#123c40' }, cream: '#fff6ef', mustard: '#f2b84b' },
fontFamily: { display: ['"League Spartan"', 'sans-serif'], sans: ['"DM Sans"', '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; } }
@keyframes ticker { to { transform: translateX(-50%); } }
html { scroll-behavior: smooth; }
[hidden] { display: none !important; }
.ticker { animation: ticker 40s linear infinite; }
.ticker:hover { animation-play-state: paused; }
.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-cream font-sans text-teal antialiased lg:pl-56">
<% active_page = "index" %>
<% nav = { "index" => "Home", "stories" => "Stories", "events" => "Events", "about" => "About", "contact" => "Contact" } %>
<!-- block:start navbar -->
<header class="sticky top-0 z-40 bg-salmon lg:fixed lg:inset-y-0 lg:left-0 lg:w-56">
<nav class="flex h-full items-center justify-between gap-4 px-5 py-4 lg:flex-col lg:items-start lg:justify-start lg:px-8 lg:py-10" aria-label="Main">
<a href="index.html" class="font-display text-3xl font-extrabold uppercase leading-none tracking-tight text-white">Ascend<span class="text-teal">/</span>ia</a>
<ul class="hidden flex-col gap-3 font-display text-lg uppercase tracking-wide lg:mt-auto lg:flex">
<% nav.each do |slug, label| %>
<% active = active_page == slug %>
<li><a href="<%= slug %>.html" <%= 'aria-current="page"'.html_safe if active %> class="transition <%= active ? 'font-extrabold text-teal' : 'font-medium text-white hover:text-teal' %>"><%= label %></a></li>
<% end %>
</ul>
<a href="index.html#newsletter" class="hidden w-full bg-teal px-4 py-3 text-center font-display text-sm font-bold uppercase tracking-wider text-white transition hover:bg-white hover:text-teal lg:mt-10 lg:block">Subscribe</a>
<button type="button" data-menu-toggle aria-controls="mobile-menu" aria-expanded="false" class="grid h-11 w-11 place-items-center border-2 border-teal text-teal lg:hidden" aria-label="Open menu"><i class="fa-solid fa-bars" aria-hidden="true"></i></button>
</nav>
<div id="mobile-menu" class="hidden border-t-2 border-teal/20 bg-salmon px-5 pb-6 lg:hidden">
<ul class="grid gap-1 pt-3 font-display text-3xl uppercase">
<% nav.each do |slug, label| %>
<% active = active_page == slug %>
<li><a href="<%= slug %>.html" <%= 'aria-current="page"'.html_safe if active %> class="block py-2 <%= active ? 'font-extrabold text-teal' : 'font-medium text-white' %>"><%= label %></a></li>
<% end %>
</ul>
</div>
</header>
<!-- block:end navbar -->
<!-- block:start hero -->
<section class="bg-salmon">
<div class="mx-auto grid max-w-6xl gap-12 px-5 pb-20 pt-10 md:px-10 lg:grid-cols-[1.2fr_1fr] lg:items-center lg:pt-20">
<div>
<p class="font-display text-sm font-bold uppercase tracking-[.2em] text-white" data-reveal>Issue 14 · Spring</p>
<h1 class="mt-5 font-display text-6xl font-extrabold uppercase leading-[.9] tracking-tight md:text-8xl" data-reveal style="--reveal-delay: 80ms">Look up.<br>Build up.</h1>
<p class="mt-8 max-w-lg text-lg leading-relaxed" data-reveal style="--reveal-delay: 160ms">Honest stories, playbooks and numbers from women building companies on their own terms. Fewer slogans, more spreadsheets.</p>
<div class="mt-8 flex flex-wrap gap-3" data-reveal style="--reveal-delay: 220ms">
<a href="stories.html" class="bg-teal px-7 py-4 font-display text-sm font-bold uppercase tracking-wider text-white transition hover:bg-white hover:text-teal">Read the issue</a>
<a href="#newsletter" class="border-2 border-teal px-7 py-4 font-display text-sm font-bold uppercase tracking-wider transition hover:bg-teal hover:text-white">Get it weekly</a>
</div>
</div>
<div class="relative mx-auto w-full max-w-md pr-6 pt-6">
<div class="absolute right-0 top-0 h-[85%] w-[85%] bg-teal" aria-hidden="true"></div>
<img src="https://images.unsplash.com/photo-1589156280159-27698a70f29e?auto=format&fit=crop&w=800&h=1000&q=80" alt="Woman looking up at the city skyline at dusk" class="relative aspect-[4/5] w-full object-cover">
<p class="absolute -bottom-5 left-4 bg-mustard px-4 py-2 font-display text-sm font-bold uppercase tracking-wider">Cover: Imani Cole, founder</p>
</div>
</div>
</section>
<!-- block:end hero -->
<% topics = ["Money", "Leadership", "First hires", "Pricing", "Fundraising", "Burnout", "Side projects", "Negotiation"] %>
<!-- block:start marquee -->
<section class="overflow-hidden border-y-2 border-teal bg-cream py-5" aria-label="Topics">
<ul class="ticker flex w-max gap-10 whitespace-nowrap font-display text-3xl font-extrabold uppercase md:text-4xl">
<% 2.times do |copy| %>
<% topics.each do |topic| %>
<li class="flex items-center gap-10" <%= 'aria-hidden="true"'.html_safe if copy == 1 %>><a href="stories.html" class="transition hover:text-salmon" <%= 'tabindex="-1"'.html_safe if copy == 1 %>><%= topic %></a><span class="text-salmon" aria-hidden="true">✳</span></li>
<% end %>
<% end %>
</ul>
</section>
<!-- block:end marquee -->
<% posts = [["money", "Money", "The first hire nobody tells you to make", "Why a part-time bookkeeper beat a marketer for three founders we spoke to.", "first-hire", "1573496359142-b8d87734a5a2", "Smiling woman standing in a modern office", "6 min"], ["pricing", "Pricing", "Charge for the outcome, not the hour", "A four-step script for raising your rates without losing your best clients.", "pricing-yourself", "1551836022-d5d88e9218df", "Two women talking over a laptop at a wooden table", "8 min"], ["leadership", "Leadership", "The quiet launch: shipping without an audience", "How a ceramicist sold out her first collection to forty email subscribers.", "quiet-launch", "1522202176988-66273c2fd55f", "Three women working together at laptops", "5 min"]] %>
<!-- block:start blog -->
<section class="mx-auto max-w-6xl px-5 py-20 md:px-10 md:py-28" data-latest>
<div class="flex flex-wrap items-end justify-between gap-6">
<h2 class="font-display text-5xl font-extrabold uppercase leading-none md:text-6xl">This week</h2>
<div class="flex flex-wrap gap-2" role="tablist" aria-label="Story topics">
<% [["all", "All"], ["money", "Money"], ["pricing", "Pricing"], ["leadership", "Leadership"]].each_with_index do |(key, label), index| %>
<button type="button" role="tab" id="latest-tab-<%= key %>" aria-controls="latest-list" aria-selected="<%= index.zero? %>" tabindex="<%= index.zero? ? 0 : -1 %>" data-topic="<%= key %>" class="border-2 border-teal px-4 py-2 font-display text-sm font-bold uppercase tracking-wider transition hover:bg-salmon-soft aria-selected:bg-teal aria-selected:text-white"><%= label %></button>
<% end %>
</div>
</div>
<ul id="latest-list" role="tabpanel" aria-label="Latest stories" class="mt-12 grid gap-10 md:grid-cols-3">
<% posts.each_with_index do |(topic, label, title, text, slug, photo, alt, time), index| %>
<li data-post-topic="<%= topic %>" data-reveal style="--reveal-delay: <%= index * 80 %>ms">
<a href="<%= slug %>.html" class="group block">
<span class="relative block pb-3 pr-3"><span class="absolute bottom-0 right-0 h-[90%] w-[90%] bg-salmon transition group-hover:translate-x-1 group-hover:translate-y-1" aria-hidden="true"></span><img src="https://images.unsplash.com/photo-<%= photo %>?auto=format&fit=crop&w=700&h=520&q=75" alt="<%= alt %>" class="relative aspect-[4/3] w-full object-cover"></span>
<span class="mt-5 flex items-center gap-3 font-display text-xs font-bold uppercase tracking-[.2em]"><span class="bg-teal px-2 py-1 text-white"><%= label %></span><%= time %> read</span>
<span class="mt-3 block font-display text-2xl font-bold uppercase leading-tight group-hover:underline group-hover:decoration-salmon group-hover:decoration-4 group-hover:underline-offset-4"><%= title %></span>
<span class="mt-2 block text-teal/75"><%= text %></span>
</a>
</li>
<% end %>
</ul>
<p class="mt-8 text-sm text-teal/60" aria-live="polite"><span data-post-count><%= posts.size %></span> stories shown · <a href="stories.html" class="font-bold underline underline-offset-4">See the archive</a></p>
</section>
<!-- block:end blog -->
<!-- block:start demo -->
<section class="bg-teal text-white" data-rate>
<div class="mx-auto grid max-w-6xl gap-12 px-5 py-20 md:px-10 md:py-28 lg:grid-cols-[1fr_1.1fr]">
<div>
<p class="font-display text-sm font-bold uppercase tracking-[.2em] text-salmon">Tool of the issue</p>
<h2 class="mt-4 font-display text-5xl font-extrabold uppercase leading-[.95] md:text-6xl">What should your day rate be?</h2>
<p class="mt-6 max-w-md text-white/75">Most of our readers undercharge by a third. Put in what you want to earn and how you really work; we will do the maths.</p>
<div class="mt-10 grid gap-7">
<% [["income", "Take-home goal", 20000, 200000, 2500, 72500, "$"], ["weeks", "Working weeks a year", 30, 50, 1, 44, ""], ["days", "Billable days a week", 1, 5, 0.5, 3, ""], ["costs", "Yearly business costs", 0, 40000, 500, 9000, "$"]].each do |(key, label, min, max, step, value, prefix)| %>
<label class="grid gap-2"><span class="flex justify-between font-display text-sm font-bold uppercase tracking-wider"><%= label %><output class="text-mustard" data-rate-out="<%= key %>"><%= prefix %><%= value.is_a?(Integer) && value > 999 ? value.to_s.reverse.scan(/\d{1,3}/).join(',').reverse : value %></output></span><input type="range" min="<%= min %>" max="<%= max %>" step="<%= step %>" value="<%= value %>" data-rate-input="<%= key %>" data-prefix="<%= prefix %>" class="accent-[#f7979a]"></label>
<% end %>
</div>
</div>
<div class="self-center">
<div class="relative pb-4 pr-4">
<div class="absolute bottom-0 right-0 h-full w-full translate-x-0 bg-salmon" aria-hidden="true"></div>
<div class="relative bg-cream p-8 text-teal md:p-10" aria-live="polite">
<p class="font-display text-sm font-bold uppercase tracking-[.2em]">Your day rate</p>
<p class="mt-2 font-display text-7xl font-extrabold md:text-8xl" data-rate-day>$801</p>
<dl class="mt-8 grid grid-cols-2 gap-6 border-t-2 border-teal pt-6">
<div><dt class="text-sm text-teal/60">Billable days a year</dt><dd class="font-display text-3xl font-bold" data-rate-days>132</dd></div>
<div><dt class="text-sm text-teal/60">Revenue needed</dt><dd class="font-display text-3xl font-bold" data-rate-revenue>$105,667</dd></div>
</dl>
<p class="mt-6 text-sm text-teal/70">Includes 25% set aside for tax. <a href="pricing-yourself.html" class="font-bold underline underline-offset-4">Read: charge for the outcome</a></p>
</div>
</div>
</div>
</div>
</section>
<!-- block:end demo -->
<% writers = [["Imani Cole", "Money editor", "money", "1494790108377-be9c29b29330", "Smiling woman in a red top"], ["Sasha Brenner", "Leadership", "leadership", "1544005313-94ddf0286df2", "Woman with short hair by a lake"], ["Marisol Vega", "Pricing columnist", "pricing", "1438761681033-6461ffad8d80", "Woman in a striped top in soft light"], ["Noor Haddad", "Money", "money", "1607746882042-944635dfe10e", "Woman with dark hair against a dark wall"], ["Freya Lund", "Leadership", "leadership", "1573497019940-1c28c88b4f3e", "Woman with curly hair smiling in an office"]] %>
<!-- block:start team -->
<section class="mx-auto max-w-6xl px-5 py-20 md:px-10 md:py-28" data-writers>
<div class="flex flex-wrap items-end justify-between gap-6">
<h2 class="font-display text-5xl font-extrabold uppercase leading-none md:text-6xl">The columnists</h2>
<div class="flex flex-wrap items-center gap-2">
<div class="flex gap-2" role="group" aria-label="Filter columnists">
<% [["all", "All"], ["money", "Money"], ["leadership", "Leadership"], ["pricing", "Pricing"]].each do |(key, label)| %>
<button type="button" data-beat="<%= key %>" aria-pressed="<%= key == 'all' %>" class="px-3 py-2 font-display text-sm font-bold uppercase tracking-wider underline-offset-8 transition hover:text-salmon aria-pressed:underline aria-pressed:decoration-4 aria-pressed:decoration-salmon"><%= label %></button>
<% end %>
</div>
<button type="button" data-writers-prev class="grid h-11 w-11 place-items-center border-2 border-teal transition hover:bg-teal hover:text-white" aria-label="Previous columnists"><i class="fa-solid fa-arrow-left" aria-hidden="true"></i></button>
<button type="button" data-writers-next class="grid h-11 w-11 place-items-center bg-teal text-white transition hover:bg-salmon" aria-label="Next columnists"><i class="fa-solid fa-arrow-right" aria-hidden="true"></i></button>
</div>
</div>
<ul class="mt-12 flex snap-x gap-5 overflow-x-auto pb-4" data-writers-track>
<% writers.each do |(name, beat, key, photo, alt)| %>
<li class="w-64 shrink-0 snap-start" data-writer-beat="<%= key %>">
<img src="https://images.unsplash.com/photo-<%= photo %>?auto=format&fit=crop&w=500&h=620&q=75" alt="<%= alt %>" class="aspect-[4/5] w-full object-cover grayscale transition hover:grayscale-0">
<p class="mt-4 font-display text-xl font-bold uppercase"><%= name %></p>
<p class="text-sm text-teal/60"><%= beat %></p>
</li>
<% end %>
</ul>
</section>
<!-- block:end team -->
<% events = [["Apr 18", "Pricing clinic", "Online · 18:00 CET", 40, 33], ["May 06", "Founders' breakfast", "Lisbon · 08:30", 24, 21], ["Jun 12", "Raise your first round", "Online · 17:00 CET", 120, 64]] %>
<!-- block:start events -->
<section class="bg-salmon-soft" data-rsvp>
<div class="mx-auto max-w-6xl px-5 py-20 md:px-10 md:py-28">
<div class="flex flex-wrap items-end justify-between gap-6">
<h2 class="font-display text-5xl font-extrabold uppercase leading-none md:text-6xl">Meet the readers</h2>
<a href="events.html" class="font-display text-sm font-bold uppercase tracking-wider underline decoration-salmon decoration-4 underline-offset-8">All events</a>
</div>
<ul class="mt-12 border-t-2 border-teal">
<% events.each do |(date, title, place, seats, taken)| %>
<li class="grid gap-4 border-b-2 border-teal py-6 md:grid-cols-[8rem_1fr_auto_auto] md:items-center">
<span class="font-display text-3xl font-extrabold uppercase text-salmon"><%= date %></span>
<span><span class="block font-display text-2xl font-bold uppercase"><%= title %></span><span class="text-teal/70"><%= place %></span></span>
<span class="text-sm font-bold" data-seats><%= seats - taken %> seats left</span>
<button type="button" data-rsvp-button data-left="<%= seats - taken %>" aria-pressed="false" class="w-fit border-2 border-teal px-5 py-2 font-display text-sm font-bold uppercase tracking-wider transition hover:bg-teal hover:text-white aria-pressed:bg-teal aria-pressed:text-white">RSVP</button>
</li>
<% end %>
</ul>
</div>
</section>
<!-- block:end events -->
<% notes = [["“The only newsletter I read before my coffee is finished.”", "Dana, bakery owner", "md:col-span-2 bg-teal text-white"], ["“Raised my rates 40% after one issue.”", "Leah, designer", "bg-mustard"], ["“Practical, funny, never preachy.”", "Ama, agency founder", "bg-salmon"], ["“Their first-hire piece saved me a bad decision.”", "Yuki, e-commerce", "bg-white"], ["“The events are worth the trip alone.”", "Mei, studio founder", "bg-salmon-soft"], ["“Feels like advice from a friend who has done it.”", "Rosa, consultant", "md:col-span-3 bg-cream border-2 border-teal"]] %>
<!-- block:start reviews -->
<section class="mx-auto max-w-6xl px-5 py-20 md:px-10 md:py-28">
<h2 class="font-display text-5xl font-extrabold uppercase leading-none md:text-6xl">Readers say</h2>
<ul class="mt-12 grid gap-4 md:grid-cols-3">
<% notes.each_with_index do |(quote, name, cls), index| %>
<li class="flex flex-col justify-between gap-6 p-8 <%= cls %>" data-reveal style="--reveal-delay: <%= (index % 3) * 70 %>ms">
<p class="font-display text-2xl font-bold uppercase leading-tight md:text-3xl"><%= quote %></p>
<p class="text-sm font-bold uppercase tracking-wider opacity-75"><%= name %></p>
</li>
<% end %>
</ul>
</section>
<!-- block:end reviews -->
<!-- block:start newsletter -->
<section id="newsletter" class="bg-salmon">
<div class="mx-auto grid max-w-6xl gap-10 px-5 py-20 md:px-10 lg:grid-cols-[1fr_1.2fr] lg:items-center">
<h2 class="font-display text-5xl font-extrabold uppercase leading-[.9] md:text-7xl">One email. Sundays. Free.</h2>
<form data-newsletter novalidate>
<fieldset>
<legend class="font-display text-sm font-bold uppercase tracking-wider">I want more on</legend>
<div class="mt-3 flex flex-wrap gap-2">
<% ["Money", "Pricing", "Leadership", "Fundraising", "Wellbeing"].each_with_index do |topic, index| %>
<label class="cursor-pointer"><input type="checkbox" name="topics" value="<%= topic %>" class="peer sr-only" <%= 'checked' if index < 2 %>><span class="block border-2 border-teal px-4 py-2 font-display text-sm font-bold uppercase tracking-wider transition hover:bg-salmon-soft peer-checked:bg-teal peer-checked:text-white peer-focus-visible:ring-2 peer-focus-visible:ring-white"><%= topic %></span></label>
<% end %>
</div>
</fieldset>
<div class="mt-6 flex flex-col gap-2 sm:flex-row">
<label for="news-email" class="sr-only">Email address</label>
<input id="news-email" name="email" type="email" required autocomplete="email" placeholder="you@company.com" class="min-w-0 flex-1 border-2 border-teal bg-cream px-5 py-4 placeholder:text-teal/40 focus:outline-none focus:ring-4 focus:ring-white">
<button type="submit" class="bg-teal px-7 py-4 font-display text-sm font-bold uppercase tracking-wider text-white transition hover:bg-white hover:text-teal">Subscribe</button>
</div>
<p class="mt-3 min-h-[1.5rem] font-medium" data-form-status role="status"></p>
</form>
</div>
</section>
<!-- block:end newsletter -->
<!-- block:start footer -->
<footer class="overflow-hidden bg-teal text-white">
<div class="mx-auto max-w-6xl px-5 pt-16 md:px-10">
<div class="flex flex-wrap justify-between gap-8">
<ul class="flex flex-wrap gap-6 font-display text-sm font-bold uppercase tracking-wider">
<% nav.drop(1).each do |slug, label| %><li><a href="<%= slug %>.html" class="hover:text-salmon"><%= label %></a></li><% end %>
<li><a href="first-hire.html" class="hover:text-salmon">Latest story</a></li>
</ul>
<div class="flex gap-4">
<% [["instagram", "Instagram"], ["linkedin-in", "LinkedIn"], ["spotify", "Podcast"]].each do |(icon, label)| %>
<a href="contact.html" aria-label="<%= label %>" class="grid h-10 w-10 place-items-center border-2 border-white transition hover:border-salmon hover:bg-salmon"><i class="fa-brands fa-<%= icon %>" aria-hidden="true"></i></a>
<% end %>
</div>
</div>
<p class="mt-12 select-none font-display text-[19vw] font-extrabold uppercase leading-[.8] tracking-tight text-salmon lg:text-[11rem]" aria-hidden="true">Ascendia</p>
<p class="flex flex-wrap justify-between gap-4 border-t border-white/20 py-6 text-sm text-white/60"><span>© 2026 Ascendia Media</span><span>Independent, reader-funded, ad-free.</span></p>
</div>
</footer>
<!-- block:end footer -->
<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-latest]').forEach((root) => {
const tabs = [...root.querySelectorAll('[role="tab"]')];
const items = [...root.querySelectorAll('[data-post-topic]')];
const select = (tab, focus) => {
tabs.forEach((other) => { const on = other === tab; other.setAttribute('aria-selected', String(on)); other.tabIndex = on ? 0 : -1; });
let shown = 0;
items.forEach((item) => { item.hidden = tab.dataset.topic !== 'all' && item.dataset.postTopic !== tab.dataset.topic; if (!item.hidden) shown += 1; });
root.querySelector('[data-post-count]').textContent = shown;
if (focus) tab.focus();
};
tabs.forEach((tab, i) => {
tab.addEventListener('click', () => select(tab));
tab.addEventListener('keydown', (event) => {
if (event.key === 'ArrowRight') select(tabs[(i + 1) % tabs.length], true);
if (event.key === 'ArrowLeft') select(tabs[(i - 1 + tabs.length) % tabs.length], true);
});
});
});
document.querySelectorAll('[data-rate]').forEach((root) => {
const inputs = Object.fromEntries([...root.querySelectorAll('[data-rate-input]')].map((input) => [input.dataset.rateInput, input]));
const money = (value) => `$${Math.round(value).toLocaleString('en-US')}`;
const update = () => {
Object.values(inputs).forEach((input) => {
const value = Number(input.value);
root.querySelector(`[data-rate-out="${input.dataset.rateInput}"]`).textContent = input.dataset.prefix ? money(value) : value;
});
const days = Number(inputs.weeks.value) * Number(inputs.days.value);
const revenue = Number(inputs.income.value) / 0.75 + Number(inputs.costs.value);
root.querySelector('[data-rate-days]').textContent = days;
root.querySelector('[data-rate-revenue]').textContent = money(revenue);
root.querySelector('[data-rate-day]').textContent = money(revenue / days);
};
Object.values(inputs).forEach((input) => input.addEventListener('input', update));
update();
});
document.querySelectorAll('[data-writers]').forEach((root) => {
const track = root.querySelector('[data-writers-track]');
const chips = [...root.querySelectorAll('[data-beat]')];
const step = () => (track.firstElementChild?.getBoundingClientRect().width || 256) + 20;
const behavior = () => (reducedMotion ? 'auto' : 'smooth');
root.querySelector('[data-writers-prev]').addEventListener('click', () => track.scrollBy({ left: -step(), behavior: behavior() }));
root.querySelector('[data-writers-next]').addEventListener('click', () => track.scrollBy({ left: step(), behavior: behavior() }));
chips.forEach((chip) => chip.addEventListener('click', () => {
chips.forEach((other) => other.setAttribute('aria-pressed', String(other === chip)));
root.querySelectorAll('[data-writer-beat]').forEach((item) => { item.hidden = chip.dataset.beat !== 'all' && item.dataset.writerBeat !== chip.dataset.beat; });
track.scrollTo({ left: 0 });
}));
});
document.querySelectorAll('[data-rsvp]').forEach((root) => {
root.querySelectorAll('[data-rsvp-button]').forEach((button) => button.addEventListener('click', () => {
const going = button.getAttribute('aria-pressed') !== 'true';
button.setAttribute('aria-pressed', String(going));
button.textContent = going ? 'Going ✓' : 'RSVP';
button.closest('li').querySelector('[data-seats]').textContent = `${Number(button.dataset.left) - (going ? 1 : 0)} seats left`;
}));
});
document.querySelectorAll('[data-newsletter]').forEach((form) => {
const status = form.querySelector('[data-form-status]');
form.addEventListener('submit', (event) => {
event.preventDefault();
const email = form.querySelector('[name="email"]');
if (!email.value.trim() || !email.checkValidity()) { status.textContent = 'Please enter a valid email address.'; email.focus(); return; }
const topics = new FormData(form).getAll('topics');
status.textContent = `You're in. First issue this Sunday${topics.length ? `, with extra ${topics.join(' and ').toLowerCase()}` : ''}.`;
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>
Unlimited access to all templates
Unlock our entire library of premium UI kits, dashboards, and landing pages. Renews automatically, cancel anytime.
$20
/ 3 months
or $60 / year
Get Unlimited Access