An on-screen keyboard is a keyboard the page draws: a grid of keys at the foot of the window
that types into whichever field has focus. You reach for one where the platform's keyboard is
not there to be had, a kiosk, a touch panel, a till, or where the keys should follow the
product's theme rather than the operating system's.

It is a provider's keyboard, not a global mode: a field opens it only while a `KeyboardProvider`
sits above it, and with no provider the field behaves exactly as it otherwise would. Under a
provider every field is wired by default, `enableVirtualKeyboard` defaults to `true`, so a page
opts in once and switches individual fields out only when it needs to.

```tsx demo="system/keyboard-preview"
```

## Usage

```tsx
import { Input, KeyboardProvider } from "@asheeui/web";

<KeyboardProvider>
  <Input label="Recipient" enableVirtualKeyboard />
</KeyboardProvider>
```

The provider holds the state, whether the keyboard is open, which element it is typing into,
which layout is showing, and renders the keyboard itself, so a page never mounts one by hand.
A field opens it on focus and requests a close on blur, which the provider honours after
`closeDelay` so that moving between two fields does not make the keyboard flicker. A blur whose
new focus is inside the keyboard is not a close: that is a reader moving to the keys, not leaving
the field.

It reads the application's configuration, so it sits inside `AsheeUIProvider` like every other
component. One provider around a form, a screen or the whole application is enough; it costs
nothing until a field in its tree is focused, and fields outside its tree are untouched. A nested
provider owns the keyboard for its own subtree, which is how a page gives a group of fields a
different set of keys or a different treatment without restating the application's configuration.

## Layouts and keys

Four layouts ship with the component: `default` (lowercase alphabet and digits), `shift`
(uppercase), `symbols` (punctuation) and `numeric` (a keypad). A layout is a list of rows, and a
row is a space-separated list of tokens, so the `{space}` token is how a row states its space bar.
The provider's `defaultLayout` names the one that opens for a field that does not ask for another.

```tsx demo="system/keyboard-layouts"
```

Stating `layouts` **replaces** the set rather than extending it, so a custom layout is written
beside the ones it still needs, in the demo, `...defaultKeyboardLayouts` keeps the alphabet,
shift and symbols keys available while `code` adds a keypad. A layout name is a free string: the
four standard names have meaning to the component, and anything else is simply a layout you
supply.

A field can name the layout it opens: `enableVirtualKeyboard={{ layout: "numeric" }}` gives that
field a keypad while the rest of the form keeps the provider's alphabet. The layout goes back to
the provider's default when the keyboard closes, so the next field starts from the provider's
choice, and refocusing a field whose keyboard is already open keeps whatever layout is showing. A
group of fields that wants another set of keys rather than another layout still states it on a
nested provider, which restates the whole keyboard for its subtree.

A token is either one of the named keys or literal text, a single character, usually, though
nothing stops a token being a whole word. The named keys carry behaviour:

| Token | What the key does | Default label |
| --- | --- | --- |
| `{space}` | Inserts a space | `space` |
| `{bksp}` | Deletes backwards from the caret, or the selection | `⌫` |
| `{enter}` | Blurs a single-line field; inserts a line break in a textarea | `↵` |
| `{shift}` | Switches to the `shift` layout, and back | `⇧` |
| `{symbols}` | Switches to the `symbols` layout | `?123` |
| `{abc}` | Returns to the default layout | `ABC` |
| `{clear}` | Empties the field | `C` |

`autoShiftBack` returns to the default layout after one shifted character, so a reader who wants
a capital does not have to press <kbd>⇧</kbd> twice. `display` replaces the label a token is drawn
with, an icon, an abbreviation, a word in another language, and defaults to the glyphs in the
table. What a screen reader says is the other half of the label: a named key announces its action
however it is drawn, and the Accessibility section has the names.

## Wiring a field

`Input` and `Textarea` each take `enableVirtualKeyboard`, which is `true` by default and only
means anything under a provider. Passing an object instead of `true` states that field's own
`layout`, `size`, `variant`, `color`, `radius` and `portal`, so the keyboard can take the numeric
layout and the large keys on one field without restating the provider around it.

```tsx demo="system/keyboard-options"
```

The provider's props and a field's options are the two levels a keyboard's look is stated at; the
field's options win for that field, and the provider's values, which themselves resolve through
the application's configuration, stand everywhere else. Keys are drawn with the framework's
`Button`, so a keyboard takes the same treatments a button does. `className` reaches the
keyboard's container and `keyClassName` every key.

A field that is not one of the family connects through `useKeyboardField(id, ref, enabled,
options)`, which returns `handleFocus`, `handleBlur` and `hasProvider` to pass to any
input-like element. `hasProvider` says whether a provider sits above at all, the difference
between a field that could open a keyboard and one that cannot, so a custom field can draw its
own affordance only where a keyboard exists:

```tsx
import { useKeyboardField } from "@asheeui/web";
import { useRef } from "react";

function Nickname() {
  const ref = useRef<HTMLInputElement>(null);
  const { handleFocus, handleBlur } = useKeyboardField("nickname", ref, true, {
    size: "lg",
  });

  return (
    <input
      ref={ref}
      aria-label="Nickname"
      onFocus={handleFocus}
      onBlur={handleBlur}
    />
  );
}
```

The keyboard writes through the element it is attached to: it sets the value, dispatches the
event a controlled field listens for, and restores the caret. So a field that keeps its value in
state updates normally, and typing into the middle of a value inserts there rather than at the
end.

## Props

```props KeyboardProvider
```

`layouts`, `display`, `defaultLayout`, `closeDelay`, `autoShiftBack`, `size`, `portal` and
`disabled` are registered under `components.keyboard`, so a configuration file can state the
keyboard's habits once. `variant`, `color` and `radius` are deliberately **not** registered: they
resolve from the application's `defaultVariant`, `defaultColor` and `defaultRadius` and then the
framework's own fallbacks, which is what keeps the keys the same accent as the rest of the page
without restating it.

`closeDelay` resolves through the cascade like the rest: the instance, then
`components.keyboard.closeDelay`, then the component's registered `500` ms. It is the one value
worth stating in configuration rather than per provider, because a product's fields should all
wait the same length of time before the keyboard goes.

```props OnScreenKeyboard
```

`OnScreenKeyboard` is what the provider renders, and its props are the provider's state passed
down: whether it is open, the element it is typing into, the resolved configuration and the
resolved size, variant, colour and rounding. Its documentation marks it internal: the package
exports it because the provider is built from it, and mounting one by hand gives you keys with no
provider behind them, so mount a provider instead.

## Accessibility

- The keyboard is a `<section aria-label="Virtual Keyboard">`, so it is announced as a region
  named "Virtual Keyboard", and every key is a real button. A letter, digit or punctuation key
  takes its name from the label it draws. A named key announces the **action** it performs,
  Backspace, Enter, Shift, Symbols, Letters, Space and Clear, while the button draws the glyph
  (`⌫`, `↵`, `⇧`, `?123`, `ABC`, `C`), so a screen reader says what the key does rather than
  reading a symbol. For a named key `display` changes the drawing alone, not the spoken name.
- A press on the pointer is cancelled by the container, so a pointer reader's field keeps focus
  and the character lands at the caret. A key pressed from the keyboard leaves focus on that key
  instead of pulling it back to the field, which is where a reader who arrived by tab expects to
  stay. While the keyboard is open, keys typed on a physical keyboard are echoed as highlights,
  and <kbd>Escape</kbd> closes it.
- The keys are ordinary buttons and therefore in the tab order, and tabbing into them keeps the
  keyboard open: a blur whose new focus is inside the keyboard is ignored rather than treated as
  a close. The arrow keys walk the grid, left and right along a row, up and down between rows,
  because a grid of forty keys would otherwise cost a tab for each one, and <kbd>Enter</kbd> or
  <kbd>Space</kbd> presses the key that has focus. A reader who would rather type on the keyboard
  they already have is served by the field itself: `disabled` on the provider, or
  `enableVirtualKeyboard={false}` on one field, leaves it typing with a real keyboard and nothing
  else.
- `disabled` is checked both when a field opens the keyboard and when a key is pressed, so it
  prevents a new one. Setting it while the keyboard is open closes that keyboard at once and
  forgets the field it belonged to, so clearing `disabled` does not bring one back until a field
  is focused again, the setting a page uses when the operating system already provides a
  keyboard.

## Native

The platform provides its own keyboard, so `@asheeui/native` ships none and there is nothing to
import: an on-screen keyboard exists to compensate for a browser constraint, and a device has
already solved it. The compatibility matrix records the pair as not applicable rather than as a
difference in behaviour; see [React Native](/platforms/native).

## Related

```related input, textarea, form
```
