Footer
Site footer with auto-fitting multi-column link groups, optional logo, social links, and a bottom bar.
Import
import { Footer } from "@hyzer-labs/ui"Demo
default fills with the muted surface (--hz-color-surface-muted). minimal is transparent with tighter padding, so whatever sits behind it
shows through. bordered is a separate boolean prop (a top hairline), so it
composes with either variant. Each demo sits on a tinted backdrop that is not part of
Footer, so minimal has something to show through. Each column's links reuses Nav's NavItem shape (label/href/external/ariaCurrent).
const columns: FooterColumn[] = [
{
title: 'Product',
links: [{ label: 'Components', href: '/components' } /* … */]
},
{
title: 'Resources',
links: [{ label: 'GitHub', href: 'https://github.com/hyzerlabs/ui', external: true } /* … */]
},
{ title: 'Company', links: [{ label: 'About', href: '/about' } /* … */] }
];
<Footer {columns} headingLevel={3} />const columns: FooterColumn[] = [
{
title: 'Product',
links: [{ label: 'Components', href: '/components' } /* … */]
},
{
title: 'Resources',
links: [{ label: 'GitHub', href: 'https://github.com/hyzerlabs/ui', external: true } /* … */]
},
{ title: 'Company', links: [{ label: 'About', href: '/about' } /* … */] }
];
<Footer {columns} headingLevel={3} variant="minimal" />const columns: FooterColumn[] = [
{
title: 'Product',
links: [{ label: 'Components', href: '/components' } /* … */]
},
{
title: 'Resources',
links: [{ label: 'GitHub', href: 'https://github.com/hyzerlabs/ui', external: true } /* … */]
},
{ title: 'Company', links: [{ label: 'About', href: '/about' } /* … */] }
];
<Footer {columns} headingLevel={3} bordered />const columns: FooterColumn[] = [
{
title: 'Product',
links: [{ label: 'Components', href: '/components' } /* … */]
},
{
title: 'Resources',
links: [{ label: 'GitHub', href: 'https://github.com/hyzerlabs/ui', external: true } /* … */]
},
{ title: 'Company', links: [{ label: 'About', href: '/about' } /* … */] }
];
<Footer {columns} headingLevel={3} variant="minimal" bordered />linkVariant restyles every column link: subtle (muted,
default), default (standard link treatment), nav (nav-weight, primary on hover).
<Footer {columns} headingLevel={3} /><Footer {columns} headingLevel={3} linkVariant="default" />social is a plain snippet, so any icon works, including brand marks. The
library ships the full Lucide set (generic glyphs like globe, mail, and rss below). Brand marks such as GitHub or X are
bring-your-own: drop an SVG, an icon-font glyph, or a package like simple-icons straight into the snippet.
<Footer {columns} headingLevel={3}>
{#snippet logo()}<strong>@hyzer-labs/ui</strong>{/snippet}
{#snippet social()}
<Link href="https://hyzer.sh" ariaLabel="Website"><IconGlobe /></Link>
<Link href="mailto:hello@hyzer.sh" ariaLabel="Email"><IconMail /></Link>
<Link href="/rss.xml" ariaLabel="RSS feed"><IconRss /></Link>
{/snippet}
{#snippet bottom()}
<p>© 2026 Hyzer Labs, MIT License</p>
{/snippet}
</Footer>Columns auto-fit the footer's own width: you get as many as have at least --hz-footer-col-min of room (12rem, and yours to retune through that custom property).
No breakpoints involved. Use the slider to watch them stack.
3 columns
<!-- Columns auto-fit the footer’s own width: as many as have at least
--hz-footer-col-min (12rem, yours to retune) of room -->
<Footer {columns} headingLevel={3} />Footer has no dedicated background hook. class merges after hz-footer, and your unlayered CSS beats the theme without needing !important. A low-percentage color-mix tint keeps the existing text
color's contrast intact.
<Footer {columns} headingLevel={3} class="brand-footer" />
<style>
:global(.brand-footer) {
background: color-mix(in srgb, var(--hz-intent-primary) 10%, var(--hz-color-surface));
}
</style>Props
| Name | Type | Default | Note |
|---|---|---|---|
columns | FooterColumn[] | — | Required. See FooterColumn below. |
variant | 'default' | 'minimal' | 'default' | |
bordered | boolean | false | Top hairline — composes with any variant. |
linkVariant | 'default' | 'subtle' | 'nav' | 'subtle' | Passed through to every column Link. |
headingLevel | 2 | 3 | 4 | 5 | 6 | 2 | Heading element for column titles — match your page hierarchy. |
logo | Snippet | — | Rendered above the columns. |
social | Snippet | — | Icon-link row rendered under the columns. |
bottom | Snippet | — | Bottom bar with a top hairline — copyright, legal links. |
class | string | — | Merged after the hz-footer class. |
Anything not listed above is forwarded as an attribute to the root element (or the native control in form components). So id, data-*, aria-*, and event handlers just work.
FooterColumn
| Name | Type | Default | Note |
|---|---|---|---|
title | string | — | Required. Labels the column’s nav landmark. |
links | NavItem[] | — | Required. Footer links are a flat list: label/href/external/ariaCurrent apply and children is ignored — see NavItem on the Nav page. For navigation with expandable sections, use a vertical Nav instead. |
Theme hooks
What this component promises your CSS. The reference theme styles exactly these — from @layer hz-theme, so your unlayered rules win. See Styling Components for the how.
Root class: .hz-footer
Data attributes
| Hook | Values | Styles |
|---|---|---|
data-variant | 'default' | 'minimal' | minimal drops the padding and goes transparent. Default default. |
data-bordered | present when bordered | Top hairline. A prop, not a variant — it composes with either surface. |
Custom properties
| Hook | Values | Styles |
|---|---|---|
--hz-footer-col-min | <length> — default 12rem | Minimum column width for the auto-fit link grid: how narrow a column may get before the row reflows. No media queries involved — this is the whole knob. |
Part classes
| Hook | Values | Styles |
|---|---|---|
.hz-footer-logo | child element | Wrapper for the logo snippet. |
.hz-footer-columns | on a Grid | Rides through Grid’s class prop. The auto-fit override hangs off it — and lands on .hz-grid-layout, since that is where Grid does its layout. |
.hz-footer-column | child element | One column’s nav landmark. |
.hz-footer-heading | child element | A column heading. |
.hz-footer-social | child element | Wrapper for the social snippet. |
.hz-footer-bottom | child element | The bottom bar. |
Accessibility
Each column is a <nav> landmark whose aria-label is its title. The visible heading repeats that text, but the heading is not what names the landmark — the title is, so give every column one and keep those titles distinct from the site's main navigation label. A column with a blank title renders without a landmark or heading. Set headingLevel to match your page hierarchy. Links use the Link component, and icon-only social links need an ariaLabel.