A footer is the last chance your page has to help a visitor find what they need. You will build a simple, clean website footer that scales from mobile to desktop, includes link columns, a short brand block, a small newsletter form, social icons, and a compact “back to top” control. You will leave with a drop-in HTML structure and a tidy CSS system that you can theme in minutes.
Why a Simple, Clean Website Footer Matters
Clarity at the bottom of a page reduces friction for visitors who scroll all the way down. A clean footer offers quick access to key links, gives your brand a steady finish, and avoids fatigue from dense layouts. Good spacing, readable contrast, and predictable patterns help people scan. Developers benefit too: a clear structure is easier to maintain, theme, and test across devices.
Prerequisites
You do not need a framework. The steps use modern CSS that ships in all evergreen browsers and keeps the HTML semantic and accessible.
- Basic HTML
- CSS custom properties
- CSS pseudo-elements (::before / ::after)
Step 1: The HTML Structure
Here is the complete, final HTML. The footer uses three main zones: a brand summary, link columns, and a small newsletter form in the top row; then a bottom row with social links, copyright, and a back-to-top link. A decorative divider element sits above the footer to create a soft transition from the page content.
<!-- HTML -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Simple, Clean Footer</title>
</head>
<body id="top">
<main>
<section class="content">
<h1>Page Content</h1>
<p>This area is only to provide scrollable content above the footer.</p>
</section>
</main>
<!-- Decorative divider above the footer -->
<div class="footer-wave" aria-hidden="true"></div>
<footer class="site-footer" role="contentinfo">
<div class="footer-top container">
<section class="footer-brand">
<a class="logo" href="/">Acme</a>
<p class="tagline">Tools that help you ship with clarity.</p>
</section>
<nav class="footer-links" aria-label="Footer">
<div class="link-col">
<h3 class="link-heading">Product</h3>
<ul class="list-reset">
<li><a href="#">Overview</a></li>
<li><a href="#">Pricing</a></li>
<li><a href="#">Changelog</a></li>
<li><a href="#">Status</a></li>
</ul>
</div>
<div class="link-col">
<h3 class="link-heading">Company</h3>
<ul class="list-reset">
<li><a href="#">About</a></li>
<li><a href="#">Careers</a></li>
<li><a href="#">Press</a></li>
<li><a href="#">Contact</a></li>
</ul>
</div>
<div class="link-col">
<h3 class="link-heading">Resources</h3>
<ul class="list-reset">
<li><a href="#">Docs</a></li>
<li><a href="#">Guides</a></li>
<li><a href="#">Blog</a></li>
<li><a href="#">Community</a></li>
</ul>
</div>
</nav>
<section class="newsletter" aria-label="Newsletter">
<h3 class="link-heading">Stay in the loop</h3>
<form class="newsletter-form">
<label class="sr-only" for="email">Email address</label>
<input id="email" name="email" type="email" placeholder="you@example.com" required>
<button type="submit">Join</button>
</form>
<p class="note">No spam. Unsubscribe any time.</p>
</section>
</div>
<div class="footer-bottom container">
<ul class="social list-reset" aria-label="Social media">
<li>
<a href="#" aria-label="Twitter">
<span class="icon circle twitter" aria-hidden="true"></span>
<span class="sr-only">Twitter</span>
</a>
</li>
<li>
<a href="#" aria-label="GitHub">
<span class="icon circle github" aria-hidden="true"></span>
<span class="sr-only">GitHub</span>
</a>
</li>
<li>
<a href="#" aria-label="LinkedIn">
<span class="icon circle linkedin" aria-hidden="true"></span>
<span class="sr-only">LinkedIn</span>
</a>
</li>
</ul>
<p class="copyright">© 2025 Acme Inc.</p>
<a class="to-top" href="#top" aria-label="Back to top">
<span class="arrow" aria-hidden="true"></span>
<span class="sr-only">Back to top</span>
</a>
</div>
</footer>
</body>
</html>
Step 2: The Basic CSS & Styling
This base layer sets variables, typography, containers, and simple resets. Color tokens make theming painless. The container class keeps content aligned with the rest of the page. Links start with a low-contrast underline that brightens on hover.
/* CSS */
:root {
--footer-bg: #0f172a; /* slate-900 */
--footer-bg-2: #0b1220; /* slightly darker for depth */
--text: #94a3b8; /* slate-400 */
--text-strong: #e2e8f0;/* slate-100 */
--muted: #7c869b; /* neutral for notes */
--link: #cbd5e1;
--accent: #22d3ee; /* cyan-400 */
--accent-2: #38bdf8; /* sky-400 */
--ring: 2px solid #22d3ee;
--radius: 10px;
--gap: 1rem;
--gap-lg: 2rem;
--maxw: 1100px;
}
*,
*::before,
*::after { box-sizing: border-box; }
body {
margin: 0;
font: 16px/1.6 system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, "Noto Sans", Arial, "Apple Color Emoji", "Segoe UI Emoji";
color: #0f172a;
background: #ffffff;
}
.container {
width: min(100% - 2rem, var(--maxw));
margin-inline: auto;
}
.list-reset {
margin: 0;
padding: 0;
list-style: none;
}
.sr-only {
position: absolute;
width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
a { color: var(--link); text-decoration-color: color-mix(in oklab, var(--link) 40%, transparent); }
a:hover, a:focus { color: var(--text-strong); text-decoration-color: currentColor; }
.content {
min-height: 60vh;
padding: 3rem 1rem 6rem;
}
.footer-wave { /* decorative scallop, sits above the footer */
height: 24px;
background: var(--footer-bg);
-webkit-mask: radial-gradient(12px at 12px 12px, #000 98%, transparent 100%) repeat-x;
mask: radial-gradient(12px at 12px 12px, #000 98%, transparent 100%) repeat-x;
-webkit-mask-size: 24px 24px;
mask-size: 24px 24px;
}
.site-footer {
background:
radial-gradient(1200px 300px at 50% 0, var(--footer-bg), var(--footer-bg-2)),
var(--footer-bg);
color: var(--text);
}
.site-footer .container {
padding: 2rem 0;
}
Advanced Tip: Keep footer colors in custom properties so a dark or light theme swap becomes a single variable update. Tokens can match your design system and still allow small tweaks at the component level.
Step 3: Build the Footer Layout Grid
This layer sets the top row grid, spacing, and responsive behavior. The layout starts stacked on small screens, then steps into a multi-column grid when space allows.
/* CSS */
.footer-top {
display: grid;
gap: var(--gap-lg);
padding-top: 2.5rem;
padding-bottom: 2rem;
}
.footer-brand .logo {
display: inline-block;
font-weight: 800;
letter-spacing: 0.5px;
color: var(--text-strong);
font-size: 1.25rem;
text-decoration: none;
}
.footer-brand .tagline {
margin: 0.5rem 0 0;
color: var(--text);
max-width: 36ch;
}
.footer-links {
display: grid;
gap: var(--gap);
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.link-heading {
margin: 0 0 .5rem;
color: var(--text-strong);
font-size: .95rem;
font-weight: 700;
}
.link-col a {
display: inline-block;
padding: .25rem 0;
text-decoration-thickness: 1px;
text-underline-offset: 3px;
}
.newsletter {
display: grid;
gap: .5rem;
}
.newsletter-form {
display: grid;
grid-template-columns: 1fr auto;
gap: .5rem;
align-items: center;
}
.newsletter-form input[type="email"] {
background: #0b1426;
border: 1px solid color-mix(in oklab, var(--text) 30%, transparent);
color: var(--text-strong);
padding: .625rem .75rem;
border-radius: var(--radius);
outline: none;
}
.newsletter-form input[type="email"]:focus {
border-color: var(--accent);
box-shadow: 0 0 0 4px color-mix(in oklab, var(--accent) 30%, transparent);
}
.newsletter-form button {
background: linear-gradient(180deg, var(--accent), var(--accent-2));
color: #001018;
font-weight: 700;
border: 0;
padding: .65rem .9rem;
border-radius: var(--radius);
cursor: pointer;
}
.newsletter .note {
margin: 0;
font-size: .875rem;
color: var(--muted);
}
.footer-bottom {
display: grid;
grid-template-columns: 1fr auto auto;
align-items: center;
gap: var(--gap);
padding-top: 1.25rem;
border-top: 1px solid color-mix(in oklab, var(--text) 20%, transparent);
}
.social {
display: flex;
gap: .5rem;
align-items: center;
padding: 0;
}
.icon {
display: inline-block;
width: 36px;
height: 36px;
}
.circle {
border-radius: 50%;
background: #0b1426;
border: 1px solid color-mix(in oklab, var(--text) 30%, transparent);
}
.twitter { background-image: radial-gradient(circle at 70% 30%, #1da1f2 12%, transparent 13%); }
.github { background-image: radial-gradient(circle at 48% 45%, #ffffff 10%, transparent 11%); }
.linkedin { background-image: radial-gradient(circle at 55% 40%, #0a66c2 12%, transparent 13%); }
.to-top {
justify-self: end;
display: inline-grid;
place-items: center;
gap: .25rem;
color: var(--text-strong);
text-decoration: none;
}
.to-top .arrow {
--size: 10px;
width: 0; height: 0;
border-left: var(--size) solid transparent;
border-right: var(--size) solid transparent;
border-bottom: calc(var(--size) * 1.4) solid var(--text-strong);
filter: drop-shadow(0 1px 0 rgba(0,0,0,.2));
}
/* Responsive layout */
@media (min-width: 880px) {
.footer-top {
grid-template-columns: 1.2fr 1fr 1fr 1fr;
align-items: start;
}
.footer-brand { padding-right: 1rem; }
.newsletter { grid-column: span 1; }
}
How This Works (Code Breakdown)
The top row uses grid with a single column by default. This stacks the brand, links, and newsletter in a natural reading order. At the 880px breakpoint, the grid switches to four columns to deliver a classic desktop footer. The brand span is slightly wider, which gives the logo and tagline breathing room.
Link columns use small underlines with a 3px offset so the text remains clean while still indicating click targets. The newsletter input uses a subtle border and a focus ring made with box-shadow rather than outline, which keeps the visual weight light on dark surfaces. The “Back to top” indicator uses a simple CSS triangle for the arrow. If you prefer a stemmed arrow icon instead of a plain triangle, follow the patterns in make an arrow up with CSS at make an arrow up with CSS.
Social icons are circular badges. They are decorative here, so each anchor gets an aria-label and a visually hidden text span for screen readers. If you want to craft crisp circular badges from first principles, the approach here mirrors the ideas in make a circle with CSS.
Step 4: Build the Details and Micro-Interactions
This layer adds hover transitions, refined focus styles, and small balances to spacing and contrast. Subtle feedback helps visitors understand that the footer is still an interactive zone without stealing attention from the main content.
/* CSS */
.footer-links a {
transition: color .2s ease, text-decoration-color .2s ease;
}
.footer-links a:focus-visible,
.newsletter-form button:focus-visible,
.to-top:focus-visible {
outline: none;
box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 45%, transparent);
border-radius: calc(var(--radius) - 2px);
}
.newsletter-form button:hover {
filter: brightness(1.05);
}
.circle { transition: transform .15s ease, box-shadow .15s ease; }
.social a:focus-visible .circle,
.social a:hover .circle {
transform: translateY(-1px);
box-shadow: 0 6px 12px rgba(0,0,0,.25);
}
.to-top {
gap: .35rem;
font-size: .9rem;
opacity: .9;
}
.to-top:hover { opacity: 1; }
How This Works (Code Breakdown)
Focus-visible styles use a soft cyan ring to keep keyboard navigation clear on a dark background. Hover feedback stays small: links only tweak color and underline, social badges lift by 1px with a shadow, and the back-to-top control brightens slightly. These micro-interactions guide attention without creating jitter at the page edge.
Step 5: Add a Decorative Divider and Finishing Touches
The scalloped divider draws the eye to the footer without heavy graphics or images. The shape uses a repeating radial mask so the top edge of the footer shows as a row of semicircles. To explore more divider patterns and spacing ideas, see CSS wave page divider.
/* CSS */
.footer-wave {
height: 24px;
background: var(--footer-bg);
-webkit-mask: radial-gradient(12px at 12px 12px, #000 98%, transparent 100%) repeat-x;
mask: radial-gradient(12px at 12px 12px, #000 98%, transparent 100%) repeat-x;
-webkit-mask-size: 24px 24px;
mask-size: 24px 24px;
}
/* Tighten small-screen spacing so the footer does not feel heavy */
@media (max-width: 479px) {
.footer-top { gap: 1.25rem; }
.footer-bottom { grid-template-columns: 1fr; text-align: center; }
.to-top { justify-self: center; }
.social { justify-content: center; }
}
How This Works (Code Breakdown)
The divider uses -webkit-mask and mask with a repeating radial gradient to cut semicircles out of a solid bar. Only the masked area shows, so the page background peeks through between the scallops. Because the wave sits in its own element above the footer background, it stays easy to remove or restyle.
Advanced Techniques: Add Motion and Hover Polish
Motion should be small and purposeful in a footer. The examples below add a gentle rise animation to the back-to-top arrow and a highlight sweep on link hover. A media query respects visitors who prefer reduced motion.
/* CSS */
@keyframes rise {
from { transform: translateY(2px); opacity: .85; }
to { transform: translateY(0); opacity: 1; }
}
.to-top:hover .arrow {
animation: rise .25s ease-in-out both;
}
.footer-links a {
background:
linear-gradient(currentColor, currentColor) 0 100% / 0 1px no-repeat;
transition: background-size .25s ease, color .2s ease;
}
.footer-links a:hover {
background-size: 100% 1px;
}
/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.circle,
.to-top .arrow,
.footer-links a {
transition: none;
animation: none;
}
}
Accessibility & Performance
Strong structure helps both. The footer uses role=”contentinfo” for assistive tech, a nav with aria-label=”Footer” for link groups, and descriptive aria-labels on social links that otherwise would be only visual badges. Focus-visible rings on interactive elements keep keyboard users on track. The back-to-top control includes readable text for screen readers, not just a visual arrow.
Accessibility
Maintain a contrast ratio of at least 4.5:1 for body text on the dark background. The color tokens above already lean toward that. The newsletter input carries an explicit label for screen readers through a visually hidden element. Keep the form button text short but clear. For any decorative icons, add aria-hidden=”true” on the icon element, and provide explicit labeling on the link or button itself.
Performance
The footer avoids external images and uses CSS gradients and masks for decoration, which keeps network requests low. Gradients render fast on modern GPUs. Animations are short, use transform and opacity, and respect prefers-reduced-motion. The grid layout reduces wrapper elements, which helps layout cost and keeps markup readable.
Tip: Treat the footer like any other component. Keep a single source of truth for spacing and colors with custom properties, write mobile-first styles, and resist one-off overrides that make future changes harder.
Ship a Footer You Can Reuse
You built a modern footer with a clear structure, responsive grid, soft focus states, and a few tasteful details. You can now theme it quickly, expand link columns, or swap in a different divider pattern. Fold this into your design system and keep shipping pages with a clean finish.