Grouped platform 01
A mark, line and round social icons beside a newsletter field, then a platform column split into three sub-groups and three more link columns, then the copyright and a faded mark and wordmark across the full width. Always dark.
Last updated
---
/**
* Grouped platform 01: A mark, line and round social icons beside a newsletter field, then a platform column split into three sub-groups and three more link columns, then the copyright and a faded mark and wordmark across the full width. Always dark.
*
* @block footers
* @order 55
*/
import { ArrowRight } from '@lucide/astro'
import Section from '@/components/apogee/Section/Section.astro'
import Button from '@/components/apogee/Button/Button.astro'
import Input from '@/components/apogee/Input/Input.astro'
import Link from '@/components/apogee/Link/Link.astro'
import { plain } from '@/components/apogee/sections/_shared/spine.ts'
import { socialIcons, type FooterContent, type FooterColumn } from '@/components/apogee/sections/_shared/footers.ts'
/** @composable */
interface Props extends FooterContent {
/** The registered company name under the line. @category content */
company?: string | null
/** The sub-groups of the wide Platform column. @shape { heading: string, links: { label: string, href: string }[] }[] @category content */
platform?: FooterColumn[] | null
}
const {
brand = 'lineworks',
tagline = 'Voice infrastructure built for the AI era.',
newsletterHeading = 'Stay up to date with our newsletter',
submitLabel = 'Submit',
columns = [
{ heading: 'Resources', links: [{ label: 'About us', href: '#' }, { label: 'Documentation', href: '#' }, { label: 'Blog', href: '#' }, { label: 'Newsroom', href: '#' }, { label: 'Newsletter', href: '#' }, { label: 'Questions', href: '#' }] },
{ heading: 'Developers', links: [{ label: 'API reference', href: '#' }, { label: 'API specification', href: '#' }, { label: 'Developer console', href: '#' }, { label: 'Integrations', href: '#' }, { label: 'Status', href: '#' }, { label: 'SDKs and examples', href: '#' }, { label: 'Agent skills', href: '#' }] },
{ heading: 'Useful links', links: [{ label: 'Pricing', href: '#' }, { label: 'Contact us', href: '#' }, { label: 'Partner programme', href: '#' }, { label: 'Careers', href: '#' }] },
],
copyright = '© 2026 Lineworks Ltd. All rights reserved.',
legal = [{ label: 'Privacy policy', href: '#' }, { label: 'Terms of use', href: '#' }, { label: 'Legal', href: '#' }],
social = [
{ label: 'Code', href: '#', icon: 'code' },
{ label: 'Videos', href: '#', icon: 'video' },
{ label: 'Community', href: '#', icon: 'users' },
{ label: 'Updates', href: '#', icon: 'at-sign' },
],
company = 'Lineworks Ltd',
platform = [
{ heading: 'Voice', links: [{ label: 'Overview', href: '#' }, { label: 'Phone trunking', href: '#' }, { label: 'Global coverage', href: '#' }, { label: 'Voice API', href: '#' }, { label: 'Web calling', href: '#' }] },
{ heading: 'Messaging', links: [{ label: 'Overview', href: '#' }, { label: 'Calls', href: '#' }, { label: 'Messages', href: '#' }] },
{ heading: 'Compliance and trust', links: [{ label: 'Security', href: '#' }, { label: 'Privacy policy', href: '#' }, { label: 'Terms of use', href: '#' }, { label: 'Acceptable use', href: '#' }, { label: 'Cookie policy', href: '#' }, { label: 'Cookie preferences', href: '#' }] },
],
} = Astro.props
// The mark: a quarter disc and a wedge, drawn once.
const mark = 'M0 0h60a60 60 0 0 1-60 60ZM70 60a60 60 0 0 1 60-60v25a35 35 0 0 0-35 35Z'
---
<Section as="footer" spacing="large" theme="dark" class="overflow-hidden">
<div class="grid gap-10 lg:grid-cols-2">
<div class="flex flex-col gap-3">
<svg viewBox="0 0 130 60" class="w-14 text-primary" aria-hidden="true"><path d={mark} fill="currentColor" /></svg>
{tagline && <p class="mt-4 text-lg">{plain(tagline)}</p>}
{company && <p class="text-sm text-neutral-faded">{company}</p>}
{social && <ul class="mt-4 flex list-none gap-3 p-0">{social.map((s) => { const Icon = socialIcons[s.icon]; return <li><Link href={s.href} color="inherit" underline="none" aria-label={s.label} class="grid size-8 place-items-center rounded-full! bg-(--foreground-neutral) text-(--background-page)!"><Icon class="size-4" aria-hidden="true" /></Link></li> })}</ul>}
</div>
<form class="flex flex-col gap-3 lg:justify-self-end" onsubmit="return false">
<label for="grouped-platform-email" class:list={['text-sm text-neutral-faded', !newsletterHeading && 'sr-only']}>{newsletterHeading ?? 'Email'}</label>
<div class="flex gap-2 rounded-lg border border-neutral-faded bg-neutral-faded p-1.5 lg:w-md">
<Input id="grouped-platform-email" type="email" name="email" placeholder="Your email" autocomplete="email" variant="ghost" class="flex-1" />
<Button type="submit">{submitLabel}<ArrowRight slot="end" class="rtl:-scale-x-100" aria-hidden="true" /></Button>
</div>
</form>
</div>
<div class="mt-20 grid gap-10 lg:grid-cols-[3fr_1fr_1fr_1fr]">
{platform && <div>
<p class="text-xs font-semibold uppercase tracking-[0.2em] text-neutral-faded">Platform</p>
<div class="mt-8 grid grid-cols-2 gap-8 sm:grid-cols-3">
{platform.map((g) => <nav aria-label={g.heading}><p class="text-sm text-neutral-faded">{g.heading}</p><ul class="mt-3 flex list-none flex-col gap-2.5 p-0">{g.links.map((l) => <li><Link href={l.href} color="neutral" underline="hover">{l.label}</Link></li>)}</ul></nav>)}
</div>
</div>}
{columns.map((c) => (
<nav aria-label={c.heading}>
<p class="text-xs font-semibold uppercase tracking-[0.2em] text-neutral-faded">{c.heading}</p>
<ul class="mt-8 flex list-none flex-col gap-2.5 p-0">{c.links.map((l) => <li><Link href={l.href} color="neutral" underline="hover">{l.label}</Link></li>)}</ul>
</nav>
))}
</div>
<div class="mt-20 flex flex-col items-center">
{copyright && <p class="text-sm text-neutral-faded">{copyright}</p>}
{legal && <ul class="mt-4 flex list-none gap-6 p-0 text-sm">{legal.map((l) => <li><Link href={l.href} color="neutral" underline="hover" class="text-neutral-faded">{l.label}</Link></li>)}</ul>}
</div>
<div class="@container mt-12">
<p class="flex items-center gap-[0.2em] whitespace-nowrap text-[16.8cqi] font-bold leading-none tracking-tight text-neutral-faded opacity-30 mask-b-from-20%" aria-hidden="true"><svg viewBox="0 0 130 60" class="h-[0.7em] shrink-0"><path d={mark} fill="currentColor" /></svg>{brand}</p>
</div>
</Section>
Show codeHide code
---
/**
* Grouped platform 01: A mark, line and round social icons beside a newsletter field, then a platform column split into three sub-groups and three more link columns, then the copyright and a faded mark and wordmark across the full width. Always dark.
*
* @block footers
* @order 55
*/
import { ArrowRight } from '@lucide/astro'
import Section from '@/components/apogee/Section/Section.astro'
import Button from '@/components/apogee/Button/Button.astro'
import Input from '@/components/apogee/Input/Input.astro'
import Link from '@/components/apogee/Link/Link.astro'
import { plain } from '@/components/apogee/sections/_shared/spine.ts'
import { socialIcons, type FooterContent, type FooterColumn } from '@/components/apogee/sections/_shared/footers.ts'
/** @composable */
interface Props extends FooterContent {
/** The registered company name under the line. @category content */
company?: string | null
/** The sub-groups of the wide Platform column. @shape { heading: string, links: { label: string, href: string }[] }[] @category content */
platform?: FooterColumn[] | null
}
const {
brand = 'lineworks',
tagline = 'Voice infrastructure built for the AI era.',
newsletterHeading = 'Stay up to date with our newsletter',
submitLabel = 'Submit',
columns = [
{ heading: 'Resources', links: [{ label: 'About us', href: '#' }, { label: 'Documentation', href: '#' }, { label: 'Blog', href: '#' }, { label: 'Newsroom', href: '#' }, { label: 'Newsletter', href: '#' }, { label: 'Questions', href: '#' }] },
{ heading: 'Developers', links: [{ label: 'API reference', href: '#' }, { label: 'API specification', href: '#' }, { label: 'Developer console', href: '#' }, { label: 'Integrations', href: '#' }, { label: 'Status', href: '#' }, { label: 'SDKs and examples', href: '#' }, { label: 'Agent skills', href: '#' }] },
{ heading: 'Useful links', links: [{ label: 'Pricing', href: '#' }, { label: 'Contact us', href: '#' }, { label: 'Partner programme', href: '#' }, { label: 'Careers', href: '#' }] },
],
copyright = '© 2026 Lineworks Ltd. All rights reserved.',
legal = [{ label: 'Privacy policy', href: '#' }, { label: 'Terms of use', href: '#' }, { label: 'Legal', href: '#' }],
social = [
{ label: 'Code', href: '#', icon: 'code' },
{ label: 'Videos', href: '#', icon: 'video' },
{ label: 'Community', href: '#', icon: 'users' },
{ label: 'Updates', href: '#', icon: 'at-sign' },
],
company = 'Lineworks Ltd',
platform = [
{ heading: 'Voice', links: [{ label: 'Overview', href: '#' }, { label: 'Phone trunking', href: '#' }, { label: 'Global coverage', href: '#' }, { label: 'Voice API', href: '#' }, { label: 'Web calling', href: '#' }] },
{ heading: 'Messaging', links: [{ label: 'Overview', href: '#' }, { label: 'Calls', href: '#' }, { label: 'Messages', href: '#' }] },
{ heading: 'Compliance and trust', links: [{ label: 'Security', href: '#' }, { label: 'Privacy policy', href: '#' }, { label: 'Terms of use', href: '#' }, { label: 'Acceptable use', href: '#' }, { label: 'Cookie policy', href: '#' }, { label: 'Cookie preferences', href: '#' }] },
],
} = Astro.props
// The mark: a quarter disc and a wedge, drawn once.
const mark = 'M0 0h60a60 60 0 0 1-60 60ZM70 60a60 60 0 0 1 60-60v25a35 35 0 0 0-35 35Z'
---
<Section as="footer" spacing="large" theme="dark" class="overflow-hidden">
<div class="grid gap-10 lg:grid-cols-2">
<div class="flex flex-col gap-3">
<svg viewBox="0 0 130 60" class="w-14 text-primary" aria-hidden="true"><path d={mark} fill="currentColor" /></svg>
{tagline && <p class="mt-4 text-lg">{plain(tagline)}</p>}
{company && <p class="text-sm text-neutral-faded">{company}</p>}
{social && <ul class="mt-4 flex list-none gap-3 p-0">{social.map((s) => { const Icon = socialIcons[s.icon]; return <li><Link href={s.href} color="inherit" underline="none" aria-label={s.label} class="grid size-8 place-items-center rounded-full! bg-(--foreground-neutral) text-(--background-page)!"><Icon class="size-4" aria-hidden="true" /></Link></li> })}</ul>}
</div>
<form class="flex flex-col gap-3 lg:justify-self-end" onsubmit="return false">
<label for="grouped-platform-email" class:list={['text-sm text-neutral-faded', !newsletterHeading && 'sr-only']}>{newsletterHeading ?? 'Email'}</label>
<div class="flex gap-2 rounded-lg border border-neutral-faded bg-neutral-faded p-1.5 lg:w-md">
<Input id="grouped-platform-email" type="email" name="email" placeholder="Your email" autocomplete="email" variant="ghost" class="flex-1" />
<Button type="submit">{submitLabel}<ArrowRight slot="end" class="rtl:-scale-x-100" aria-hidden="true" /></Button>
</div>
</form>
</div>
<div class="mt-20 grid gap-10 lg:grid-cols-[3fr_1fr_1fr_1fr]">
{platform && <div>
<p class="text-xs font-semibold uppercase tracking-[0.2em] text-neutral-faded">Platform</p>
<div class="mt-8 grid grid-cols-2 gap-8 sm:grid-cols-3">
{platform.map((g) => <nav aria-label={g.heading}><p class="text-sm text-neutral-faded">{g.heading}</p><ul class="mt-3 flex list-none flex-col gap-2.5 p-0">{g.links.map((l) => <li><Link href={l.href} color="neutral" underline="hover">{l.label}</Link></li>)}</ul></nav>)}
</div>
</div>}
{columns.map((c) => (
<nav aria-label={c.heading}>
<p class="text-xs font-semibold uppercase tracking-[0.2em] text-neutral-faded">{c.heading}</p>
<ul class="mt-8 flex list-none flex-col gap-2.5 p-0">{c.links.map((l) => <li><Link href={l.href} color="neutral" underline="hover">{l.label}</Link></li>)}</ul>
</nav>
))}
</div>
<div class="mt-20 flex flex-col items-center">
{copyright && <p class="text-sm text-neutral-faded">{copyright}</p>}
{legal && <ul class="mt-4 flex list-none gap-6 p-0 text-sm">{legal.map((l) => <li><Link href={l.href} color="neutral" underline="hover" class="text-neutral-faded">{l.label}</Link></li>)}</ul>}
</div>
<div class="@container mt-12">
<p class="flex items-center gap-[0.2em] whitespace-nowrap text-[16.8cqi] font-bold leading-none tracking-tight text-neutral-faded opacity-30 mask-b-from-20%" aria-hidden="true"><svg viewBox="0 0 130 60" class="h-[0.7em] shrink-0"><path d={mark} fill="currentColor" /></svg>{brand}</p>
</div>
</Section>Add
npx @ava-ui/apogee add footers/grouped-platform-01
Every piece of copy is a prop, with the sample copy as its default. Pass yours, or null to hide a part; see copy is props.