<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Keyboard</title>
</head>
<body>
<div id="container"></div>
</body>
</html>
:root {
/* keyboard width */
--width: 100vmin;
/* keyboard height */
--height: calc(var(--width) / 16 * 6.72);
/* border radius */
--radius: calc(var(--width) * 8 / 1600);
/* defines how high the button is raised */
--depth: 5px;
/* letter color */
--color: yellow;
/* font size base */
--fontSize: calc(var(--width)* 16 / 1000)
}
html,
body {
width: 100%;
height: 100%;
margin: 0;
}
body {
/* centering the content */
display: flex;
align-items: center;
justify-content: center;
/* adding a gradient background */
background-image: linear-gradient(to bottom, hsl(225 25% 20%) 0%, hsl(225 40% 10%) 100%);
}
#container {
/* the perspective is equal to the initial keyboard width */
perspective: var(--width);
outline: 1px solid white;
}
.keyboard {
/* spreading sections evenly */
display: flex;
justify-content: space-between;
/* setting the size */
width: var(--width);
height: var(--height);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
font-size: var(--fontSize);
/* adding a gradient background */
background-image: linear-gradient(to bottom, hsl(192 11% 53%) 0%, hsl(192 26% 43%) 100%);
/* setting the border radius */
border-radius: var(--radius);
/* calculating paddings */
padding: calc(var(--radius) * 2);
box-sizing: border-box;
/* enabling the 3d mode */
transform-style: preserve-3d;
/* applying the transform rule */
transform: rotateX(0.08turn) rotateY(0.03turn) rotateZ(0turn);
}
.overlay {
/* setting the size */
width: var(--width);
height: var(--height);
/* centering the overlay */
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%) translateZ(10px);
/* adding a gradient background */
background-image: linear-gradient(to bottom, #ffffff33 0%, transparent 100%);
/* adding a noisy effect */
filter: blur(25px);
}
.section {
/* spreading rows evenly */
display: flex;
flex-direction: column;
justify-content: space-between;
}
.row {
/* spreading buttons evenly */
display: flex;
justify-content: space-between;
}
.row.functions .button {
/* calculating the height of the function button */
height: calc(var(--height) / 10);
}
.row.actions .button,
.row.functions .button {
/* calculating the width of the action and function buttons */
--size: calc(var(--width) / 18);
}
.button {
/* setting the default dimensions of the button */
--size: calc(var(--width) / 20);
height: calc(var(--height) / 7);
width: var(--size);
/* setting the border radius */
border-radius: var(--radius);
/* centering the content of the button */
display: flex;
justify-content: center;
align-items: center;
/* additional settings */
box-sizing: border-box;
background: #000000;
/* applying the global color */
color: var(--color);
/* adding the default margin */
margin-left: calc(var(--width) / 200);
/* raising the button above the keyboard */
transform: translate3d(0px, 0px, var(--depth));
/* enabling the 3d mode */
transform-style: preserve-3d;
/* calculating the perspective from the width */
perspective: calc(var(--size) * 3);
}
.button:first-child {
/* reset margin for the leftmost button */
margin-left: 0;
}
.button .shadow {
/* centering the shadow */
position: absolute;
left: 50%;
top: 50%;
/* applying the transform */
transform: translate3d(-50%, -50%, calc(var(--depth) * -1));
background: #00000088;
}
.button.multi {
/* placing labels under each other */
flex-direction: column-reverse;
}
/* the next set of rules are buttons adjustments */
.button.backspace,
.button.tab {
--size: calc(var(--width) / 13);
}
.button.backspace {
font-size: calc(var(--fontSize) * 0.8)
}
.button.capslock,
.button.enter {
--size: calc(var(--width) / 11);
}
.button.shift {
--size: calc(var(--width) / 9);
}
.button.space {
--size: calc(var(--width) / 2.3);
}
/* settings for the special button */
.button.dev {
/* defining the accent color */
--accent: #ffffff;
color: var(--accent);
/* adjusting letter spacing for the better readability */
letter-spacing: 0.5px;
/* adding the glow effect */
text-shadow: 0 0 5px var(--accent), 0 0 10px var(--accent), 0 0 15px var(--accent), 0 0 20px var(--color), 0 0 30px var(--color),
0 0 40px var(--color), 0 0 50px var(--color), 0 0 75px var(--color);
}
/* the empty button settings */
.button.empty {
background: transparent;
}
/**
* @typedef {Object} key
* @property {string} [extra] extra class name
* @property {string | string[]} value button label(s)
*/
/**
* @typedef {Object} section
* @property {string} [extra] extra class name
* @property {key[]} keys set of keys in the row
*/
/**
* the list of buttons of the main section
* @type {section[]}
*/
const mainSection = [
{
extra: "functions",
keys: [
{ value: "Esc" },
{ value: "F1" },
{ value: "F2" },
{ value: "F3" },
{ value: "F4" },
{ value: "F5" },
{ value: "F6" },
{ value: "F7" },
{ value: "F8" },
{ value: "F9" },
{ value: "F10" },
{ value: "F11" },
{ value: "F12" }
]
},
{
keys: [
{ value: ["`", `~`] },
{ value: ["1", "!"] },
{ value: ["2", "@"] },
{ value: ["3", "#"] },
{ value: ["4", "$"] },
{ value: ["5", "%"] },
{ value: ["6", "^"] },
{ value: ["7", "&"] },
{ value: ["8", "*"] },
{ value: ["9", "("] },
{ value: ["0", ")"] },
{ value: ["-", "_"] },
{ value: ["=", "+"] },
{ value: "Backspace", extra: "backspace" }
]
},
{
keys: [
{ value: "Tab", extra: "tab" },
{ value: "Q" },
{ value: "W" },
{ value: "E" },
{ value: "R" },
{ value: "T" },
{ value: "Y" },
{ value: "U" },
{ value: "I" },
{ value: "O" },
{ value: "P" },
{ value: ["[", "{"] },
{ value: ["]", "}"] },
{ value: ["\\", "|"] }
]
},
{
keys: [
{ value: "Caps Lock", extra: "capslock" },
{ value: "A" },
{ value: "S" },
{ value: "D" },
{ value: "F" },
{ value: "G" },
{ value: "H" },
{ value: "J" },
{ value: "K" },
{ value: "L" },
{ value: [";", ":"] },
{ value: ["'", '"'] },
{ value: "Enter", extra: "enter" }
]
},
{
keys: [
{ value: "Shift", extra: "shift" },
{ value: "Z" },
{ value: "X" },
{ value: "C" },
{ value: "V" },
{ value: "B" },
{ value: "N" },
{ value: "M" },
{ value: [",", "<"] },
{ value: [".", ">"] },
{ value: ["/", "?"] },
{ value: "Shift", extra: "shift" }
]
},
{
keys: [
{ value: "Ctrl" },
{ value: "Fn" },
{ value: "[DEV]", extra: "dev" },
{ value: "Alt" },
{ value: "Space", extra: "space" },
{ value: "Alt" },
{ value: "Ctrl" }
]
}
];
/**
* the list of buttons of the additional section
* @type {section[]}
*/
const additionalSection = [
{
extra: "functions",
keys: [{ value: "PrtScr" }, { value: "ScrLk" }, { value: "Pause" }]
},
{
extra: "actions",
keys: [{ value: "Ins" }, { value: "Home" }, { value: "PgUp" }]
},
{
extra: "actions",
keys: [{ value: "Del" }, { value: "End" }, { value: "PgDn" }]
},
{ extra: "actions", keys: [{ extra: "empty" }] },
{
extra: "actions",
keys: [{ extra: "empty" }, { value: "\u2191" }, { extra: "empty" }]
},
{
extra: "actions",
keys: [{ value: "\u2190" }, { value: "\u2193" }, { value: "\u2192" }]
}
];
/**
* parse the array of strings and build a string from the values
* @param {string[]} values values to be parsed
* @returns {string}
*/
function toString(values) {
return values.filter(value => !!value).join(" ");
}
/**
* create new div element
* @returns {HTMLDivElement}
*/
function div() {
return document.createElement("div");
}
/**
* draw a section
* @param {section[][]} sections list of sections to be drawn
*/
function draw(sections) {
// obtaining the container
const container = document.getElementById("container");
if (container) {
// creating keyboard
const keyboard = div();
keyboard.className = "keyboard";
sections.forEach(rows => {
// creating section
const section = div();
section.className = "section";
rows.forEach(data => {
// creating row
const row = div();
row.className = toString(["row", data.extra]);
if (data.keys instanceof Array) {
data.keys.forEach(key => {
// creating button
const button = div();
// creating shadow
const shadow = div();
const value = key.value instanceof Array ? key.value : [key.value];
// setting classes
button.className = toString([
"button",
key.extra,
value.length > 1 ? "multi" : null
]);
shadow.className = toString(["button", key.extra, "shadow"]);
// appending the shadow
button.appendChild(shadow);
// rendering labels
value.forEach(item => {
const label = div();
label.innerText = item || "";
button.appendChild(label);
});
// appending the button to the row
row.appendChild(button);
});
}
// appending the row to the section
section.appendChild(row);
});
// appending the section to the keyboard
keyboard.appendChild(section);
});
// adding the overlay
const overlay = div();
overlay.className = "overlay";
keyboard.appendChild(overlay);
// appending the keyboard to the container
container.appendChild(keyboard);
}
}
/** draw the keyboard */
draw([mainSection, additionalSection]);
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.