Floating four columns slim with footer
A floating bar whose menus open one row of four links over a sign-up strip.
Last updated
---
/**
* Floating four columns slim with footer: A floating bar whose menus open one row of four links over a sign-up strip.
*
* @block header-navigation
* @order 15
*/
import { CirclePlay, DollarSign, Flag, Folder, MessageCircle, MessagesSquare, Orbit, Users } from '@lucide/astro'
import Navbar from '@/components/apogee/Navbar/Navbar.astro'
import NavLink from '@/components/apogee/NavLink/NavLink.astro'
import NavDropdown from '@/components/apogee/NavDropdown/NavDropdown.astro'
import Link from '@/components/apogee/Link/Link.astro'
import Button from '@/components/apogee/Button/Button.astro'
import FeaturedIcon from '@/components/apogee/FeaturedIcon/FeaturedIcon.astro'
import Badge from '@/components/apogee/Badge/Badge.astro'
import type { Action } from '@/components/apogee/sections/_shared/spine.ts'
import type { NavGroup, NavigationContent } from '@/components/apogee/sections/_shared/navigation.ts'
const icons = [Flag, MessageCircle, Users, Folder]
const footerIcons = [DollarSign, CirclePlay, MessagesSquare]
const groups: NavGroup[] = [{ items: [
{ label: 'About us', href: '#', description: 'Our team and what we are working towards.' },
{ label: 'Press', href: '#', description: 'News, press releases and press resources.' },
{ label: 'Careers', href: '#', description: 'We are always looking for talented people. Join our remote team.', badge: "We're hiring" },
{ label: 'Legal', href: '#', description: 'Company details, licensing, terms and privacy.' },
] }]
/** @composable */
interface Props extends NavigationContent {
/** The bar's fill: the page, a grey band or the brand colour. */
background?: 'page' | 'neutral-faded' | 'primary' | undefined
/** `dark` sets the bar in the dark theme on a light page. */
theme?: 'dark' | undefined
/** Bold line in the strip under each menu. @category content */
footerText?: string | null
/** The link after `footerText`. @category content */
footerAction?: Action | null
/** Links at the end of the strip. @category content */
footerLinks?: Action[] | null
}
const {
brand = 'Example',
links = [
{ label: 'Products', groups },
{ label: 'Services', groups },
{ label: 'Pricing', href: '#' },
{ label: 'Resources', groups },
{ label: 'About', href: '#' },
],
primaryAction = { label: 'Sign up', href: '#' },
secondaryAction = { label: 'Log in', href: '#' },
footerText = 'Ready to get started?',
footerAction = { label: 'Sign up for free', href: '#' },
footerLinks = [
{ label: 'Pricing', href: '#' },
{ label: 'Watch demo', href: '#' },
{ label: 'Chat to sales', href: '#' },
],
background,
theme,
} = Astro.props
---
<Navbar floating background={background} data-theme={theme}>
<Link slot="brand" href="/" color="inherit" underline="none" class="inline-flex items-center gap-2 text-lg font-semibold"><FeaturedIcon theme="solid" color="primary" size="small"><Orbit /></FeaturedIcon>{brand}</Link>
{links.map((l) => 'groups' in l ? (
<NavDropdown label={l.label} fullWidth floating>
<div class="p-4 md:p-6">
<ul class="list-none p-0 grid gap-2 md:grid-cols-2 xl:grid-cols-4">
{l.groups.flatMap((g) => g.items).map((i, n) => {
const Icon = icons[n % icons.length]!
return (
<li>
<Link href={i.href} color="inherit" underline="none" class="flex gap-3 rounded-lg p-3 transition-colors hover:bg-neutral-faded">
<FeaturedIcon theme="outline" color="neutral" size="medium"><Icon /></FeaturedIcon>
<span class="flex flex-col gap-1">
<span class="flex flex-wrap items-center gap-2 font-semibold">{i.label}{i.badge && <Badge size="small" color={i.badge === 'New' ? 'neutral' : 'positive'} variant="faded">{i.badge}</Badge>}</span>
{i.description && <span class="line-clamp-2 text-sm text-neutral-faded">{i.description}</span>}
</span>
</Link>
</li>
)})}
</ul>
{(footerText || footerAction || footerLinks) && <div class="flex flex-col gap-3 rounded-xl bg-neutral-faded px-4 py-3 sm:flex-row sm:items-center sm:justify-between mt-4">
{(footerText || footerAction) && <p class="text-sm">{footerText && <span class="font-semibold">{footerText}</span>} {footerAction && <Link href={footerAction.href}>{footerAction.label}</Link>}</p>}
{footerLinks && <ul class="flex list-none flex-wrap gap-x-6 gap-y-2 p-0 text-sm font-semibold max-sm:hidden">
{footerLinks.map((f, n) => {
const Icon = footerIcons[n % footerIcons.length]!
return <li><Link href={f.href} color="neutral" underline="hover" class="inline-flex items-center gap-2"><Icon class="size-4" aria-hidden="true" />{f.label}</Link></li>
})}
</ul>}
</div>}
</div>
</NavDropdown>
) : <NavLink href={l.href}>{l.label}</NavLink>)}
{secondaryAction && <Button slot="actions" href={secondaryAction.href} variant="outline" color="neutral" size="small">{secondaryAction.label}</Button>}
{primaryAction && <Button slot="actions" href={primaryAction.href} size="small">{primaryAction.label}</Button>}
</Navbar>
Show codeHide code
---
/**
* Floating four columns slim with footer: A floating bar whose menus open one row of four links over a sign-up strip.
*
* @block header-navigation
* @order 15
*/
import { CirclePlay, DollarSign, Flag, Folder, MessageCircle, MessagesSquare, Orbit, Users } from '@lucide/astro'
import Navbar from '@/components/apogee/Navbar/Navbar.astro'
import NavLink from '@/components/apogee/NavLink/NavLink.astro'
import NavDropdown from '@/components/apogee/NavDropdown/NavDropdown.astro'
import Link from '@/components/apogee/Link/Link.astro'
import Button from '@/components/apogee/Button/Button.astro'
import FeaturedIcon from '@/components/apogee/FeaturedIcon/FeaturedIcon.astro'
import Badge from '@/components/apogee/Badge/Badge.astro'
import type { Action } from '@/components/apogee/sections/_shared/spine.ts'
import type { NavGroup, NavigationContent } from '@/components/apogee/sections/_shared/navigation.ts'
const icons = [Flag, MessageCircle, Users, Folder]
const footerIcons = [DollarSign, CirclePlay, MessagesSquare]
const groups: NavGroup[] = [{ items: [
{ label: 'About us', href: '#', description: 'Our team and what we are working towards.' },
{ label: 'Press', href: '#', description: 'News, press releases and press resources.' },
{ label: 'Careers', href: '#', description: 'We are always looking for talented people. Join our remote team.', badge: "We're hiring" },
{ label: 'Legal', href: '#', description: 'Company details, licensing, terms and privacy.' },
] }]
/** @composable */
interface Props extends NavigationContent {
/** The bar's fill: the page, a grey band or the brand colour. */
background?: 'page' | 'neutral-faded' | 'primary' | undefined
/** `dark` sets the bar in the dark theme on a light page. */
theme?: 'dark' | undefined
/** Bold line in the strip under each menu. @category content */
footerText?: string | null
/** The link after `footerText`. @category content */
footerAction?: Action | null
/** Links at the end of the strip. @category content */
footerLinks?: Action[] | null
}
const {
brand = 'Example',
links = [
{ label: 'Products', groups },
{ label: 'Services', groups },
{ label: 'Pricing', href: '#' },
{ label: 'Resources', groups },
{ label: 'About', href: '#' },
],
primaryAction = { label: 'Sign up', href: '#' },
secondaryAction = { label: 'Log in', href: '#' },
footerText = 'Ready to get started?',
footerAction = { label: 'Sign up for free', href: '#' },
footerLinks = [
{ label: 'Pricing', href: '#' },
{ label: 'Watch demo', href: '#' },
{ label: 'Chat to sales', href: '#' },
],
background,
theme,
} = Astro.props
---
<Navbar floating background={background} data-theme={theme}>
<Link slot="brand" href="/" color="inherit" underline="none" class="inline-flex items-center gap-2 text-lg font-semibold"><FeaturedIcon theme="solid" color="primary" size="small"><Orbit /></FeaturedIcon>{brand}</Link>
{links.map((l) => 'groups' in l ? (
<NavDropdown label={l.label} fullWidth floating>
<div class="p-4 md:p-6">
<ul class="list-none p-0 grid gap-2 md:grid-cols-2 xl:grid-cols-4">
{l.groups.flatMap((g) => g.items).map((i, n) => {
const Icon = icons[n % icons.length]!
return (
<li>
<Link href={i.href} color="inherit" underline="none" class="flex gap-3 rounded-lg p-3 transition-colors hover:bg-neutral-faded">
<FeaturedIcon theme="outline" color="neutral" size="medium"><Icon /></FeaturedIcon>
<span class="flex flex-col gap-1">
<span class="flex flex-wrap items-center gap-2 font-semibold">{i.label}{i.badge && <Badge size="small" color={i.badge === 'New' ? 'neutral' : 'positive'} variant="faded">{i.badge}</Badge>}</span>
{i.description && <span class="line-clamp-2 text-sm text-neutral-faded">{i.description}</span>}
</span>
</Link>
</li>
)})}
</ul>
{(footerText || footerAction || footerLinks) && <div class="flex flex-col gap-3 rounded-xl bg-neutral-faded px-4 py-3 sm:flex-row sm:items-center sm:justify-between mt-4">
{(footerText || footerAction) && <p class="text-sm">{footerText && <span class="font-semibold">{footerText}</span>} {footerAction && <Link href={footerAction.href}>{footerAction.label}</Link>}</p>}
{footerLinks && <ul class="flex list-none flex-wrap gap-x-6 gap-y-2 p-0 text-sm font-semibold max-sm:hidden">
{footerLinks.map((f, n) => {
const Icon = footerIcons[n % footerIcons.length]!
return <li><Link href={f.href} color="neutral" underline="hover" class="inline-flex items-center gap-2"><Icon class="size-4" aria-hidden="true" />{f.label}</Link></li>
})}
</ul>}
</div>}
</div>
</NavDropdown>
) : <NavLink href={l.href}>{l.label}</NavLink>)}
{secondaryAction && <Button slot="actions" href={secondaryAction.href} variant="outline" color="neutral" size="small">{secondaryAction.label}</Button>}
{primaryAction && <Button slot="actions" href={primaryAction.href} size="small">{primaryAction.label}</Button>}
</Navbar>Add
npx @ava-ui/apogee add header-navigation/floating-four-col-slim-with-footer
This copies the block to src/components/apogee/blocks/header-navigation/ with everything it uses: Badge, Button, FeaturedIcon, Link, NavDropdown, NavLink, Navbar. 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.
Accessibility
- Each menu is a button with a native popover: Esc or a click outside closes it and focus returns to the button. On phones the menus are rows that expand inside the menu. No JS is needed for either.
Related
NavbarA responsive site header that collapses its links into a mobile panel below the md breakpoint.
NavDropdownA Navbar item that opens a panel of links; a popover under the button or across the header on desktop, a row that expands in the phone menu.
NavLinkA navigation link that auto-detects and styles the current route, with prefix matching, external links and a collapsible NavGroup.