@baukit/navigation

中文文档
0.18.3发布于 2026/11/07MITTypeScriptES Module零依赖构建来源已验证近 30 天下载 8109

Shared collapsible navigation for React web and React Native products.

原始使用文档
文档目录4 节
  1. Entries
  2. Native tokens and shell
  3. Verification
  4. Brand and accessory slots

@baukit/navigation

Collapsible navigation for React DOM and React Native. Products supply routes, copy, icon renderers and tokens. The package imports no router, icon library or Tailwind code.

Entries

  • @baukit/navigation contains the TypeScript model, validation, active-route resolution, section rotation, disclosure reducer and layout selection. It has no React runtime import.
  • @baukit/navigation/web contains AppNavigation and SectionPicker for DOM. It never imports React Native, including through a11y-core.
  • @baukit/navigation/native contains those components for React Native and React Native Web. Use AppNavigation as an Expo Router custom tabBar.
  • @baukit/navigation/web.css styles the DOM entry with --bk-* variables.
import { AppNavigation, SectionPicker } from '@baukit/navigation/web';
import '@baukit/navigation/web.css';

const progress = {
  id: 'progress',
  label: 'Progress',
  href: '/progress',
  icon: ({ active, size }) => <ChartIcon filled={active} size={size} />,
  children: [
    { id: 'overview', label: 'Overview', href: '/progress' },
    { id: 'history', label: 'History', href: '/progress/history' },
  ],
};

<AppNavigation
  label={copy.primary}
  collapseLabel={copy.collapse}
  expandLabel={copy.expand}
  closeLabel={copy.close}
  items={[progress]}
  pathname={pathname}
  onNavigate={(href) => router.navigate(href)}
  profile={{ label: 'Account', initials: 'AB', href: '/profile' }}
/>;
<SectionPicker closeLabel={copy.close} item={progress} pathname={pathname} onNavigate={navigate} />;

Declare items as readonly NavigationItem<NavigationIcon>[] to type icon parameters. Web and native entries each export NavigationIcon. Renderers receive { active, size }. The wrapper hides decorative icons from assistive technology. Include section roots in children when they belong in the cycle.

The default route match compares paths and ignores query strings and hashes. It accepts an exact path or a slash-delimited descendant. matches(pathname) replaces it. Children win over parents, and the longest matching href wins among children. Pass a custom matcher for route aliases, search parameters or hashes. A missing match selects nothing. nextSectionHref returns the first child for a missing match and wraps at the last child.

Below 1024 pixels the component renders a bottom bar with icons above labels. It supports six main items plus an optional profile. At 320 CSS pixels, the web bar has 310 pixels after 5 pixels of padding on each side. Seven 44-pixel targets need 308 pixels. Longer labels ellipsize inside their buttons; accessible labels retain the full text. At 1024 pixels it renders a 280-pixel rail. Collapse reduces it to 76 pixels. The active section starts open. Clicking a collapsed disclosure expands the rail and opens that section.

collapsed, defaultCollapsed, and onCollapsedChange support controlled and uncontrolled state. Persist the controlled value in the product's preference store. Supply collapseLabel, expandLabel, and label from the product's catalog. Web and native AppNavigation and SectionPicker also require closeLabel for the visible and accessible menu Close control. Components have no English defaults.

Web uses real anchors. onNavigate intercepts only an unmodified primary click. Without it the browser follows the href. renderLink(props) can return a router Link. Forward every supplied prop, especially href, ref, onKeyDown, onClick and ARIA attributes. Keep modified clicks in the browser. TanStack Router products can map href to to, or use onNavigate with their router instance.

Set replace on SectionPicker to replace the current history entry when a section is picked. It defaults to false. Existing callbacks keep their arguments unless replacement is requested. Web receives onNavigate(href, event, { replace: true }); native receives onNavigate(href, { replace: true }). The product's adapter must apply the option:

// Web with React Router
<SectionPicker
  closeLabel={copy.close}
  item={progress}
  pathname={pathname}
  replace
  onNavigate={(href, _event, options) => navigate(href, { replace: options?.replace ?? false })}
/>;

// Native with Expo Router, alongside the required item, labels, pathname and theme
const onNavigate = (href: string, options?: { readonly replace: boolean }) => {
  if (options?.replace === true) router.replace(href);
  else router.navigate(href);
};

Web renderLink receives replace: true for picker links when replacement is requested. Otherwise the field is omitted, so existing renderers keep their props. A router Link can apply it. Remove it before spreading props onto a DOM anchor. The default anchor renderer removes it. Modified clicks keep normal browser behavior.

A profile accepts either href or a nonempty menu. Menu entries have unique ids, labels and either href or synchronous onSelect. Set tone: 'danger' for destructive actions. Web uses color.status.danger; native uses the NavigationTheme.danger token against background. Check that pair at 4.5:1 in both themes. Native invokes callbacks after the menu closes, using iOS onDismiss or the first frame after unanimated Modal removal on Android/web. Link entries accept matches(pathname). Only the most-specific matching path is selected in a menu. A product owns async action errors. Start an async sign-out in onSelect and handle its rejection in the product. imageUrl renders an avatar, with initials after an image failure. renderAvatar({ active, size }) replaces the image or initials with a product glyph or frame on web and native. It receives the route selection state and the 28-unit avatar size. Return decorative content. The wrapper hides it from assistive technology; the profile label and subtitle still name the button. Web custom avatars have no default background or circular frame. The profile stays last in the bar and at the bottom of the rail.

Active leaves use a muted accent background with an accent icon and bold label. In the expanded rail, the parent of an active child has accent text and icon without a fill. The active icon renderer receives active: true for both rows. In the bar and collapsed rail, the parent gets the fill. Web rows expose data-active="page" or data-active="ancestor"; inactive rows omit the attribute. Override --bk-navigation-active-background, --bk-navigation-active-text and --bk-navigation-ancestor-text on .bk-navigation to match a product palette. The default background mixes 14% accent over the navigation background.

Native tokens and shell

Supply a NavigationTheme from the product's compiled ui-tokens. Do not use raw brand colors in the navigation component.

NavigationTheme fieldSemantic token
backgroundcolor.background.primary or surface
textcolor.text.primary
dangercolor.status.danger
mutedcolor.text.muted
activeBackgroundblendColors(accent, background, 0.14) from ui-tokens
activeTextcolor.background.accent
ancestorTextcolor.background.accent
bordercolor.border.primary
focuscolor.focus.ring
spacingspace.small
radiusradius.small

Use a tested contrast pair for activeText and the muted activeBackground. Check ancestorText against background too. blendColors and contrastRatio from ui-tokens check these pairs in both themes. Native changes are immediate. Web width transitions run only after reduced motion resolves and only if motion is allowed.

Set Expo Tabs' tabBarPosition to left in rail mode and bottom otherwise. Pass the safe-area insets and pathname to the custom tab bar. The component occupies layout space on native. The DOM component is fixed; reserve its width or bottom height in the content shell. NAVIGATION_DIMENSIONS exports those numbers, including 44-unit web and iOS targets and 48 dp Android targets. Match the shell's margin and width transitions to the rail, or reserve the expanded width. Enable transitions only for data-motion="standard". Include the bottom safe-area inset. The section picker belongs inside the current section's content. The DOM picker measures the space below its trigger and above the compact bottom bar. Long sections scroll inside that space. It updates the limit when the viewport, content layout, or scroll position changes.

On native, the profile menu and section picker open in a transparent modal that draws under edge-to-edge system bars. AppNavigation pads that modal by its insets plus 8 units. Pass the same insets to the native SectionPicker. Without insets, both keep the 8-unit padding.

For compact native content, useNavigationBarHeight(bottomInset, theme) reserves space using the current font scale and the bar's typography and spacing. Pass the same theme as AppNavigation when you customize it. The default height is 75 dp before the bottom inset. getNavigationBarHeight(fontScale, bottomInset, metrics) provides the same calculation without a hook. Its optional metrics are spacing, fontSize and lineHeight. NAVIGATION_DIMENSIONS.nativeBar is the default native height; bar remains the 64-pixel DOM height.

Keep route-heading focus in the product. Use createRouteFocusController from @baukit/a11y-core/web for DOM route changes, and the product's screen-transition adapter on native. Navigation does not know which heading has mounted.

The DOM stylesheet uses stable bk-navigation* classes and data-layout, data-collapsed, data-active, data-open, and data-motion attributes. Products can override these selectors in plain CSS or Tailwind.

Verification

pnpm test runs model and DOM tests, native tests with React Native Testing Library and the React Native Jest preset, and checks the packed exports. Expo is not needed for these tests. pnpm test:browser runs Playwright through Vitest in Chromium and WebKit at 320, 1023 and 1024 pixels, with short and normal heights. It checks geometry, 44-pixel targets, browser warnings and axe.

A profile can supply subtitle for a name or sync status. The expanded rail and profile menu display it below the label. The profile button includes both lines in its accessible label, including in collapsed and compact navigation.

Native NavigationTheme.typography can set fontFamily, activeFontFamily, fontSize, barFontSize, subtitleFontSize, fontWeight, activeFontWeight, lineHeight and letterSpacing. Selected entries use activeFontFamily when set and fontFamily otherwise. Set it when the font ships one file per weight, such as IBMPlexSans_600SemiBold from Expo Google Fonts, so Android renders a real semibold. Sizes and line height use React Native units. Omitted fields keep the default sizes and weights. The font family also applies to menu entries, initials and section pickers, as the web navigation inherits its CSS font family.

Brand and accessory slots

Pass renderBrand(state) for a wordmark or logo and renderAccessory(state) for status or a product control. Both receive { layout: 'bar' | 'rail', collapsed }. Return a small logo and compact status in a collapsed rail. Supply accessible names for logos and controls. Slot content keeps its own semantics.

The rail puts both slots above its controls. The compact bar puts them above the stacked icon and label targets. Web wraps the slot row when its content does not fit. Native measures the slot row and includes it in getNavigationBarHeight through NavigationBarMetrics.slotHeight. useNavigationBarHeight(bottomInset, theme, slotHeight) accepts the same height.

On both platforms, onBarHeightChange(height) reports the full compact bar height, including slots and safe areas. Store it in the content shell to reserve bottom space. Web reports rendered height with a ResizeObserver, including font changes. Native reports the height from the same calculation it uses to size the bar. Use this callback when slots can change size.

版本与发布记录

@baukit/navigation 共发布 57 个版本,首次发布于 2026/10/04;过去 12 个月发布了 57 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.18.3latest2026/11/07今天
0.18.22026/11/061 天前
0.18.12026/11/062 天前
0.18.02026/11/052 天前
0.17.12026/11/034 天前
0.17.02026/11/025 天前

关于 @baukit/navigation 的常见问题

@baukit/navigation 是什么?

Shared collapsible navigation for React web and React Native products.

如何安装 @baukit/navigation?

在项目目录运行 npm install @baukit/navigation,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(^19.2.0)、@baukit/a11y-core(^0.18.3)、@baukit/ui-tokens(^0.18.3)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@baukit/navigation 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@baukit/navigation 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@baukit/navigation 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=24.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@baukit/navigation 可以商用吗?

@baukit/navigation 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@baukit/navigation 的下载量有多少?

根据 npm 官方下载统计,@baukit/navigation 在 2026/10/08 – 2026/11/06 这 30 天内被下载 8,109 次,日均约 270 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@baukit/navigation 还在维护吗?

最新版本 0.18.3 发布于 2026/11/07(今天);至今共发布 57 个版本,其中过去 12 个月发布了 57 个;首次发布于 2026/10/04。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。