# @basenative/keyboard 中文文档

规范地址：https://npmcn.com/package/%40basenative/keyboard/

版本：3\.0\.0
npm 元数据更新时间：2026-10-31T13:48:07.160Z
中文译文更新时间：2026-10-31T16:10:38.274Z
软件包许可：Apache-2\.0

本文由上游 README 机器翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40basenative/keyboard
原文对照：https://npmcn.com/package/%40basenative/keyboard/?lang=original
项目主页：https://github.com/DuganLabs/BaseNative/tree/main/packages/keyboard#readme
源码仓库：https://github.com/DuganLabs/BaseNative

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：38.6 kB，9 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：0 个
- peer 依赖：@basenative/runtime 3\.0\.0
- 首次发布：2026-10-18T05:29:26.370Z
- 最新版本发布：2026-10-31T13:48:06.824Z
- 已发布版本数：11（近 12 个月 11 个）

## 中文简介

可访问的移动-第一个屏幕上的虚拟键盘原语-布局不可知，信号驱动的键状态着色，主题

## 安装

```sh
npm install @basenative/keyboard
```

也可以使用：`pnpm add @basenative/keyboard`、`yarn add @basenative/keyboard`、`bun add @basenative/keyboard`

## README

# @ basenative/键盘

> 可访问的，移动优先的屏幕虚拟键盘原始-布局无关，信号驱动，主题。

部分 [BaseNative](https://github.com/DuganLabs/basenative) 生态系统。

专为益智游戏，kiosk应用程序，可访问的输入，以及任何时候你需要一个屏幕上的键盘，不与移动Safari战斗。

## 为什么

移动浏览器对键盘有很多看法。本机不请自来地弹出，焦点跳来跳去，命中目标太小，动画中断了要求他们不要的用户。这个包是一个单一的原语:

-   呈现语义 `<button>` 元素 (作品没有JS-SSR预画友好)。
-   通过信号感知处理程序调度轻击，使用硬件键镜像，因此蓝牙键盘会经过完全相同的调度路径。
-   从应用每个键的状态课程 `state` getter (`green` / `yellow` / `absent` / `staked`) -完美的文字风格的游戏或任何反馈驱动的UI。
-   主题贯穿始终 `--kb-*` CSS自定义属性。没有捆绑的调色板假设。
-   是否有44pt min命中目标默认（WCAG 2.5.5 AA），默认调色板WCAG AA对比每个状态状态，和荣誉 `prefers-reduced-motion`.
-   修复了mobile-Safari focus-steal bug (更多内容见下文)。

## 安装

```bash
pnpm add @basenative/keyboard @basenative/runtime
```

## 快速开始-游戏键盘

```js
import { Keyboard, LAYOUTS } from '@basenative/keyboard';
import { signal, effect } from '@basenative/runtime';
import '@basenative/keyboard/css';

const keyStatus = signal({ A: 'green', E: 'yellow', X: 'absent' });

const kb = Keyboard({
  layout: LAYOUTS.qwerty,
  primary: 'ENTER',
  state: keyStatus.get,
  runtime: { effect },
  onKey:    (char) => guessSignal.update(g => g + char),
  onAction: (a) => {
    if (a === 'ENTER') submitGuess();
    if (a === 'BACKSPACE') guessSignal.update(g => g.slice(0, -1));
  },
});

document.querySelector('#kb-mount').innerHTML = kb.html;
kb.hydrate(document.querySelector('[data-bn="keyboard"]'));
```

## 数字键盘

```js
import { Keyboard } from '@basenative/keyboard';

const pad = Keyboard({
  layout: 'numpad',
  primary: 'ENTER',
  onKey: (digit) => pin.update(s => s + digit),
  onAction: (a) => a === 'BACKSPACE' && pin.update(s => s.slice(0, -1)),
});
```

## 自定义布局

```js
import { defineLayout, Keyboard } from '@basenative/keyboard';

const dvorak = defineLayout([
  ['1','2','3','4','5','6','7','8','9','0'],
  ["'", ',', '.', 'P', 'Y', 'F', 'G', 'C', 'R', 'L'],
  ['A', 'O', 'E', 'U', 'I', 'D', 'H', 'T', 'N', 'S'],
  [
    { type: 'action', label: '⌫', key: 'BACKSPACE', span: 1.5, variant: 'backspace' },
    ';', 'Q', 'J', 'K', 'X', 'B', 'M', 'W', 'V', 'Z',
    { type: 'action', label: 'GO', key: 'ENTER', span: 1.5, variant: 'enter' },
  ],
]);

const kb = Keyboard({ layout: dvorak, primary: 'ENTER', onKey, onAction });
```

## 主题

所有视觉对象都是CSS自定义属性。覆盖任何祖先:

```css
.my-app [data-bn="keyboard"] {
  --kb-bg: #fff;
  --kb-key-bg: #f4f4f5;
  --kb-key-fg: #18181b;
  --kb-key-active: #d4d4d8;
  --kb-key-green:  #16a34a;
  --kb-key-yellow: #eab308;
  --kb-key-absent: #d4d4d8;
  --kb-key-radius: 8px;
  --kb-min-tap: 48px;
}
```

| 令牌 | 目的 | 默认的 |
| --- | --- | --- |
| `--kb-bg` | 键盘表面背景 | `#0C0B09` |
| `--kb-key-bg` | 默认键背景 | `#3A3530` |
| `--kb-key-fg` | 默认键文本 | `#F0EDE4` |
| `--kb-key-active` | 按下的背景 | `#5A5550` |
| `--kb-key-green` | 状态: 正确的位置 | `#2E8B5A` |
| `--kb-key-yellow` | 状态：正确的字母，错误的位置 | `#D4B445` |
| `--kb-key-absent` | 状态: 不在答案中 | `#1E1C18` |
| `--kb-key-radius` | 每键角半径 | `6px` |
| `--kb-gap` | 键间水平间隙 | `4px` |
| `--kb-min-tap` | 最小命中目标（WCAG 2.5.5 AA） | `44px` |

## 移动Safari焦点修复

当你点击一个键时，移动Safari会将焦点短暂地移到 `<button>`,这会模糊活动的文本输入。这种模糊会导致Safari关闭键盘-在下一个焦点上，它会引发 _本地的_ 键盘在我们旁边。解决办法是打电话 `preventDefault` 在 `mousedown` / `touchstart` 所以注意力永远不会移动。

`hydrateKeyboard` 自动执行此操作。如果你需要钥匙的话 _做_ 取焦点（很少-通常是通过本机语义提交表单的键），设置 `data-bn-kb-focusable="true"` 在按钮上。

## 硬件键盘奇偶校验

当 `bindHardware` 是 `true` (默认), `keydown` 事件 `document` 被翻译成相同的 `onKey` / `onAction` 呼叫为轻敲。因此，蓝牙键盘或桌面用户获得相同的行为-没有单独的输入管道。

要限制硬件侦听器转发的字符，请传递 `charSet` 通过较低的水平 `bindHardwareKeys(...)` API。

## 移动测试笔记

-   iOS 16/17 Safari (iPhone 12, 14 Pro，SE 3rd gen): 点击dispatch，无本机键盘弹出窗口，haptics fire。
-   Android 13/14 Chrome（像素6,7）：点击调度， `navigator.vibrate(8)` 火灾，命中目标通过灯塔a11y审计。
-   减少运动：测试与iOS“减少运动”和 `prefers-reduced-motion: reduce` 在DevTools中-按比例动画下降到平面颜色变化。

## API

```ts
import {
  Keyboard,            // factory: { html, hydrate(rootEl) }
  renderKeyboard,      // SSR: returns HTML string
  hydrateKeyboard,     // wire reactivity to an existing root
  LAYOUTS,             // { qwerty, alphanumeric, numpad, phone }
  defineLayout,        // (rows, meta?) => Layout
  validateLayout,      // throw on shape mismatch
  keyState,            // single-letter status reader
  applyAriaAttributes,
  preventFocusSteal,
  bindHardwareKeys,
  haptic,
} from '@basenative/keyboard';
```

看到 `types/index.d.ts` 完整签名。

## 许可证

apache - 2.0

## 常见问题

### @basenative/keyboard 是什么？

可访问的移动-第一个屏幕上的虚拟键盘原语-布局不可知，信号驱动的键状态着色，主题

### 如何安装 @basenative/keyboard？

在项目目录运行 npm install @basenative/keyboard，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：@basenative/runtime（3\.0\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @basenative/keyboard 支持 TypeScript 吗？

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

### @basenative/keyboard 支持 ESM 还是 CommonJS？

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

### @basenative/keyboard 可以商用吗？

@basenative/keyboard 使用 Apache-2\.0 许可。宽松许可：允许商业使用、修改和再分发，并附带明确的专利授权；分发时需保留许可与 NOTICE 文件，修改过的文件要注明改动。以上为概括，不构成法律意见，请以许可原文为准。

### @basenative/keyboard 还在维护吗？

最新版本 3\.0\.0 发布于 2026/10/31（2 天前）；至今共发布 11 个版本，其中过去 12 个月发布了 11 个；首次发布于 2026/10/18。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

