Skip to content

Menu

Navigation

Getting around — the header, the trail, the page links, the closing band.

Navigation

.k-nav is a brand on one side, links on the other, wrapping on small screens. The current page's link gets aria-current="page" — keel styles that attribute, so the highlight and the accessibility state are the same thing. The header of this page is a live example.

Live demo
Markup
<header class="k-section--alt">
  <div class="k-container">
    <nav class="k-nav" aria-label="Site">
      <a class="k-nav__brand" href="../index.html">brand</a>
      <ul class="k-nav__links">
        <li><a href="docs.html">Docs</a></li>
        <li><a href="pricing.html" aria-current="page">Pricing</a></li>
      </ul>
    </nav>
  </div>
</header>

Add .k-nav--stack and the same nav turns vertical: the links drop into a full-width column, each one a target with a leading bar. Hover tints the surface; the aria-current="page" link takes an accent-soft background and an accent leading border. It's meant for a sidebar — constrain the width and it reads as one.

Live demo
Markup
<nav class="k-nav k-nav--stack" aria-label="Sidebar">
  <ul class="k-nav__links">
    <li><a href="overview.html">Overview</a></li>
    <li><a href="settings.html" aria-current="page">Settings</a></li>
    <li><a href="members.html">Members</a></li>
  </ul>
</nav>

Sidebar nav, collapsible

.k-nav--stack handles the links; .k-nav-group groups them under a labeled, collapsible heading — a plain <details>/<summary>, so opening and closing a group needs no JavaScript at all. A .k-tool button can shrink the whole sidebar down to an icon-only rail with .k-sidebar--collapsible — that toggle is the only JavaScript in this pattern, the same one-line tolerance keel already uses for the burger and every modal on this page. This is the same sidebar keel's own docs use — try the real thing in the left column of any docs page, not just the demo below.

Markup
<div class="k-sidebar--collapsible" id="docs-sidebar">
  <button class="k-tool" type="button" aria-pressed="false"
          aria-label="Collapse sidebar to icons" id="docs-sidebar-toggle">
    <span class="k-icon k-icon--sidebar" aria-hidden="true"></span>
  </button>

  <nav class="k-nav k-nav--stack" aria-label="Sidebar">
    <details class="k-nav-group" open>
      <summary>Getting started</summary>
      <ul class="k-list k-list--flush">
        <li><a href="overview.html"><span class="k-icon k-icon--home"></span><span class="nav-label">Overview</span></a></li>
        <li><a href="settings.html" aria-current="page"><span class="k-icon k-icon--tag"></span><span class="nav-label">Settings</span></a></li>
      </ul>
    </details>
  </nav>
</div>

<script>
  // the only JS this pattern needs — same tolerance as showModal()
  document.getElementById('docs-sidebar-toggle').addEventListener('click', function () {
    var collapsed = document.getElementById('docs-sidebar').classList.toggle('is-collapsed');
    this.setAttribute('aria-pressed', collapsed);
  });
</script>

Responsive nav (burger)

keel's honest answer to the hamburger: the menu is a .k-drawer — a real modal dialog — so the focus trap, Esc, and the backdrop are the platform's, not a script's. Opening it is the one line of JavaScript on the button: showModal(). Closing needs none — a close button sits at the top, beside the "Menu" heading in a .k-cluster row, inside a form method="dialog". The trigger is a .k-btn--icon with the built-in .k-icon--menu; it's icon-only, so the aria-label is not optional — same for the close button and .k-icon--x. On wide screens you don't collapse anything clever — you simply show the links and hide the button, one utility each: .k-desktop-only on the link list, .k-mobile-only on the burger.

Also try: .k-drawer opens from the end (right in LTR); add .k-drawer--start and it opens from the start (left) instead — same dialog, one class. The toggle below swaps it live on the demo drawer.

Live demo
Markup
<nav class="k-nav" aria-label="Site">
  <a class="k-nav__brand" href="../index.html">brand</a>

  <!-- wide screens: just show the links -->
  <ul class="k-nav__links k-desktop-only">
    <li><a href="docs.html">Docs</a></li>
    <li><a href="pricing.html">Pricing</a></li>
  </ul>

  <!-- small screens: the burger, one line of JS -->
  <button class="k-btn k-btn--icon k-btn--ghost k-mobile-only" type="button"
          aria-label="Menu"
          onclick="document.getElementById('site-menu').showModal()">
    <span class="k-icon k-icon--menu" aria-hidden="true"></span>
  </button>
</nav>

<dialog class="k-drawer" id="site-menu" aria-label="Menu">
  <div class="k-cluster k-justify-between">
    <h4 class="k-mt-0 k-mb-0">Menu</h4>
    <form method="dialog">
      <button class="k-btn k-btn--ghost k-btn--icon k-btn--small" type="submit" aria-label="Close menu">
        <span class="k-icon k-icon--x" aria-hidden="true"></span>
      </button>
    </form>
  </div>
  <ul class="k-menu">
    <li><a href="docs.html">Docs</a></li>
    <li><a href="pricing.html">Pricing</a></li>
  </ul>
</dialog>

Tabs

.k-tabs is the visual tab bar — a row of links or buttons on a baseline rule, scrolling sideways if it overflows. The active tab is marked with aria-current (or aria-selected="true" in a scripted ARIA tabs widget) and gets the accent underline — the visual state is the accessibility state. Honestly: keel ships the bar, not the panel switching. Point the tabs at separate pages, use a .k-accordion instead, or write the three lines of JavaScript yourself.

Live demo
Markup
<ul class="k-tabs">
  <li><a href="overview.html" aria-current="true">Overview</a></li>
  <li><a href="activity.html">Activity</a></li>
  <li><a href="settings.html">Settings</a></li>
  <li><a href="billing.html">Billing</a></li>
</ul>

For a lighter look, .k-tabs--pill drops the baseline rule and turns each tab into a chip. The active tab — aria-current or aria-selected="true" — fills with a soft accent instead of gaining an underline; the rest tint on hover. Same markup, same accessibility state, a different shape.

Live demo
Markup
<ul class="k-tabs k-tabs--pill">
  <li><button type="button" aria-selected="true">Overview</button></li>
  <li><button type="button">Activity</button></li>
  <li><button type="button">Settings</button></li>
  <li><button type="button">Billing</button></li>
</ul>

Segmented control

.k-segmented is a compact pill track holding a few buttons (or radios); the active segment fills with a raised surface "thumb". Mark it with aria-pressed="true" on a button, aria-checked="true" on a radio, or .is-active — the visual state is the accessibility state. It's the more compact, single-choice cousin of the bordered .k-btn-group and of tabs: where .k-tabs switch page-level views, this is a small inline toggle for one setting. It's chrome only — your JavaScript (or radios in a form) tracks the selection.

Live demo
Markup
<div class="k-segmented" role="group" aria-label="View">
  <button type="button" aria-pressed="false">Day</button>
  <button type="button" aria-pressed="true">Week</button>
  <button type="button" aria-pressed="false">Month</button>
</div>

Breadcrumb

.k-breadcrumb is a list with slash separators. Mark the current page with aria-current="page" on a plain span — it renders bold instead of as a link.

Live demo
Markup
<nav aria-label="Breadcrumb">
  <ol class="k-breadcrumb">
    <li><a href="../index.html">Home</a></li>
    <li><a href="docs.html">Docs</a></li>
    <li><span aria-current="page">Components</span></li>
  </ol>
</nav>

Pagination

.k-pagination is a row of page links. The current page gets aria-current="page", which keel fills with the accent — again, the visual state is the accessibility state.

Live demo
Markup
<nav aria-label="Pagination">
  <ul class="k-pagination">
    <li><a href="?p=1">1</a></li>
    <li><a href="?p=2" aria-current="page">2</a></li>
    <li><a href="?p=3">3</a></li>
    <li><span>…</span></li>
    <li><a href="?p=9">9</a></li>
  </ul>
</nav>

Skip link

The first focusable thing on a page should let keyboard users jump past the header. .k-skip-link ships hidden off-screen above the page and slides into view on focus — pair it with <a class="k-skip-link" href="#main"> as the first child of body, and an element with id="main" to land on. This page uses it: press Tab from the top of this document to see it appear.

Markup
<body>
  <a class="k-skip-link" href="#main">Skip to content</a>
  …
  <main id="main">…</main>