Skip to content

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.

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.

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>

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 — the close button sits in 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. 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.

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">
  <h4>Menu</h4>
  <ul class="k-menu">
    <li><a href="docs.html">Docs</a></li>
    <li><a href="pricing.html">Pricing</a></li>
  </ul>
  <form method="dialog">
    <button class="k-btn k-btn--ghost">Close</button>
  </form>
</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.

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.

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.

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.

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.

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: <a href="#main">Skip to content</a> as the first child of body. Honestly: .k-visually-hidden alone keeps it hidden even on focus — which defeats the point. Either pair it with a three-line :focus reveal of your own (the recommended pattern below), or skip the hiding entirely and put the link visibly in a .k-banner.

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

<style>
  /* your CSS — reveal it when it's focused */
  .skip-link:focus {
    position: fixed; inset: var(--k-space-2) auto auto var(--k-space-2);
    inline-size: auto; block-size: auto; margin: 0; clip-path: none;
  }
</style>