Single-file virtual keyboard for web projects. It renders inside a shadow root, stays fixed to the bottom of the viewport, supports an optional HUD above the keys, and exposes a small JavaScript API.
codexkeyboard.js: reusable keyboard libraryindex.html: demo page
<div id="keyboardMount"></div>
<script src="./codexkeyboard.js"></script>
<script>
const myTheme = {
enterColor: "#c11",
textFontSize: "1.35rem",
textFontWeight: "800",
textUppercase: true,
outputBackground: "#1f1f1f",
outputBorderColor: "#5f5f5f",
outputBorderThickness: "1px",
keyLabelTranslateY: "-0.08em"
};
const keyboard = new CodexKeyboard({
mount: "#keyboardMount",
layouts: { ...CodexKeyboard.DEFAULT_LAYOUTS },
theme: myTheme,
layout: "qw",
slide: true,
numbers: true,
hud: true,
output: "hud",
physicalKeyboard: true,
hideOnOutsideClick: false,
hideOnOutsideClickDelay: 180,
outputAlign: "center",
value: "",
hidden: false,
disabledKeys: [],
marginBottom: 0,
paddingBottom: 10,
onKey: (key, detail) => {
console.log(key, detail.value);
}
});
</script>Themes should be defined in consumer code and passed through theme or updated later with setTheme(theme).
To use a custom font file, load it in the page first with @font-face, then reference the font family name in theme.textFont. Do not set textFont to the .ttf filename directly.
<style>
@font-face {
font-family: "agency";
src: url("./AGENCYB.TTF") format("truetype");
font-weight: normal;
font-style: normal;
}
</style>
<script>
const keyboard = new CodexKeyboard({
mount: "#keyboardMount",
theme: {
textFont: '"agency", Arial, Helvetica, sans-serif',
textFontSize: "1.35rem",
textFontWeight: "800",
textUppercase: true,
outputTextSize: "1.1rem",
outputLineHeight: 1.3
}
});
</script>const keyboard = new CodexKeyboard({
mount: "#keyboardMount",
onKey: (key, detail) => {},
layouts: {
qw: [
["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"],
["a", "s", "d", "f", "g", "h", "j", "k", "l"],
["{backspace}", "z", "x", "c", "v", "b", "n", "m", "{enter}"]
]
},
theme: {
backColor: "#181818",
keyColor: "#f0eeee",
keyTextColor: "#10131a",
textFont: '"agency", Arial, Helvetica, sans-serif',
textFontSize: "1.6rem",
textFontWeight: "700",
textUppercase: true,
enterColor: "#9acd32",
enterTextColor: "#ffffff",
backspaceColor: "#727272",
backspaceTextColor: "#111111",
hudBackground: "#181818",
hudBorderColor: "grey",
hudMinHeight: "60px",
outputTextColor: null,
outputTextSize: null,
outputTextWeight: null,
outputBackground: "rgba(255, 255, 255, 0.08)",
outputBorderColor: "transparent",
outputBorderThickness: 0,
outputLineHeight: "1.4",
keyLabelTranslateY: 0
},
layout: "qw",
slide: true,
numbers: false,
hud: true,
output: null, // null | "hud" | selector | element
physicalKeyboard: false,
hideOnOutsideClick: false,
hideOnOutsideClickDelay: 150,
outputAlign: "center", // "left" | "center" | "right"
value: "",
hidden: false,
disabledKeys: [],
marginBottom: 0, // or "margin-bottom"
paddingBottom: 10 // or "padding-bottom"
});mount: required. DOM element or selector for the host element.layout: initial layout name. Built-in options are"qw"and"az". Default:"qw".slide: whentrue,show()andhide()animate from the bottom. Default:true.numbers: whentrue, prepends a1to0number row. Default:false.hud: whenfalse, removes the HUD area unlessoutput: "hud"is used. Default:true.output: output target for the keyboard value.outputvalues:null: no output syncing"hud": create a built-in output element inside the HUD selector string: sync to the matched element DOM element: sync directly to that elementoutputAlign: text alignment for the synced output. Supported values:"left","center","right". Default:"center".physicalKeyboard: whentrue, listens for physicalkeydownevents while the keyboard is visible. Ignores modifier shortcuts and common editable or interactive elements outside the keyboard. Default:false.hideOnOutsideClick: whentrue, hides the keyboard when the user clicks or taps outside it while visible. Default:false.hideOnOutsideClickDelay: grace period in milliseconds before outside-click dismissal becomes active after the keyboard is shown. Default:150.value: initial keyboard value. Default:"".hidden: start hidden. Default:false.disabledKeys: array of disabled keys such as["q", "{enter}"]. Default:[]."margin-bottom"/marginBottom: fixed bottom offset for the whole keyboard. Default:0."padding-bottom"/paddingBottom: inner bottom spacing inside the keyboard dock. Default:10.onKey(key, detail): callback fired after every key press.layouts: optional layout map to extend or replace built-in layouts.theme: optional partial theme object.
theme.textFont must be a CSS font-family value. If you want to use a local .ttf file, load it with @font-face in your page first.
theme.textFontSize accepts CSS length values such as "24px", "1.6rem", or numeric pixel values like 24.
theme.textFontWeight accepts CSS font-weight values such as "400", "700", "bold", or numeric values like 800.
theme.textUppercase controls display casing for key labels and synced output. Default: true. This changes presentation only, not the underlying typed value.
theme.outputTextColor, theme.outputTextSize, and theme.outputTextWeight style the built-in output: "hud" box. If omitted, they inherit keyTextColor, textFontSize, and textFontWeight.
theme.outputBackground sets the built-in output box background. Default: "rgba(255, 255, 255, 0.08)".
theme.outputBorderColor and theme.outputBorderThickness control the built-in output box border. Default: transparent 0.
theme.outputLineHeight accepts unitless numbers such as 1.4 or CSS values such as "24px" or "1.6".
theme.keyLabelTranslateY offsets key labels vertically. Default: 0. Numeric values are pixels; strings are used as-is.
Numeric margin-bottom and padding-bottom values are treated as pixels. String values such as "12px", "5%", or "2rem" are used as-is.
"qw": qwerty-style"az": azerty-style"num": numpad-style"num2": numeric on 2 lines -style
Special keys:
{backspace}{enter}- Regular key width is based on the widest row in the active layout, so compact layouts like a numpad expand to fill more horizontal space while
qwandazkeep their existing sizing.
- Letter and number keys append to the current value.
{backspace}removes the last character.{enter}does not add a newline.- Pressing
{enter}dispatches a validation event with the current value. - With
physicalKeyboard: true, matching physical keys trigger the same behavior as clicking the on-screen keys. - With
hideOnOutsideClick: true, outside pointer interactions act as a cancel/dismiss gesture. hideOnOutsideClickDelayhelps avoid the opening click immediately dismissing the keyboard.
onKey receives two arguments:
onKey: (key, detail) => {}key: the raw key value that was presseddetail: metadata objectdetail.key: pressed keydetail.layout: current layout namedetail.keyboard: theCodexKeyboardinstancedetail.previousValue: value before the key pressdetail.value: value after the key press
Example:
const keyboard = new CodexKeyboard({
mount: "#keyboardMount",
onKey: (key, detail) => {
console.log("key:", key);
console.log("before:", detail.previousValue);
console.log("after:", detail.value);
}
});The mount element dispatches these custom events:
codexkey: fired on every key presscodexvalidate: fired when{enter}is pressed
document.getElementById("keyboardMount").addEventListener("codexkey", (event) => {
console.log(event.detail.key);
console.log(event.detail.value);
});event.detail contains:
keylayoutkeyboardpreviousValuevalue
document.getElementById("keyboardMount").addEventListener("codexvalidate", (event) => {
console.log("Validate:", event.detail.value);
});event.detail contains:
valuepreviousValuelayoutkeyboard
setLayout(name): switch to a registered layout.setLayouts(layouts): merge in additional layouts.setTheme(theme): update theme values.setMarginBottom(value): update the fixed bottom offset.setPaddingBottom(value): update the inner bottom spacing.setOutput(output): change output target withnull,"hud", selector, or element.setPhysicalKeyboard(enabled): enable or disable physical keyboard support.setHideOnOutsideClick(enabled): enable or disable outside-click dismissal.setHideOnOutsideClickDelay(delay): update the outside-click grace period in milliseconds.setSlide(enabled): enable or disable slide animation for futureshow()andhide()calls.setOutputAlign(value): update output text alignment with"left","center", or"right".setDisabledKeys(keys): update disabled keys.setHudContent(content): set HUD content with text or a DOM node.clearHud(): remove HUD content.setValue(value): replace the current value.getValue(): return the current value.clearValue(): clear the current value.shakeOutput(): shake the current output element to indicate an invalid or rejected entry. Returnstruewhen an output element is available.isVisible(): returntruewhen the keyboard is currently shown.show(hideOnOutsideClick = false): show the keyboard and optionally allow outside-click dismissal for that presentation.hide(): hide the keyboard using the configuredslidebehavior.destroy(): remove the keyboard from the shadow root.
CodexKeyboard.DEFAULT_LAYOUTSCodexKeyboard.DEFAULT_THEME
- The keyboard is fixed to the bottom of the viewport.
output: "hud"forces the HUD to exist even ifhud: false.physicalKeyboard: trueonly handles keys that exist in the current rendered layout, and only while the keyboard is visible.hideOnOutsideClick: trueis useful for cancellable flows; leave itfalsewhen the user must explicitly finish entry before moving on.hideOnOutsideClickDelayis applied each time the keyboard becomes visible.show(true)is the quick way to present the keyboard in dismissible mode;show(false)forces explicit completion.- Slide animation is controlled by the constructor
slideoption orsetSlide(enabled). shakeOutput()is useful after a failed validation attempt whenoutput: "hud"or another output target is active.- The slide behavior uses a transform transition from the bottom; it does not fade.
- Styles are isolated in a shadow root.
- No external CSS or HTML file is required.