Build a Virtual Keyboard in Tailwind CSS
We will create a virtual keyboard with a responsive design, animated transitions, and colored keys using the Tailwind CSS. It will be responsive for all the devices.
Prerequisites
Approach
- Use Tailwind CSS for the styling and layout.
- Implement JavaScript for the keyboard functionality and animation.
- Create a virtual keyboard with the keys for letters, numbers and special characters.
- Add animations for the key press and caps lock.
- Ensure the keyboard is responsive and works across different devices.
Example: This example shows the implementation of the above-explained approach.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,
initial-scale=1.0">
<title>The Virtual Keyboard</title>
<link href=
"https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css"
rel="stylesheet">
<link href=
"https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet">
<style>
.text-area {
padding: 5px;
width: calc(100vw - 30px);
height: calc(100vh - 295px);
}
.keyboard {
position: fixed;
left: 0;
bottom: 0;
width: 100%;
padding: 5px 0;
background: #000000;
box-shadow: 0 0 50px rgba(0, 0, 0, 0.5);
transition: bottom 0.4s;
}
.keyboard--hidden {
bottom: -100%;
}
.keyboard__keys {
text-align: center;
}
.keyboard__key {
height: 45px;
width: 6%;
max-width: 90px;
margin: 3px;
border-radius: 4px;
border: none;
background: rgba(255, 255, 255, 0.2);
color: white;
font-size: 1.05rem;
outline: none;
cursor: pointer;
display: inline-flex;
justify-content: center;
align-items: center;
vertical-align: top;
padding: 0;
-webkit-tap-highlight-color: transparent;
position: relative;
transition: background-color 0.3s, transform 0.1s;
}
.keyboard__key:hover {
background-color: rgba(255, 255, 255, 0.3);
}
.keyboard__key:active {
background-color: rgba(255, 255, 255, 0.5);
transform: scale(0.95);
}
.keyboard__key--wide {
width: 12%;
}
.keyboard__key--extra--wide {
width: 36%;
max-width: 500px;
}
.keyboard__key--activatable::after {
content: "";
position: absolute;
top: 10px;
right: 10px;
width: 8px;
height: 8px;
background: rgba(255, 255, 255, 0.7);
border-radius: 50%;
}
.keyboard__key--active::after {
background: #08ff00;
}
.keyboard__key--dark {
background: rgba(0, 0, 0, 0.35);
}
</style>
</head>
<body class="bg-gray-400 p-4">
<textarea name="anytext"
class="text-area use-keyboard-input"
placeholder="Click me!"></textarea>
<script>
class VirtualKeyboard {
constructor() {
this.elements = {
main: null,
keysContainer: null,
keys: [],
capsKey: null,
};
this.properties = {
value: "",
capsLock: false,
keyboardInputs: null,
keyLayout: [
"1", "2", "3", "4", "5", "6", "7",
"8", "9", "0",
"backspace", "q", "w", "e", "r",
"t", "y", "u", "i", "o", "p",
"caps", "a", "s", "d", "f", "g",
"h", "j", "k", "l", "enter",
"done", "z", "x", "c", "v", "b",
"n", "m", ",", ".", "?", "space"
],
};
}
init() {
this.elements.main = document
.createElement("div");
this.elements.main.classList
.add("keyboard", "keyboard--hidden");
document.body
.appendChild(this.elements.main);
this.elements.keysContainer = document
.createElement("div");
this.elements.keysContainer
.classList.add("keyboard__keys");
this.elements.main
.appendChild(this.elements.keysContainer);
this.elements.keysContainer
.appendChild(this._createKeys());
this.elements.keys = this.elements
.keysContainer.querySelectorAll(".keyboard__key");
this.properties.keyboardInputs = document
.querySelectorAll(".use-keyboard-input");
this.properties.keyboardInputs
.forEach((element) => {
element.addEventListener("focus", () => {
this.open(element.value, (currentValue) => {
element.value = currentValue;
});
});
});
}
_createIconHTML(icon_name) {
return `<span class="material-icons">${icon_name}</span>`;
}
_createKeyBtn(iconName, class1, onclick, class2) {
const keyElement = document.createElement("button");
keyElement.setAttribute("type",
"button");
keyElement.classList.add("keyboard__key");
keyElement.classList.add(class1,
class2);
keyElement.innerHTML = this
._createIconHTML(iconName);
keyElement.addEventListener("click", onclick);
return keyElement;
}
_createKeys() {
const fragment = document.createDocumentFragment();
this.properties.keyLayout.forEach((key) => {
const insertLineBreak = ["backspace",
"p", "enter", "?"].indexOf(key) !== -1;
switch (key) {
case "backspace":
fragment.appendChild(thi
s._createKeyBtn("backspace",
"keyboard__key--wide", () => {
this.properties.value = this
.properties.value.slice(0, -1);
this._updateValueInTarget();
}));
break;
case "caps":
fragment.appendChild(this
._createKeyBtn("keyboard_capslock",
"keyboard__key--activatable", () => {
this.elements.capsKey
.classList.toggle("keyboard__key--active");
this._toggleCapsLock();
}, "keyboard__key--wide"));
this.elements.capsKey = this
.elements.keys[this.elements.keys.length - 1];
break;
case "enter":
fragment.appendChild(this.
_createKeyBtn("keyboard_return",
"keyboard__key--wide", () => {
this.properties.value += "\n";
this._updateValueInTarget();
}));
break;
case "space":
fragment.appendChild(this.
_createKeyBtn("space_bar",
"keyboard__key--extra--wide", () => {
this.properties.value += " ";
this._updateValueInTarget();
}));
break;
case "done":
fragment.appendChild(this.
_createKeyBtn("check_circle",
"keyboard__key--dark", () => {
this.close();
this._updateValueInTarget();
}, "keyboard__key--wide"));
break;
default:
const keyElement = this._createKeyBtn();
keyElement.textContent = key.toLowerCase();
keyElement.addEventListener("click", () => {
this.properties.value += this.properties.capsLock ?
key.toUpperCase() : key.toLowerCase();
this._updateValueInTarget();
});
fragment.appendChild(keyElement);
break;
}
if (insertLineBreak) {
fragment.appendChild(document.createElement("br"));
}
});
return fragment;
}
_updateValueInTarget() {
this.properties.keyboardInputs.forEach((keyboard) => {
keyboard.value = this.properties.value;
});
}
_toggleCapsLock() {
this.properties.capsLock = !this.properties.capsLock;
for (let key of this.elements.keys) {
if (key.childElementCount === 0) {
key.textContent = this.properties.capsLock ?
key.textContent.toUpperCase() : key.textContent.toLowerCase();
}
}
}
open(initialValue, oninput) {
this.properties.value = initialValue || "";
this.elements.main.classList.remove("keyboard--hidden");
}
close() {
this.properties.value = this.properties.value;
this.elements.main.classList.add("keyboard--hidden");
}
}
window.addEventListener("DOMContentLoaded", function () {
const keyboard = new VirtualKeyboard();
keyboard.init();
});
</script>
</body>
</html>
Output:
Contact Us