SolutionsCase StudiesAI ToolsResourcesAboutContact
Get Your Free AI Automation Audit Book a Free 30-Minute Strategy Call
Playground~/frontend Open editor

~/css/navigation

6 CSS Navigation

5 Pure CSS1 with JSPublished 2026-10-03Free · no sign-up

Navigation components built to be found and used by everyone: a responsive navbar with a proper menu button, tabs that work without JavaScript, an FAQ accordion on native details, breadcrumbs, pagination and an app sidebar. Each one handles keyboard focus and tells screen readers where the visitor is.

01. Responsive Navbar with Menu Button

CSS + JS

A header with a logo, links and a call-to-action that turns into a menu button on small screens. The button is a real button with aria-expanded and aria-controls, so screen readers announce whether the menu is open, and Escape closes it. Resize the editor preview below 600px to see the mobile menu.

View code
<header class="ttn-bar">
  <a href="#" class="ttn-logo">Northwind</a>
  <button class="ttn-toggle" aria-expanded="false" aria-controls="ttn-menu">Menu</button>
  <nav id="ttn-menu" class="ttn-menu" aria-label="Main">
    <a href="#" aria-current="page">Shop</a>
    <a href="#">Collections</a>
    <a href="#">About</a>
    <a href="#" class="ttn-cta">Cart (2)</a>
  </nav>
</header>
.ttn-bar {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  width: min(100%, 760px);
  padding: 12px 16px;
  border: 1px solid rgba(255, 255, 255, .1);
  border-radius: 14px;
  background: #121722;
  font-family: Inter, system-ui, sans-serif;
}
.ttn-logo { color: #F5F7FA; font-weight: 800; text-decoration: none; }
.ttn-menu { display: flex; align-items: center; gap: 4px; }
.ttn-menu a { padding: 8px 12px; border-radius: 8px; color: #A7AFBD; font-size: 14px; font-weight: 600; text-decoration: none; }
.ttn-menu a:hover, .ttn-menu a[aria-current="page"] { color: #F5F7FA; background: rgba(255, 255, 255, .06); }
.ttn-menu .ttn-cta { margin-left: 8px; background: #5B8CFF; color: #080A0F; }
.ttn-toggle { display: none; padding: 8px 12px; border: 1px solid rgba(255, 255, 255, .2); border-radius: 8px; background: none; color: #F5F7FA; font: 600 14px Inter, system-ui, sans-serif; cursor: pointer; }
a:focus-visible, .ttn-toggle:focus-visible { outline: 2px solid #7DA3FF; outline-offset: 2px; }
@media (max-width: 600px) {
  .ttn-toggle { display: block; }
  .ttn-menu {
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    right: 0;
    display: none;
    flex-direction: column;
    align-items: stretch;
    padding: 8px;
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 14px;
    background: #121722;
  }
  .ttn-menu.is-open { display: flex; }
  .ttn-menu .ttn-cta { margin-left: 0; text-align: center; }
}
var toggle = document.querySelector('.ttn-toggle');
var menu = document.getElementById('ttn-menu');
function setOpen(open) {
  menu.classList.toggle('is-open', open);
  toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
  toggle.textContent = open ? 'Close' : 'Menu';
}
toggle.addEventListener('click', function () { setOpen(!menu.classList.contains('is-open')); });
document.addEventListener('keydown', function (e) { if (e.key === 'Escape') { setOpen(false); toggle.focus(); } });

02. Tabs (No JavaScript)

Pure CSS

Three tabs switched with hidden radio buttons and the :checked selector, so they work without JavaScript and arrow keys move between them. The active tab gets an underline and stronger text. For complex apps, add the full ARIA tabs pattern with JavaScript; for simple content panels this is enough.

View code
<div class="ttn-tabs">
  <input type="radio" name="ttn-tab" id="ttn-t1" checked>
  <label for="ttn-t1">Details</label>
  <input type="radio" name="ttn-tab" id="ttn-t2">
  <label for="ttn-t2">Shipping</label>
  <input type="radio" name="ttn-tab" id="ttn-t3">
  <label for="ttn-t3">Returns</label>
  <div class="ttn-panel" id="ttn-p1">Solid oak, oiled finish. 40 × 30 × 12 cm.</div>
  <div class="ttn-panel" id="ttn-p2">Ships in 2 working days. Free over $80.</div>
  <div class="ttn-panel" id="ttn-p3">Return within 30 days for a full refund.</div>
</div>
.ttn-tabs {
  display: flex;
  flex-wrap: wrap;
  width: min(100%, 520px);
  font-family: Inter, system-ui, sans-serif;
}
.ttn-tabs input { position: absolute; opacity: 0; pointer-events: none; }
.ttn-tabs label {
  padding: 12px 16px;
  border-bottom: 2px solid rgba(255, 255, 255, .1);
  color: #A7AFBD;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}
.ttn-tabs input:checked + label { color: #F5F7FA; border-bottom-color: #5B8CFF; }
.ttn-tabs input:focus-visible + label { outline: 2px solid #7DA3FF; outline-offset: -2px; border-radius: 6px 6px 0 0; }
.ttn-panel {
  display: none;
  width: 100%;
  order: 1;
  padding: 18px 4px;
  color: #D7DCE6;
  line-height: 1.6;
}
#ttn-t1:checked ~ #ttn-p1, #ttn-t2:checked ~ #ttn-p2, #ttn-t3:checked ~ #ttn-p3 { display: block; }

03. Accordion with details and summary

Pure CSS

An FAQ accordion built on the native details and summary elements, which are keyboard accessible and announced correctly by screen readers with no JavaScript. The plus sign rotates into a cross when a panel opens. Adding the same name attribute to each details element makes only one open at a time in current browsers.

View code
<div class="ttn-acc">
  <details name="ttn-faq" open>
    <summary>How long does delivery take?</summary>
    <p>Most orders arrive in two to four working days.</p>
  </details>
  <details name="ttn-faq">
    <summary>Can I return an item?</summary>
    <p>Yes, within 30 days, as long as it is unused.</p>
  </details>
  <details name="ttn-faq">
    <summary>Do you ship abroad?</summary>
    <p>We ship to most countries. Costs are shown at checkout.</p>
  </details>
</div>
.ttn-acc { display: grid; gap: 8px; width: min(100%, 520px); font-family: Inter, system-ui, sans-serif; }
.ttn-acc details { border: 1px solid rgba(255, 255, 255, .1); border-radius: 12px; background: #121722; }
.ttn-acc summary {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 14px 16px;
  color: #F5F7FA;
  font-weight: 700;
  cursor: pointer;
  list-style: none;
}
.ttn-acc summary::-webkit-details-marker { display: none; }
.ttn-acc summary::after { content: "+"; color: #7DA3FF; font-size: 20px; line-height: 1; transition: transform .2s ease; }
.ttn-acc details[open] summary::after { transform: rotate(45deg); }
.ttn-acc summary:focus-visible { outline: 2px solid #7DA3FF; outline-offset: 2px; border-radius: 12px; }
.ttn-acc p { margin: 0; padding: 0 16px 16px; color: #A7AFBD; line-height: 1.6; }
@media (prefers-reduced-motion: reduce) { .ttn-acc summary::after { transition: none; } }

05. Pagination

Pure CSS

Page links with previous and next controls, a current page marker and an ellipsis for skipped pages. Every control is at least 40px square for touch, the current page uses aria-current so it is announced, and disabled controls are real disabled states rather than just greyed-out text.

View code
<nav class="ttn-pages" aria-label="Pagination">
  <a href="#" class="ttn-prev" aria-disabled="true" tabindex="-1">Previous</a>
  <a href="#" aria-current="page">1</a>
  <a href="#">2</a>
  <a href="#">3</a>
  <span aria-hidden="true">…</span>
  <a href="#">12</a>
  <a href="#" class="ttn-next">Next</a>
</nav>
.ttn-pages { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-family: Inter, system-ui, sans-serif; }
.ttn-pages a, .ttn-pages span {
  display: grid;
  place-items: center;
  min-width: 40px;
  height: 40px;
  padding: 0 12px;
  border: 1px solid rgba(255, 255, 255, .12);
  border-radius: 10px;
  color: #D7DCE6;
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
}
.ttn-pages span { border-color: transparent; color: #7F8899; }
.ttn-pages a:hover { background: rgba(255, 255, 255, .06); }
.ttn-pages a[aria-current="page"] { border-color: transparent; background: #5B8CFF; color: #080A0F; }
.ttn-pages a[aria-disabled="true"] { opacity: .4; pointer-events: none; }
.ttn-pages a:focus-visible { outline: 2px solid #7DA3FF; outline-offset: 2px; }

About these CSS Navigation

Navigation must be accessible first

Navigation is how people move through a site, so a broken menu breaks the whole visit. Use real links and buttons, mark the current page with aria-current="page", give the navigation a label such as aria-label="Main", and make sure every control is reachable with the Tab key and shows a visible focus ring.

Mobile menus need a real button

The menu toggle in the navbar demo is a <button> with aria-expanded and aria-controls, so assistive technology announces whether the menu is open. The Escape key closes it and returns focus to the button, which is what keyboard users expect from any pop-up.

Use native elements where they exist

The accordion uses <details> and <summary>, which browsers already make keyboard accessible and announce correctly, with no JavaScript. Native elements are less code to maintain and often more robust than custom widgets.

Breadcrumbs and pagination help search engines too

Breadcrumbs inside a nav with an ordered list, and pagination with real links, give search engines a clear map of your site's structure. Pair breadcrumbs with BreadcrumbList structured data for the best chance of rich results.

Frequently asked questions

Are the CSS-only tabs accessible?

They work with the keyboard and are fine for simple content panels. For complex applications, add the full ARIA tabs pattern with JavaScript so screen readers announce tab and panel roles.

How do I make only one accordion item open?

Give every details element the same name attribute. Current browsers then close the others when one opens, with no script.

Do these work on mobile?

Yes. Every control has a touch target of at least 40px, and the navbar collapses into a menu below 600px.