Footer
Accordion footer for mobile
Link columns that collapse on small screens
Link columns that collapse on small screens
Build a responsive footer with Tailwind CSS whose link groups collapse on small screens and expand on medium and up. Each group is a details element with a summary showing the group heading and a chevron that rotates when open; from md upward, force the details open with an attribute and hide the chevron so the groups read as ordinary columns. Include four groups — Product, Company, Developers and Legal — with four to six links each. Above the groups, a brand row with a wordmark, a one-line description and three social glyph buttons. Below them, a bottom bar with the copyright on the left and, on the right, a small "Back to top" link with an up arrow. Then add JavaScript that opens every group when the viewport crosses the md breakpoint and closes them again below it, so switching orientation does not leave the footer in a strange state. <footer class="border-t border-neutral-200 bg-white">
<div class="mx-auto max-w-6xl px-6 py-12">
<div class="flex flex-col gap-5 pb-8 sm:flex-row sm:items-center sm:justify-between">
<div>
<p class="text-lg font-semibold tracking-tight text-neutral-900">Halden</p>
<p class="mt-1 text-sm text-neutral-500">Infrastructure for teams who would rather not run their own.</p>
</div>
<div class="flex gap-2">
<a href="#" class="grid h-9 w-9 place-items-center rounded-full border border-neutral-200 text-neutral-500 transition hover:text-neutral-900"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="inline-block h-[1em] w-[1em] shrink-0 align-[-0.125em] transition-transform"><path d="M15 21v-3.5c0-1 .1-1.4-.5-2 2.8-.3 5.7-1.4 5.7-6.2 0-1.4-.5-2.5-1.3-3.4.1-.3.6-1.7-.1-3.4 0 0-1.1-.3-3.6 1.3a12.4 12.4 0 0 0-6.5 0C6.2 2.2 5.1 2.5 5.1 2.5c-.7 1.7-.2 3.1-.1 3.4a5 5 0 0 0-1.3 3.4c0 4.8 2.9 5.9 5.7 6.2-.5.5-.6 1-.5 2V21"/><path d="M9 18c-2.7.8-2.7-1.3-3.8-1.6"/></svg></a>
<a href="#" class="grid h-9 w-9 place-items-center rounded-full border border-neutral-200 text-neutral-500 transition hover:text-neutral-900"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="inline-block h-[1em] w-[1em] shrink-0 align-[-0.125em] transition-transform"><path d="M5 4l14 16M19 4 5 20"/></svg></a>
<a href="#" class="grid h-9 w-9 place-items-center rounded-full border border-neutral-200 text-neutral-500 transition hover:text-neutral-900"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="inline-block h-[1em] w-[1em] shrink-0 align-[-0.125em] transition-transform"><path d="M7 9v11M7 5v.01M11 20v-6.5a4.5 4.5 0 0 1 9 0V20M11 9v11"/></svg></a>
</div>
</div>
<div class="grid border-t border-neutral-200 md:grid-cols-4 md:gap-8 md:border-0">
<details class="footer-group group border-b border-neutral-200 md:border-0">
<summary class="flex cursor-pointer list-none items-center justify-between py-4 md:cursor-default md:py-0">
<span class="text-xs font-semibold uppercase tracking-widest text-neutral-400">Product</span>
<span class="text-neutral-400 transition-transform group-open:rotate-180 md:hidden"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="inline-block h-[1em] w-[1em] shrink-0 align-[-0.125em] transition-transform"><path d="m7 10 5 5 5-5"/></svg></span>
</summary>
<ul class="space-y-2.5 pb-5 text-sm text-neutral-500 md:mt-4 md:pb-0">
<li><a href="#" class="transition hover:text-neutral-900">Overview</a></li>
<li><a href="#" class="transition hover:text-neutral-900">Workflows</a></li>
<li><a href="#" class="transition hover:text-neutral-900">Integrations</a></li>
<li><a href="#" class="transition hover:text-neutral-900">Pricing</a></li>
<li><a href="#" class="transition hover:text-neutral-900">Changelog</a></li>
</ul>
</details>
<details class="footer-group group border-b border-neutral-200 md:border-0">
<summary class="flex cursor-pointer list-none items-center justify-between py-4 md:cursor-default md:py-0">
<span class="text-xs font-semibold uppercase tracking-widest text-neutral-400">Company</span>
<span class="text-neutral-400 transition-transform group-open:rotate-180 md:hidden"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="inline-block h-[1em] w-[1em] shrink-0 align-[-0.125em] transition-transform"><path d="m7 10 5 5 5-5"/></svg></span>
</summary>
<ul class="space-y-2.5 pb-5 text-sm text-neutral-500 md:mt-4 md:pb-0">
<li><a href="#" class="transition hover:text-neutral-900">About</a></li>
<li><a href="#" class="transition hover:text-neutral-900">Careers</a></li>
<li><a href="#" class="transition hover:text-neutral-900">Journal</a></li>
<li><a href="#" class="transition hover:text-neutral-900">Customers</a></li>
</ul>
</details>
<details class="footer-group group border-b border-neutral-200 md:border-0">
<summary class="flex cursor-pointer list-none items-center justify-between py-4 md:cursor-default md:py-0">
<span class="text-xs font-semibold uppercase tracking-widest text-neutral-400">Developers</span>
<span class="text-neutral-400 transition-transform group-open:rotate-180 md:hidden"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="inline-block h-[1em] w-[1em] shrink-0 align-[-0.125em] transition-transform"><path d="m7 10 5 5 5-5"/></svg></span>
</summary>
<ul class="space-y-2.5 pb-5 text-sm text-neutral-500 md:mt-4 md:pb-0">
<li><a href="#" class="transition hover:text-neutral-900">Documentation</a></li>
<li><a href="#" class="transition hover:text-neutral-900">API reference</a></li>
<li><a href="#" class="transition hover:text-neutral-900">SDKs</a></li>
<li><a href="#" class="transition hover:text-neutral-900">Status</a></li>
<li><a href="#" class="transition hover:text-neutral-900">Open source</a></li>
</ul>
</details>
<details class="footer-group group border-b border-neutral-200 md:border-0">
<summary class="flex cursor-pointer list-none items-center justify-between py-4 md:cursor-default md:py-0">
<span class="text-xs font-semibold uppercase tracking-widest text-neutral-400">Legal</span>
<span class="text-neutral-400 transition-transform group-open:rotate-180 md:hidden"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="inline-block h-[1em] w-[1em] shrink-0 align-[-0.125em] transition-transform"><path d="m7 10 5 5 5-5"/></svg></span>
</summary>
<ul class="space-y-2.5 pb-5 text-sm text-neutral-500 md:mt-4 md:pb-0">
<li><a href="#" class="transition hover:text-neutral-900">Terms</a></li>
<li><a href="#" class="transition hover:text-neutral-900">Privacy</a></li>
<li><a href="#" class="transition hover:text-neutral-900">DPA</a></li>
<li><a href="#" class="transition hover:text-neutral-900">Sub-processors</a></li>
</ul>
</details>
</div>
<div class="mt-10 flex items-center justify-between border-t border-neutral-200 pt-6 text-xs text-neutral-400">
<p>© 2026 Halden Systems BV</p>
<a href="#" class="flex items-center gap-1.5 transition hover:text-neutral-700"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="inline-block h-[1em] w-[1em] shrink-0 align-[-0.125em] transition-transform"><path d="M12 16V4m0 0L7 9m5-5 5 5"/><path d="M5 14v5a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-5"/></svg> Back to top</a>
</div>
</div>
</footer> var groups = Array.prototype.slice.call(document.querySelectorAll(".footer-group"));
var wide = window.matchMedia("(min-width: 768px)");
function sync(matches) {
// Above md the groups are columns, so they must all be open; below, closed.
groups.forEach(function (group) {
group.open = matches;
});
}
sync(wide.matches);
if (wide.addEventListener) {
wide.addEventListener("change", function (e) {
sync(e.matches);
});
} else {
wide.addListener(function (e) {
sync(e.matches);
});
}