The keyboard.

A till or a tablet has no keyboard. Arda.keyboard() opens one over the page and resolves with what was typed. Your app opens it: nothing watches for a focused input, on purpose — a keyboard that guesses covers the wrong thing.

Working — describes ArdaPlayer v1.2.3

Asking for a value

app/app.js
// One value: resolves the string, or null if they cancelled
Arda.keyboard({ layout: 'full', title: 'Customer name', value: '' }).then(function (name) {
  if (name === null) return;          // cancelled: always check this first
  setCustomer(name);
});

// Several named fields: resolves an object keyed by name
Arda.keyboard({
  fields: [
    { name: 'user', caption: 'User name', flex: 2 },
    { name: 'role', caption: 'Role' }
  ]
}).then(function (r) {
  if (r === null) return;
  console.log(r.user, r.role);
});
The personThe promise resolves
cancelsnull, in every layout
submits, and you passed fieldsan object keyed by each field's name
submits, without fieldsthe string typed
  • Which of the two you get depends on what you asked with, not on how many fields there are.
  • It rejects (it does not resolve) when it cannot open: in a headless app, or for a screen the device does not have.
  • One keyboard at a time per screen. Opening another cancels the first; its promise resolves null.

Layouts

layoutWhat it is
'full'letters, digits, symbols, caps, shift, tab, clear, space. The default when you pass fields.
'numeric'a 4×4 pad: digits, ., backspace, clear, tab, cancel, submit
'pin'centred on the screen, one masked field, digits, backspace, cancel and a tall submit. The default with no fields.
'date', 'time', 'datetime'pickers (below)
  • Tab and Enter move to the next field, wrapping. Submit is its own key: nothing submits by accident. Touching a field selects it.
  • Clicking outside the keyboard does nothing unless you pass closeOnOutside: true — an operator's sleeve must not throw away what they typed.
  • Only a PIN listens to a physical keyboard. On a machine with a keyboard, full and numeric ignore it (otherwise it types in two places at once). On pin, digits type, Backspace deletes, Enter submits and Escape cancels — a manager's override at a staffed till.
app/app.js
Arda.keyboard({ layout: 'pin', title: 'Manager PIN' }).then(function (pin) {
  if (pin === null) return;
  if (!checkPin(pin)) showRefused();
});

Fields

Field keyMeaning
namethe key in the result object
captionthe label on the field
valuethe starting value
flexits share of the row; 1 unless you say otherwise
secretdots instead of characters (on by default for a PIN)
maska typing pattern (below)
prefix, suffixdisplay only: a currency sign, a unit. Never in the value.
decimals, posnumbers: a decimal limit enforced while typing; till entry (below)

The value you get back is exactly what the field shows, minus the prefix and the suffix. An untouched field resolves '', not '0.00': "they said nothing" and "they said zero" are different answers.

Masks

app/app.js
Arda.keyboard({
  layout: 'numeric',
  fields: [
    { name: 'price', caption: 'Price each', prefix: '£', suffix: ' /kg' },
    { name: 'date', caption: 'Collect on', mask: '00/00/0000' },
    { name: 'phone', caption: 'Phone', mask: '00000 000000' }
  ]
}).then(function (r) {
  if (r === null) return;
  // r.price === '4.50'        the £ and the /kg are not in it
  // r.date  === '12/03/2026'  the slashes are; digits only: r.date.replace(/\D/g, '')
});
  • 0 one digit, A one letter, * either, anything else a literal inserted as you reach it. \0 is a literal zero (written '\\0' in a JavaScript string).
  • A character the mask cannot hold is dropped silently; a full mask stops accepting.
  • Pass either form as the starting value: '12032026' and '12/03/2026' mean the same.
  • secret wins over a mask: dots, not the format.

Till entry: pos and decimals

app/app.js
Arda.keyboard({
  layout: 'numeric',
  fields: [{ name: 'amount', caption: 'Amount', prefix: '£', decimals: 2, pos: true }]
}).then(function (r) {
  if (r === null || r.amount === '') return;
  takePayment(Math.round(Number(r.amount) * 100));   // pence
});

pos: true is the till's way of typing money: no decimal point to press, digits shift in from the right (0.00 → 3 → 0.03 → 5 → 0.35 → 0 → 3.50). decimals limits the places while typing, rather than correcting them on submit. The exact string a pos field resolves was not run in a browser here; check it once on your screen before you store it.

Following along: onpress

app/app.js
Arda.keyboard({
  fields: [{ name: 'note', caption: 'Note' }],
  onpress: function (e) {
    // e = {key, field, value} for every press, characters and actions alike
    preview.textContent = e.value;
  }
});

When the person cancels, the keyboard replays each changed field's original value through onpress, so an app that copies e.value into its state unwinds by itself. A throw inside onpress is logged and the keyboard carries on.

Date and time pickers

app/app.js
Arda.keyboard({ layout: 'date', title: 'Collect on', min: '2026-11-01', max: '2026-12-31' })
  .then(function (iso) {
    if (iso === null) return;              // cancelled
    if (iso === '') return clearDate();    // 'None' was pressed (only with none: true)
    var d = new Date(iso + 'T00:00');      // local midnight; new Date('2026-11-14') would be UTC
    label.textContent = d.toLocaleDateString(undefined, { weekday: 'short', day: 'numeric', month: 'short' });
  });
layoutResolves
'date''2026-03-12'
'time''14:30'
'datetime''2026-03-12T14:30'
  • A picker shows the date the way a person reads it and resolves it machine-readable. It is the one place where "what it shows is what you get" does not hold.
  • Options: value (ISO in, the same ISO out), title, min, max, minuteStep (default 5), weekStart (1 Monday, the default; 0 Sunday), none: true (a None key that resolves '' — different from null, which is cancel).
  • A picker ignores fields. On datetime, the Date and Time fields switch the pad.
  • Days outside min/max stay in the grid, dimmed, and cannot be pressed.

On another screen

app/app.js
Arda.screens.ready().then(function () {
  return Arda.keyboard({ layout: 'pin', screen: 'customer' });
}).then(function (pin) { if (pin !== null) verify(pin); },
        function (e) { console.warn(e.message); });   // no screen of that name

Styling, and the CSS traps

app/app.js
Arda.keyboard({
  layout: 'numeric',
  theme: { accent: '#00b3ff', key: '#3a4a5a', keySpecial: '#26313b', radius: '6px', unit: '20px' },
  css: 'key { font-weight: 600 }'
});
  • The keyboard lives in a shadow root. Your page's stylesheet cannot reach it, and its styles cannot reach your page. .my-app key { … } in your CSS does nothing.
  • theme is a closed list: accent, bg, key, keyAlt, keySpecial, keyText, fieldBg, fieldText, fieldTextOn, backdrop, radius, font, keyHeight, keyGap, unit. Any other name is refused with a console warning listing the right ones.
  • css is added after the theme, inside the shadow root, so it wins. The elements are custom names: keyboard, keys, keyrow, key, fields, field, fieldval. If you set the keyboard's custom properties from css, set them on the keyboard element: a value set on :host loses to the defaults declared on keyboard, with no error.
  • Size follows the screen, not your page (since v1.1.12): about 16px per unit on a 1366×768 till, 22px on 1920×1200. Your html { font-size } does not shrink it. theme.unit scales the whole keyboard; theme.keyHeight moves only the keys.
  • keyGap is in pixels and should stay a whole number: a gap exists to stop two keys being touched at once.
  • The panel is opaque and has a backdrop, so the keyboard looks the same whatever your page draws behind it. bg and backdrop change both.

Checked: every option and theme name is from ArdaJS() and keyboard.css in the player source; the reject in a headless app was observed ("there is no screen on a headless device"), and the calls are shaped like those of apps running on devices today. The keyboard is drawn only in a window, so the drawn behaviour (masks, pos, pickers) was not driven by a test here; it is as the player's manual describes it.