mica
mica

Example

Persistent destinations below a scrolling page. The icons and unread badge belong to this example; Mica does not supply an icon set.

Open the full viewport example

Usage

Use a named native nav with direct links. Mark the current destination with aria-current="page". No script or custom element registration is needed.

<nav data-bottom-nav aria-label="Primary">
  <a href="/home">Home</a>
  <a href="/projects" aria-current="page">Projects</a>
  <a href="/inbox">Inbox</a>
  <a href="/settings">Settings</a>
</nav>

Links receive equal width. Text-only navigation uses a compact target height; adding data-bottom-icon opts the whole bar into taller icon-and-label sizing. Keep labels short and visible.

Optional icons and badges

<a href="/inbox" aria-label="Inbox, 3 unread">
  <svg data-bottom-icon aria-hidden="true" viewBox="0 0 24 24">
    <!-- Your icon paths -->
  </svg>
  <span>Inbox <span aria-hidden="true">3</span></span>
</a>

data-bottom-icon only sizes your icon. It does not inject artwork. Badge layout is authored content; include its meaning in the link’s accessible name. Use a consistent icon treatment across the bar.

Keeping the bar at the bottom

The component stays in normal flow. Give it the last row of a viewport-height shell, with a scrolling main region. This reserves its actual height, including safe-area padding and wrapped labels.

<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<div class="app">
  <header></header>
  <main></main>
  <nav data-bottom-nav aria-label="Primary"></nav>
</div>

<style>
.app {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  block-size: 100svh;
  block-size: 100dvh;
}
.app > main {
  min-block-size: 0;
  overflow: auto;
}
</style>

Mica adds bottom and side safe-area padding. The app chooses its responsive breakpoint and whether a header or sidebar replaces this navigation on larger screens. The bar does not hide itself, fix itself to the viewport, or manage the software keyboard.

States and behavior

  • Current destination: aria-current="page" gives a quiet background, stronger text, and no link border.
  • Unavailable destination: remove href and add aria-disabled="true". The attribute alone does not stop a native link from navigating.
  • Keyboard: normal Tab/Shift+Tab and Enter, with the shared focus ring. These are navigation links, not tabs; do not add tab roles or arrow-key behavior.
  • Navigation and focus after a route change belong to the browser or your router. The example’s script demonstrates routing and is not part of the library.

API and tokens

PartContractDescription
data-bottom-navOn a native navDirect children are native links. Supply a navigation label.
data-bottom-iconOptional, inside a linkSizes an authored icon and selects icon-and-label spacing for the bar.
TokenDefaultDescription
--bottom-nav-pad--space-xsBar padding, increased to accommodate safe areas.
--bottom-nav-gap--space-2xsGap between links and between icon and label.
--bottom-nav-target-height3.5rem / 2.75remMinimum link height with icons / text only.
--bottom-nav-font-size.75rem / .8125remLabel size with icons / text only.
--bottom-nav-font-weight500Default label weight.
--bottom-nav-active-weight650Current destination weight.
--bottom-nav-icon-size1.125remAuthored icon width and height.
--bottom-nav-active-background--color-surface-raisedCurrent destination fill. Set to transparent for emphasis without fill.

Colors and radius follow Mica’s semantic tokens. Custom properties set on the nav override the defaults, including automatic text-only sizing.

← paginationsidebar →