Footer Mega Section
Full site footer with brand, newsletter field, link columns, status pill, social icons and legal row.
footernavigationnewslettersocial
Live Preview
Usage
Copy and paste this code into your Astro page:
astro
---
import { FooterMegaSection } from 'astro-component-kit/sections';
---
<FooterMegaSection
brand="Acme"
columns={[{ title: 'Product', links: [{ label: 'Pricing', href: '/pricing' }] }]}
socials={[{ label: 'GitHub', href: 'https://github.com', icon: 'github' }]}
/> ---
import { FooterMegaSection } from 'astro-component-kit/sections';
---
<FooterMegaSection
brand="Acme"
columns={[{ title: 'Product', links: [{ label: 'Pricing', href: '/pricing' }] }]}
socials={[{ label: 'GitHub', href: 'https://github.com', icon: 'github' }]}
/> Manual Installation
If you are not using the npm package, create a new file and paste the following code: src/components/sections/FooterMegaSection.astro:
astro
---
/**
* FooterMegaSection — Full site footer: brand blurb, newsletter field, four link
* columns, social icons, system-status pill and legal row.
*
* @param {string} brand - Brand name.
* @param {string} tagline - Short brand description.
* @param {Array<{title: string, links: Array<{label: string, href: string}>}>} columns - Link columns.
* @param {Array<{label: string, href: string, icon: 'github'|'x'|'discord'|'youtube'}>} socials - Social links.
* @param {string} status - System status text (empty to hide).
* @param {string} copyright - Copyright line.
* @param {string} variant - Footer style (default|bordered|minimal).
* @param {string} size - Vertical rhythm (sm|md|lg).
* @param {string} className - Extra CSS class.
*/
interface Column { title: string; links: Array<{ label: string; href: string }> }
interface Social { label: string; href: string; icon: 'github' | 'x' | 'discord' | 'youtube' }
interface Props {
brand?: string;
tagline?: string;
columns?: Column[];
socials?: Social[];
status?: string;
copyright?: string;
variant?: 'default' | 'bordered' | 'minimal';
size?: 'sm' | 'md' | 'lg';
className?: string;
}
const {
brand = 'Astro Kit',
tagline = 'Accessible, zero-dependency components for building beautiful Astro sites.',
columns = [
{ title: 'Product', links: [{ label: 'Components', href: '#' }, { label: 'Sections', href: '#' }, { label: 'Charts', href: '#' }, { label: 'Changelog', href: '#' }] },
{ title: 'Resources', links: [{ label: 'Documentation', href: '#' }, { label: 'Installation', href: '#' }, { label: 'Notes', href: '#' }, { label: 'Showcase', href: '#' }] },
{ title: 'Company', links: [{ label: 'About', href: '#' }, { label: 'Careers', href: '#' }, { label: 'Contact', href: '#' }] },
{ title: 'Legal', links: [{ label: 'Privacy', href: '#' }, { label: 'Terms', href: '#' }, { label: 'License (MIT)', href: '#' }] },
],
socials = [
{ label: 'GitHub', href: '#', icon: 'github' },
{ label: 'X (Twitter)', href: '#', icon: 'x' },
{ label: 'Discord', href: '#', icon: 'discord' },
{ label: 'YouTube', href: '#', icon: 'youtube' },
],
status = 'All systems operational',
copyright = `© ${new Date().getFullYear()} Astro Kit. All rights reserved.`,
variant = 'default',
size = 'md',
className = '',
} = Astro.props;
const icons = {
github: '<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.9a3.4 3.4 0 0 0-.9-2.6c3.1-.4 6.4-1.5 6.4-7A5.4 5.4 0 0 0 20 4.8 5 5 0 0 0 19.9 1S18.7.6 16 2.5a13.4 13.4 0 0 0-7 0C6.3.6 5.1 1 5.1 1A5 5 0 0 0 5 4.8a5.4 5.4 0 0 0-1.5 3.7c0 5.4 3.3 6.6 6.4 7a3.4 3.4 0 0 0-.9 2.6V22"/>',
x: '<path d="M4 4l16 16M20 4 4 20"/>',
discord: '<path d="M8 12h.01M16 12h.01"/><path d="M7.5 7.5c3-1 6-1 9 0M7 16.5c3 1 7 1 10 0"/><path d="M15.5 17l1 3c3-1 5-3 5.5-4.5.5-4-1-8-3-11-1.5-.8-3-1.2-4.5-1.5L14 4.5M8.5 17l-1 3c-3-1-5-3-5.5-4.5-.5-4 1-8 3-11 1.5-.8 3-1.2 4.5-1.5L10 4.5"/>',
youtube: '<path d="M2.5 17a24 24 0 0 1 0-10 2 2 0 0 1 1.4-1.4 49.6 49.6 0 0 1 16.2 0A2 2 0 0 1 21.5 7a24 24 0 0 1 0 10 2 2 0 0 1-1.4 1.4 49.6 49.6 0 0 1-16.2 0A2 2 0 0 1 2.5 17"/><path d="m10 15 5-3-5-3z"/>',
};
const id = `fms-${Math.random().toString(36).slice(2, 8)}`;
---
<div class="cq-footer-mega-section-cq">
<footer class={`fms fms--${variant} fms--${size} ${className}`} aria-labelledby={id}>
<h2 id={id} class="fms__sr">Site footer</h2>
<div class="fms__inner">
<div class="fms__top">
<div class="fms__brand">
<a class="fms__logo" href="/">
<svg width="28" height="28" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2 3 20h4l5-10 5 10h4z" fill="var(--c-primary-light, #d4a017)" /></svg>
{brand}
</a>
<p class="fms__tagline">{tagline}</p>
<form class="fms__news" action="#" method="post">
<label for={`${id}-email`} class="fms__news-label">Get product updates</label>
<div class="fms__news-field">
<input id={`${id}-email`} type="email" name="email" placeholder="you@company.com" autocomplete="email" required />
<button type="submit" aria-label="Subscribe to product updates">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6" /></svg>
</button>
</div>
</form>
</div>
<nav class="fms__cols" aria-label="Footer">
{columns.map((c) => (
<div class="fms__col">
<h3 class="fms__col-title">{c.title}</h3>
<ul>
{c.links.map((l) => <li><a href={l.href}>{l.label}</a></li>)}
</ul>
</div>
))}
</nav>
</div>
<div class="fms__bottom">
<p class="fms__copy">{copyright}</p>
{status && (
<a class="fms__status" href="#">
<span class="fms__dot" aria-hidden="true"></span>{status}
</a>
)}
<ul class="fms__socials">
{socials.map((s) => (
<li>
<a href={s.href} aria-label={s.label}>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" set:html={icons[s.icon]}></svg>
</a>
</li>
))}
</ul>
</div>
</div>
</footer>
</div>
<style>
.cq-footer-mega-section-cq { container-type: inline-size; }
.fms {
--fms-py: 4.5rem;
padding: var(--fms-py) var(--sp-6, 1.5rem) 2rem;
background: var(--c-bg-1, #0a0a0a);
border-top: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.06);
}
.fms--sm { --fms-py: 3rem; }
.fms--lg { --fms-py: 6rem; }
.fms--bordered .fms__inner { padding: clamp(1.5rem, 4vw, 3rem); border-radius: var(--r-xl, 28px); border: 1px solid var(--c-border, rgb(var(--ink-rgb, 255 255 255) / 0.08)); background: rgb(var(--ink-rgb, 255 255 255) / 0.015); }
.fms--minimal { background: transparent; border-top: 0; }
.fms--minimal .fms__news { display: none; }
.fms__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.fms__inner { max-width: 1180px; margin: 0 auto; }
.fms__top { display: grid; grid-template-columns: 1.2fr 2fr; gap: clamp(2rem, 6vw, 5rem); padding-bottom: 3rem; border-bottom: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.07); }
.fms__logo { display: inline-flex; align-items: center; gap: 0.6rem; min-height: 44px; font-size: 1.25rem; font-weight: 800; letter-spacing: -0.02em; color: var(--c-text-1, #fff); text-decoration: none; }
.fms__tagline { margin: 0.75rem 0 1.75rem; max-width: 320px; font-size: 0.9375rem; line-height: 1.6; color: var(--c-text-2, #a3a3a3); }
.fms__news-label { display: block; margin-bottom: 0.5rem; font-size: 0.8125rem; font-weight: 600; color: var(--c-text-1, #fff); }
.fms__news-field { display: flex; max-width: 340px; border-radius: var(--r-md, 12px); border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.12); background: rgba(0, 0, 0, 0.3); overflow: hidden; transition: border-color 0.2s ease; }
.fms__news-field:focus-within { border-color: var(--c-primary-light, #d4a017); }
.fms__news-field input { flex: 1; min-width: 0; min-height: 46px; padding: 0 0.9rem; border: 0; background: transparent; color: var(--c-text-1, #fff); font: inherit; font-size: 0.9375rem; }
.fms__news-field input:focus { outline: none; }
.fms__news-field input::placeholder { color: var(--c-text-3, #737373); }
.fms__news-field button { width: 46px; border: 0; background: rgb(var(--ink-rgb, 255 255 255) / 0.06); color: var(--c-text-1, #fff); cursor: pointer; transition: background 0.2s ease; }
.fms__news-field button:hover { background: var(--c-primary-light, #d4a017); color: var(--c-on-primary, #0a0a0a); }
.fms__news-field button:focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: -2px; }
.fms__cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 2rem; }
.fms__col-title { margin: 0 0 1rem; font-size: 0.8125rem; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--c-text-1, #fff); }
.fms__col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.25rem; }
.fms__col a, .fms__copy, .fms__status { font-size: 0.9rem; color: var(--c-text-2, #a3a3a3); text-decoration: none; }
.fms__col a { display: inline-flex; align-items: center; min-height: 36px; transition: color 0.2s ease; }
.fms__col a:hover { color: var(--c-text-1, #fff); }
.fms a:focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: 2px; border-radius: 4px; }
.fms__bottom { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1rem; padding-top: 1.75rem; }
.fms__copy { margin: 0; font-size: 0.8125rem; }
.fms__status { display: inline-flex; align-items: center; gap: 0.5rem; min-height: 36px; padding: 0 0.85rem; border-radius: 999px; border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08); font-size: 0.8125rem; }
.fms__status:hover { color: var(--c-text-1, #fff); }
.fms__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--chart-pos, #34c38f); box-shadow: 0 0 8px var(--chart-pos, #34c38f); }
.fms__socials { list-style: none; margin: 0; padding: 0; display: flex; gap: 0.25rem; }
.fms__socials a { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 10px; color: var(--c-text-2, #a3a3a3); transition: color 0.2s ease, background 0.2s ease; }
.fms__socials a:hover { color: var(--c-text-1, #fff); background: rgb(var(--ink-rgb, 255 255 255) / 0.06); }
@container (max-width: 860px) { .fms__top { grid-template-columns: 1fr; } }
@container (max-width: 560px) { .fms__bottom { flex-direction: column; align-items: flex-start; } }
</style> ---
/**
* FooterMegaSection — Full site footer: brand blurb, newsletter field, four link
* columns, social icons, system-status pill and legal row.
*
* @param {string} brand - Brand name.
* @param {string} tagline - Short brand description.
* @param {Array<{title: string, links: Array<{label: string, href: string}>}>} columns - Link columns.
* @param {Array<{label: string, href: string, icon: 'github'|'x'|'discord'|'youtube'}>} socials - Social links.
* @param {string} status - System status text (empty to hide).
* @param {string} copyright - Copyright line.
* @param {string} variant - Footer style (default|bordered|minimal).
* @param {string} size - Vertical rhythm (sm|md|lg).
* @param {string} className - Extra CSS class.
*/
interface Column { title: string; links: Array<{ label: string; href: string }> }
interface Social { label: string; href: string; icon: 'github' | 'x' | 'discord' | 'youtube' }
interface Props {
brand?: string;
tagline?: string;
columns?: Column[];
socials?: Social[];
status?: string;
copyright?: string;
variant?: 'default' | 'bordered' | 'minimal';
size?: 'sm' | 'md' | 'lg';
className?: string;
}
const {
brand = 'Astro Kit',
tagline = 'Accessible, zero-dependency components for building beautiful Astro sites.',
columns = [
{ title: 'Product', links: [{ label: 'Components', href: '#' }, { label: 'Sections', href: '#' }, { label: 'Charts', href: '#' }, { label: 'Changelog', href: '#' }] },
{ title: 'Resources', links: [{ label: 'Documentation', href: '#' }, { label: 'Installation', href: '#' }, { label: 'Notes', href: '#' }, { label: 'Showcase', href: '#' }] },
{ title: 'Company', links: [{ label: 'About', href: '#' }, { label: 'Careers', href: '#' }, { label: 'Contact', href: '#' }] },
{ title: 'Legal', links: [{ label: 'Privacy', href: '#' }, { label: 'Terms', href: '#' }, { label: 'License (MIT)', href: '#' }] },
],
socials = [
{ label: 'GitHub', href: '#', icon: 'github' },
{ label: 'X (Twitter)', href: '#', icon: 'x' },
{ label: 'Discord', href: '#', icon: 'discord' },
{ label: 'YouTube', href: '#', icon: 'youtube' },
],
status = 'All systems operational',
copyright = `© ${new Date().getFullYear()} Astro Kit. All rights reserved.`,
variant = 'default',
size = 'md',
className = '',
} = Astro.props;
const icons = {
github: '<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.9a3.4 3.4 0 0 0-.9-2.6c3.1-.4 6.4-1.5 6.4-7A5.4 5.4 0 0 0 20 4.8 5 5 0 0 0 19.9 1S18.7.6 16 2.5a13.4 13.4 0 0 0-7 0C6.3.6 5.1 1 5.1 1A5 5 0 0 0 5 4.8a5.4 5.4 0 0 0-1.5 3.7c0 5.4 3.3 6.6 6.4 7a3.4 3.4 0 0 0-.9 2.6V22"/>',
x: '<path d="M4 4l16 16M20 4 4 20"/>',
discord: '<path d="M8 12h.01M16 12h.01"/><path d="M7.5 7.5c3-1 6-1 9 0M7 16.5c3 1 7 1 10 0"/><path d="M15.5 17l1 3c3-1 5-3 5.5-4.5.5-4-1-8-3-11-1.5-.8-3-1.2-4.5-1.5L14 4.5M8.5 17l-1 3c-3-1-5-3-5.5-4.5-.5-4 1-8 3-11 1.5-.8 3-1.2 4.5-1.5L10 4.5"/>',
youtube: '<path d="M2.5 17a24 24 0 0 1 0-10 2 2 0 0 1 1.4-1.4 49.6 49.6 0 0 1 16.2 0A2 2 0 0 1 21.5 7a24 24 0 0 1 0 10 2 2 0 0 1-1.4 1.4 49.6 49.6 0 0 1-16.2 0A2 2 0 0 1 2.5 17"/><path d="m10 15 5-3-5-3z"/>',
};
const id = `fms-${Math.random().toString(36).slice(2, 8)}`;
---
<div class="cq-footer-mega-section-cq">
<footer class={`fms fms--${variant} fms--${size} ${className}`} aria-labelledby={id}>
<h2 id={id} class="fms__sr">Site footer</h2>
<div class="fms__inner">
<div class="fms__top">
<div class="fms__brand">
<a class="fms__logo" href="/">
<svg width="28" height="28" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2 3 20h4l5-10 5 10h4z" fill="var(--c-primary-light, #d4a017)" /></svg>
{brand}
</a>
<p class="fms__tagline">{tagline}</p>
<form class="fms__news" action="#" method="post">
<label for={`${id}-email`} class="fms__news-label">Get product updates</label>
<div class="fms__news-field">
<input id={`${id}-email`} type="email" name="email" placeholder="you@company.com" autocomplete="email" required />
<button type="submit" aria-label="Subscribe to product updates">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6" /></svg>
</button>
</div>
</form>
</div>
<nav class="fms__cols" aria-label="Footer">
{columns.map((c) => (
<div class="fms__col">
<h3 class="fms__col-title">{c.title}</h3>
<ul>
{c.links.map((l) => <li><a href={l.href}>{l.label}</a></li>)}
</ul>
</div>
))}
</nav>
</div>
<div class="fms__bottom">
<p class="fms__copy">{copyright}</p>
{status && (
<a class="fms__status" href="#">
<span class="fms__dot" aria-hidden="true"></span>{status}
</a>
)}
<ul class="fms__socials">
{socials.map((s) => (
<li>
<a href={s.href} aria-label={s.label}>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" set:html={icons[s.icon]}></svg>
</a>
</li>
))}
</ul>
</div>
</div>
</footer>
</div>
<style>
.cq-footer-mega-section-cq { container-type: inline-size; }
.fms {
--fms-py: 4.5rem;
padding: var(--fms-py) var(--sp-6, 1.5rem) 2rem;
background: var(--c-bg-1, #0a0a0a);
border-top: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.06);
}
.fms--sm { --fms-py: 3rem; }
.fms--lg { --fms-py: 6rem; }
.fms--bordered .fms__inner { padding: clamp(1.5rem, 4vw, 3rem); border-radius: var(--r-xl, 28px); border: 1px solid var(--c-border, rgb(var(--ink-rgb, 255 255 255) / 0.08)); background: rgb(var(--ink-rgb, 255 255 255) / 0.015); }
.fms--minimal { background: transparent; border-top: 0; }
.fms--minimal .fms__news { display: none; }
.fms__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.fms__inner { max-width: 1180px; margin: 0 auto; }
.fms__top { display: grid; grid-template-columns: 1.2fr 2fr; gap: clamp(2rem, 6vw, 5rem); padding-bottom: 3rem; border-bottom: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.07); }
.fms__logo { display: inline-flex; align-items: center; gap: 0.6rem; min-height: 44px; font-size: 1.25rem; font-weight: 800; letter-spacing: -0.02em; color: var(--c-text-1, #fff); text-decoration: none; }
.fms__tagline { margin: 0.75rem 0 1.75rem; max-width: 320px; font-size: 0.9375rem; line-height: 1.6; color: var(--c-text-2, #a3a3a3); }
.fms__news-label { display: block; margin-bottom: 0.5rem; font-size: 0.8125rem; font-weight: 600; color: var(--c-text-1, #fff); }
.fms__news-field { display: flex; max-width: 340px; border-radius: var(--r-md, 12px); border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.12); background: rgba(0, 0, 0, 0.3); overflow: hidden; transition: border-color 0.2s ease; }
.fms__news-field:focus-within { border-color: var(--c-primary-light, #d4a017); }
.fms__news-field input { flex: 1; min-width: 0; min-height: 46px; padding: 0 0.9rem; border: 0; background: transparent; color: var(--c-text-1, #fff); font: inherit; font-size: 0.9375rem; }
.fms__news-field input:focus { outline: none; }
.fms__news-field input::placeholder { color: var(--c-text-3, #737373); }
.fms__news-field button { width: 46px; border: 0; background: rgb(var(--ink-rgb, 255 255 255) / 0.06); color: var(--c-text-1, #fff); cursor: pointer; transition: background 0.2s ease; }
.fms__news-field button:hover { background: var(--c-primary-light, #d4a017); color: var(--c-on-primary, #0a0a0a); }
.fms__news-field button:focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: -2px; }
.fms__cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 2rem; }
.fms__col-title { margin: 0 0 1rem; font-size: 0.8125rem; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--c-text-1, #fff); }
.fms__col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.25rem; }
.fms__col a, .fms__copy, .fms__status { font-size: 0.9rem; color: var(--c-text-2, #a3a3a3); text-decoration: none; }
.fms__col a { display: inline-flex; align-items: center; min-height: 36px; transition: color 0.2s ease; }
.fms__col a:hover { color: var(--c-text-1, #fff); }
.fms a:focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: 2px; border-radius: 4px; }
.fms__bottom { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1rem; padding-top: 1.75rem; }
.fms__copy { margin: 0; font-size: 0.8125rem; }
.fms__status { display: inline-flex; align-items: center; gap: 0.5rem; min-height: 36px; padding: 0 0.85rem; border-radius: 999px; border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08); font-size: 0.8125rem; }
.fms__status:hover { color: var(--c-text-1, #fff); }
.fms__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--chart-pos, #34c38f); box-shadow: 0 0 8px var(--chart-pos, #34c38f); }
.fms__socials { list-style: none; margin: 0; padding: 0; display: flex; gap: 0.25rem; }
.fms__socials a { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 10px; color: var(--c-text-2, #a3a3a3); transition: color 0.2s ease, background 0.2s ease; }
.fms__socials a:hover { color: var(--c-text-1, #fff); background: rgb(var(--ink-rgb, 255 255 255) / 0.06); }
@container (max-width: 860px) { .fms__top { grid-template-columns: 1fr; } }
@container (max-width: 560px) { .fms__bottom { flex-direction: column; align-items: flex-start; } }
</style>