Nasaq

MobileNavKit

Mobile navigation, a bottom tab bar with badges, swipe-action list rows that also open a context menu, and a horizontally scrolling filter strip.

PreviewOpen ↗

Code

import { ArchiveIcon } from "lucide-react";import { SwipeActionRow } from "@fadymondy/nasaq/web";export function Row() {  return (    <SwipeActionRow endActions={[{ id: "archive", label: "Archive", icon: ArchiveIcon, tone: "primary", onSelect: () => {} }]}>      <div className="p-4">Design review moved to 3 PM</div>    </SwipeActionRow>  );}

Navigation · beta

Live examples and controls: MobileNavKit in the lab.

Install

npx shadcn@latest add https://docs.nasaqui.com/r/mobile-nav-kit.json

Three touch-first pieces. BottomTabBar is the thumb-reach navigation with badges. SwipeActionRow reveals actions when a row is dragged toward either inline edge. FilterStrip is a row of chips that scrolls sideways and keeps the active chip in view.

When to use

  • Phone layouts with three to five top-level destinations.
  • Lists where each row has quick actions (archive, delete, star).

When not to use

  • Desktop navigation: use the sidebar or tabs.
  • Actions that must be discoverable without a gesture: also show a button. Swipe rows already offer the same actions through a context-click menu.

Import

import { BottomTabBar, FilterStrip, SwipeActionRow } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"

Quick start

import { ArchiveIcon } from "lucide-react";
import { SwipeActionRow } from "@fadymondy/nasaq/web";

export function Row() {
  return (
    <SwipeActionRow endActions={[{ id: "archive", label: "Archive", icon: ArchiveIcon, tone: "primary", onSelect: () => {} }]}>
      <div className="p-4">Design review moved to 3 PM</div>
    </SwipeActionRow>
  );
}

Anatomy

BottomTabBar        nav data-slot="bottom-tab-bar"   items with icon, label, badge
FilterStrip         data-slot="filter-strip"         aria-pressed chips, edge fade mask
SwipeActionRow      data-slot="swipe-action-row"     start and end action panels, sliding content

API

BottomTabBar

PropTypeDefaultDescription
itemsBottomTabBarItem[]requiredvalue, label, icon, badge, href.
value / onValueChangestring / (value) => voidrequiredActive tab.
position?"sticky" | "fixed" | "static""sticky"Placement.

FilterStrip

PropTypeDefaultDescription
itemsFilterStripItem[]requiredvalue, label, icon, count.
multiple?booleanfalseSingle or multi select.
value / onValueChangestring or string[]requiredSelection.

SwipeActionRow

PropTypeDefaultDescription
startActions? / endActions?SwipeAction[][]id, label, icon, tone, onSelect.
contextMenu?booleantrueAlso offer the actions on context-click.
disabled?booleanfalseTurns the gesture off.
onOpenChange?(state) => voidnoneFires when a panel opens or closes.

The pure gesture maths (clampSwipe, settleSwipe, nextTabIndex) is in mobile-nav-math.ts with node tests.

Examples

The lab page combines all three over an inbox list.

Accessibility

Tabs use arrow keys, Home and End. Chips are aria-pressed buttons. Swipe actions are reachable from the context-click menu, so keyboard and mouse users are not left out. The gesture handles touch and pen only.

RTL & i18n

Swipe offsets are inline: "start" reveals from the inline start edge in either direction, and the pointer delta is flipped in RTL. The filter strip scrolls with RTL-aware maths. Strings via useOptionalNasaq().

Styling & tokens

Only --nq-* tokens. Action tones use the status colours.

Do / Don't

  • Do limit swipe actions to two per side.
  • Don't use a destructive action as the only way to do something.

ContextMenu, Tabs, Badge.

Lab

Pages / App / Mobile Nav Kit in the lab: Default, Arabic, Mobile.

On this page