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

Open block
Show 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.