Footer
Preview
Features
-
Responsive Design: It automatically rearranges elements according to the screen size (mobile, tablet, PC), ensuring good readability.
-
Optional Logo: If you don’t specify a
logo
, only the navigation links will be displayed and right-aligned. -
Accessibility: Visual indicators are provided for
hover
andfocus
states on links, making keyboard navigation user-friendly. -
Dark Mode Support: Background and text colors adapt automatically to system settings, leveraging design tokens for consistent styling.
-
Minimal Props: Only
logo
(optional) andnavItems
are required to quickly implement a fully-featured footer.
Even if the logo text is very long, styles are carefully applied to prevent layout breaking.
Properties
Property | Type | Default Value | Description |
---|---|---|---|
logo | string | Optional (If empty or undefined, no logo is shown) | The text displayed on the left side of the footer. If not specified, links are right-aligned. |
navItems | {label: string; href: string;}[] | [ { label: "Privacy Policy", href: "/privacy" } ] | An array of link objects to display at the footer. |
Github
Code Examples
import "@/styles/tailwind.css";
type NavItem = { label: string; href: string;};
type FooterProps = { logo?: string; navItems?: NavItem[];};
export default function Footer({ logo, navItems = [ { label: "About", href: "#" }, { label: "Contact", href: "#" }, { label: "Q&A", href: "#" }, ],}: FooterProps) { return ( <footer className="relative w-full bg-white text-black dark:bg-black dark:text-white mt-8"> <div className="absolute top-0 left-0 w-full h-[2px] bg-gradient-to-r from-lime-500 via-teal-400 to-blue-500" /> <div className={`flex flex-col md:flex-row items-center px-4 py-6 gap-4 text-center md:text-left md:px-8 ${ logo ? "md:justify-between" : "md:justify-end" }`} > {logo && ( <h2 className="text-xl font-bold overflow-hidden text-ellipsis whitespace-nowrap"> {logo} </h2> )} <nav className="flex flex-wrap items-center gap-4 md:gap-8 justify-center md:justify-end w-full md:w-auto"> {navItems.map(({ label, href }) => ( <a key={href} href={href} className="block px-2 py-1 transition-opacity duration-200 hover:opacity-75 focus:opacity-75" > {label} </a> ))} </nav> </div> <div className="border-t border-dashed border-gray/30 dark:border-white/30 px-4 py-4 md:px-8 text-sm text-center"> © {new Date().getFullYear()} Your Company </div> </footer> );}
How to Use
Import the component into your page or layout. Override logo
and navItems
as needed.
Adjust the import destination for each framework, and ideally keep the code in a separate file.
// import Footer from "@/components/Footer";import Footer from "@/components/Footer";
export default function Layout({ children }) { return ( <> <main id="main">{children}</main> <Footer logo="MyWebsite" navItems={[ { label: "Privacy Policy", href: "/privacy" }, { label: "Terms of Service", href: "/terms" }, { label: "Contact", href: "/contact" }, ]} /> </> );}
import { colorPalette } from "@/styles/ColorPalette";import { typography } from "@/styles/Typography";import { LitElement, css, html } from "lit";import { customElement, property } from "lit/decorators.js";
type NavItem = { label: string; href: string;};
@customElement("my-footer")export class MyFooter extends LitElement { @property({ type: String }) logo?: string; @property({ type: Array }) navItems: NavItem[] = [ { label: "About", href: "/about" }, { label: "Contact", href: "/contact" }, { label: "Q&A", href: "/qa" }, ];
static styles = [ colorPalette, typography, css` :host { display: block; position: relative; margin-top: var(--spacing-8); background-color: var(--light-basic-white); color: var(--light-basic-black); font-family: var(--font-family-sans); } @media (prefers-color-scheme: dark) { :host { background-color: var(--dark-basic-black); color: var(--dark-basic-white); } } .gradient-line { position: absolute; top: 0; left: 0; width: 100%; height: 2px; background: linear-gradient( to right, var(--basic-lime), var(--basic-teal), var(--basic-blue) ); } .footer-content { display: flex; flex-direction: column; align-items: center; padding: var(--spacing-6) var(--spacing-4); text-align: center; gap: var(--spacing-4); } @media (min-width: 768px) { .footer-content { flex-direction: row; justify-content: space-between; text-align: left; padding: var(--spacing-6) var(--spacing-8); } } .logo { font-size: var(--font-xl); font-weight: var(--font-bold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin: 0; } .nav { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--spacing-4); } @media (min-width: 768px) { .nav { justify-content: flex-end; gap: var(--spacing-8); } } .link { text-decoration: none; color: inherit; padding: var(--spacing-1) var(--spacing-2); transition: opacity 0.2s ease-in-out; } .link:hover, .link:focus { opacity: 0.75; } .border { border-top: 1px dashed var(--basic-gray); } @media (prefers-color-scheme: dark) { .border { border-top: 1px dashed var(--basic-white); } } .copyright { padding: var(--spacing-4); text-align: center; font-size: var(--font-sm); } @media (min-width: 768px) { .copyright { padding: var(--spacing-4) var(--spacing-8); } } `, ];
render() { return html` <footer> <div class="gradient-line"></div> <div class="footer-content" style=${!this.logo ? "justify-content: flex-end;" : ""} > ${this.logo ? html`<h2 class="logo">${this.logo}</h2>` : null} <nav class="nav"> ${this.navItems.map( (item) => html` <a class="link" href=${item.href}>${item.label}</a> `, )} </nav> </div> <div class="border"> <div class="copyright"> © ${new Date().getFullYear()} Your Company </div> </div> </footer> `; }}
How to Use
Import the component into your page or layout. Override logo
and navItems
as needed.
Adjust the import destination for each framework, and ideally keep the code in a separate file.
// import "@/components/MyFooter";export class AppLayout extends HTMLElement { connectedCallback() { this.innerHTML = ` <main id="main"> <!-- Main content goes here --> </main> <my-footer logo="MyAwesomeSite" .navItems="${JSON.stringify([ { label: 'Privacy Policy', href: '/privacy' }, { label: 'Terms of Service', href: '/terms' }, { label: 'Contact', href: '/contact' }, ])}" ></my-footer> `; }}