Introducing Nuvyx UI v1.0.0

Keyboard

A customizable interactive keyboard component with various themes, layouts, and animation options.

Esc
F1
F2
F3
F4
F5
F6
F7
F8
F9
F10
F11
F12
`
1
2
3
4
5
6
7
8
9
0
-
=
Backspace
Tab
Q
W
E
R
T
Y
U
I
O
P
[
]
\
Caps
A
S
D
F
G
H
J
K
L
;
'
Enter
Shift
Z
X
C
V
B
N
M
,
.
/
Shift
Ctrl
Alt
Space
Alt
Ctrl

Installation Guide

1

Install Dependencies

Lucide React

npm install lucide-react
2

Copy Component Code

Keyboard.tsx
1"use client"
2import type React from "react"
3import { useEffect, useState } from "react"
4import { Command, ArrowUp, ArrowDown, ArrowLeft, ArrowRight, ChevronsUp, Menu } from "lucide-react"
5
6interface KeyObject {
7  label?: string
8  code?: string
9  size: number
10  spacer?: boolean
11  type?: string
12  icon?: string
13}
14interface KeyboardRow {
15  function?: boolean
16  keys: KeyObject[]
17  nav?: KeyObject[]
18}
19interface InteractiveKeyboardProps {
20  layout?: "standard" | "compact"
21  showFunctionKeys?: boolean
22  showNavigationCluster?: boolean
23  activeKeys?: string[]
24  activeKeyGlowColor?: string
25  activeKeyGlowIntensity?: number
26  theme?: "cyberpunk" | "minimal" | "retro" | "mechanical" | "neon" | "pastel"
27  keyColor?: string
28  keyTextColor?: string
29  accentColor?: string
30  keyPressedColor?: string
31  keyPressAnimationDuration?: number
32  onKeyPress?: (code: string, key?: string) => void
33  onKeyRelease?: (code: string, key?: string) => void
34  className?: string
35  allowPhysicalKeyboard?: boolean
36  perspective?: number
37  rotateX?: number
38  [key: string]: unknown
39}
40
41interface KeyStyleProps {
42  background: string
43  color: string
44  boxShadow: string
45  textShadow?: string
46  border: string
47  fontFamily?: string
48  fontWeight?: string | number
49  fontSize?: string
50  borderRadius?: string
51  letterSpacing?: string
52  transform?: string
53  transition?: string
54  height?: string
55  marginBottom?: string
56  padding?: string
57  width?: string
58}
59
60interface ThemeStyles {
61  keyboard: {
62    background: string
63    boxShadow: string
64    border: string
65    borderRadius?: string
66    marginBottom?: string
67    padding?: string
68    width?: string
69  }
70  key: KeyStyleProps
71  keyPressed: KeyStyleProps
72  keyHover: KeyStyleProps
73  keyActive?: KeyStyleProps
74  specialKey?: KeyStyleProps
75  functionKey?: KeyStyleProps
76  modifierKey?: KeyStyleProps
77  spaceKey?: KeyStyleProps
78  arrowKey?: KeyStyleProps
79}
80
81const InteractiveKeyboard: React.FC<InteractiveKeyboardProps> = ({
82  layout = "standard",
83  showFunctionKeys = true,
84  showNavigationCluster = true,
85  activeKeys = [],
86  activeKeyGlowColor = "#6366f1",
87  activeKeyGlowIntensity = 0.8,
88  theme = "cyberpunk",
89  keyColor = "#2a2a2a",
90  keyTextColor = "#ffffff",
91  accentColor = "#6366f1",
92  keyPressedColor = "#333333",
93  keyPressAnimationDuration = 150,
94  onKeyPress = () => {},
95  onKeyRelease = () => {},
96  className = "",
97  allowPhysicalKeyboard = true,
98  perspective = 1000,
99  rotateX = 10,
100  ...props
101}) => {
102  const [pressedKeys, setPressedKeys] = useState<Set<string>>(new Set())
103  const getKeyboardLayout = (): KeyboardRow[] => {
104    switch (layout) {
105      case "compact":
106        return getStandardLayout().filter((row) => !row.function)
107      case "standard":
108      default:
109        return getStandardLayout()
110    }
111  }
112
113  const getStandardLayout = (): KeyboardRow[] => {
114    return [
115      {
116        function: true,
117        keys: [
118          { label: "Esc", code: "Escape", size: 1 },
119          { spacer: true, size: 1 },
120          { label: "F1", code: "F1", size: 1 },
121          { label: "F2", code: "F2", size: 1 },
122          { label: "F3", code: "F3", size: 1 },
123          { label: "F4", code: "F4", size: 1 },
124          { spacer: true, size: 0.5 },
125          { label: "F5", code: "F5", size: 1 },
126          { label: "F6", code: "F6", size: 1 },
127          { label: "F7", code: "F7", size: 1 },
128          { label: "F8", code: "F8", size: 1 },
129          { spacer: true, size: 0.5 },
130          { label: "F9", code: "F9", size: 1 },
131          { label: "F10", code: "F10", size: 1 },
132          { label: "F11", code: "F11", size: 1 },
133          { label: "F12", code: "F12", size: 1 },
134        ],
135        nav: [
136          { spacer: true, size: 0.5 },
137          { type: "light", size: 0.5, code: "light1" },
138          { type: "light", size: 0.5, code: "light2" },
139          { type: "light", size: 0.5, code: "light3" },
140        ],
141      },
142      {
143        keys: [
144          { label: "`", code: "Backquote", size: 1 },
145          { label: "1", code: "Digit1", size: 1 },
146          { label: "2", code: "Digit2", size: 1 },
147          { label: "3", code: "Digit3", size: 1 },
148          { label: "4", code: "Digit4", size: 1 },
149          { label: "5", code: "Digit5", size: 1 },
150          { label: "6", code: "Digit6", size: 1 },
151          { label: "7", code: "Digit7", size: 1 },
152          { label: "8", code: "Digit8", size: 1 },
153          { label: "9", code: "Digit9", size: 1 },
154          { label: "0", code: "Digit0", size: 1 },
155          { label: "-", code: "Minus", size: 1 },
156          { label: "=", code: "Equal", size: 1 },
157          { label: "Backspace", code: "Backspace", size: 2 },
158        ],
159        nav: [
160          { label: "Del", code: "Delete", size: 1 },
161          { label: "End", code: "End", size: 1 },
162          { label: "PgDn", code: "PageDown", size: 1 },
163        ],
164      },
165      {
166        keys: [
167          { label: "Tab", code: "Tab", size: 1.5 },
168          { label: "Q", code: "KeyQ", size: 1 },
169          { label: "W", code: "KeyW", size: 1 },
170          { label: "E", code: "KeyE", size: 1 },
171          { label: "R", code: "KeyR", size: 1 },
172          { label: "T", code: "KeyT", size: 1 },
173          { label: "Y", code: "KeyY", size: 1 },
174          { label: "U", code: "KeyU", size: 1 },
175          { label: "I", code: "KeyI", size: 1 },
176          { label: "O", code: "KeyO", size: 1 },
177          { label: "P", code: "KeyP", size: 1 },
178          { label: "[", code: "BracketLeft", size: 1 },
179          { label: "]", code: "BracketRight", size: 1 },
180          { label: "\\", code: "Backslash", size: 1.5 },
181        ],
182        nav: [
183          { label: "Ins", code: "Insert", size: 1 },
184          { label: "Home", code: "Home", size: 1 },
185          { label: "PgUp", code: "PageUp", size: 1 },
186        ],
187      },
188      {
189        keys: [
190          { label: "Caps", code: "CapsLock", size: 1.75, icon: "capslock" },
191          { label: "A", code: "KeyA", size: 1 },
192          { label: "S", code: "KeyS", size: 1 },
193          { label: "D", code: "KeyD", size: 1 },
194          { label: "F", code: "KeyF", size: 1 },
195          { label: "G", code: "KeyG", size: 1 },
196          { label: "H", code: "KeyH", size: 1 },
197          { label: "J", code: "KeyJ", size: 1 },
198          { label: "K", code: "KeyK", size: 1 },
199          { label: "L", code: "KeyL", size: 1 },
200          { label: ";", code: "Semicolon", size: 1 },
201          { label: "'", code: "Quote", size: 1 },
202          { label: "Enter", code: "Enter", size: 2.25 },
203        ],
204        nav: [
205          { label: "Print", code: "PrintScreen", size: 1 },
206          { label: "Scroll", code: "ScrollLock", size: 1 },
207          { label: "Pause", code: "Pause", size: 1 },
208        ],
209      },
210      {
211        keys: [
212          { label: "Shift", code: "ShiftLeft", size: 2.25 },
213          { label: "Z", code: "KeyZ", size: 1 },
214          { label: "X", code: "KeyX", size: 1 },
215          { label: "C", code: "KeyC", size: 1 },
216          { label: "V", code: "KeyV", size: 1 },
217          { label: "B", code: "KeyB", size: 1 },
218          { label: "N", code: "KeyN", size: 1 },
219          { label: "M", code: "KeyM", size: 1 },
220          { label: ",", code: "Comma", size: 1 },
221          { label: ".", code: "Period", size: 1 },
222          { label: "/", code: "Slash", size: 1 },
223          { label: "Shift", code: "ShiftRight", size: 2.75 },
224        ],
225        nav: [
226          { spacer: true, size: 1 },
227          { label: "", code: "ArrowUp", size: 1, icon: "arrowup" },
228          { spacer: true, size: 1 },
229        ],
230      },
231      {
232        keys: [
233          { label: "Ctrl", code: "ControlLeft", size: 1.25 },
234          { label: "", code: "MetaLeft", size: 1.25, icon: "windows" },
235          { label: "Alt", code: "AltLeft", size: 1.25 },
236          { label: "Space", code: "Space", size: 6.25 },
237          { label: "Alt", code: "AltRight", size: 1.25 },
238          { label: "", code: "MetaRight", size: 1.25, icon: "windows" },
239          { label: "", code: "ContextMenu", size: 1.25, icon: "menu" },
240          { label: "Ctrl", code: "ControlRight", size: 1.25 },
241        ],
242        nav: [
243          { label: "", code: "ArrowLeft", size: 1, icon: "arrowleft" },
244          { label: "", code: "ArrowDown", size: 1, icon: "arrowdown" },
245          { label: "", code: "ArrowRight", size: 1, icon: "arrowright" },
246        ],
247      },
248    ]
249  }
250
251  useEffect(() => {
252    if (!allowPhysicalKeyboard) return
253
254    const handleKeyDown = (e: KeyboardEvent) => {
255      setPressedKeys((prev) => {
256        const newSet = new Set(prev)
257        newSet.add(e.code)
258        return newSet
259      })
260
261      onKeyPress(e.code, e.key)
262    }
263    const handleKeyUp = (e: KeyboardEvent) => {
264      setPressedKeys((prev) => {
265        const newSet = new Set(prev)
266        newSet.delete(e.code)
267        return newSet
268      })
269      onKeyRelease(e.code, e.key)
270    }
271    if (allowPhysicalKeyboard) {
272      window.addEventListener("keydown", handleKeyDown)
273      window.addEventListener("keyup", handleKeyUp)
274    }
275    return () => {
276      window.removeEventListener("keydown", handleKeyDown)
277      window.removeEventListener("keyup", handleKeyUp)
278    }
279  }, [allowPhysicalKeyboard, onKeyPress, onKeyRelease])
280
281  const handleKeyDown = (code: string) => {
282    setPressedKeys((prev) => {
283      const newSet = new Set(prev)
284      newSet.add(code)
285      return newSet
286    })
287
288    onKeyPress(code)
289  }
290
291  const handleKeyUp = (code: string) => {
292    setPressedKeys((prev) => {
293      const newSet = new Set(prev)
294      newSet.delete(code)
295      return newSet
296    })
297
298    onKeyRelease(code)
299  }
300
301  const getThemeStyles = (): ThemeStyles => {
302    switch (theme) {
303      case "minimal":
304        return {
305          keyboard: {
306            background: "linear-gradient(to bottom, #ffffff, #f8f9fa)",
307            boxShadow: "0 10px 30px rgba(0, 0, 0, 0.08), 0 6px 10px rgba(0, 0, 0, 0.05)",
308            border: "1px solid rgba(0, 0, 0, 0.06)",
309            borderRadius: "10px",
310          },
311          key: {
312            background: "linear-gradient(to bottom, #ffffff, #f7f7f9)",
313            color: "#333333",
314            boxShadow: "0 2px 3px rgba(0, 0, 0, 0.06), inset 0 1px 0 rgba(255, 255, 255, 0.8)",
315            textShadow: "none",
316            border: "1px solid rgba(0, 0, 0, 0.08)",
317            fontFamily: "system-ui, -apple-system, sans-serif",
318            fontWeight: "500",
319            fontSize: "11px",
320            borderRadius: "5px",
321            transition: "all 0.15s ease",
322          },
323          keyPressed: {
324            background: "linear-gradient(to bottom, #f0f0f0, #e8e8e8)",
325            boxShadow: "inset 0 1px 2px rgba(0, 0, 0, 0.08)",
326            color: accentColor,
327            border: "1px solid rgba(0, 0, 0, 0.12)",
328            transform: "translateY(1px)",
329            fontSize: "11px",
330            fontWeight: "500",
331            fontFamily: "system-ui, -apple-system, sans-serif",
332            textShadow: "none",
333            borderRadius: "5px",
334            transition: "all 0.05s ease",
335          },
336          keyHover: {
337            background: "linear-gradient(to bottom, #ffffff, #f9f9f9)",
338            boxShadow: "0 2px 3px rgba(0, 0, 0, 0.08), inset 0 1px 0 rgba(255, 255, 255, 1)",
339            color: accentColor,
340            border: "1px solid rgba(0, 0, 0, 0.1)",
341            fontSize: "11px",
342            fontWeight: "500",
343            fontFamily: "system-ui, -apple-system, sans-serif",
344            textShadow: "none",
345            borderRadius: "5px",
346            transition: "all 0.15s ease",
347          },
348          keyActive: {
349            background: "linear-gradient(to bottom, #ffffff, #f5f5f7)",
350            boxShadow: `0 0 10px ${activeKeyGlowColor}, inset 0 1px 0 rgba(255, 255, 255, 0.8)`,
351            color: activeKeyGlowColor,
352            border: `1px solid ${activeKeyGlowColor}`,
353            fontSize: "11px",
354            fontWeight: "500",
355            fontFamily: "system-ui, -apple-system, sans-serif",
356            textShadow: `0 0 5px ${activeKeyGlowColor}`,
357            borderRadius: "5px",
358            transition: "all 0.1s ease",
359          },
360          specialKey: {
361            background: "linear-gradient(to bottom, #f8f8fa, #eff0f2)",
362            color: "#555555",
363            boxShadow: "0 2px 3px rgba(0, 0, 0, 0.06), inset 0 1px 0 rgba(255, 255, 255, 0.8)",
364            border: "1px solid rgba(0, 0, 0, 0.08)",
365            fontSize: "10px",
366            fontWeight: "500",
367            fontFamily: "system-ui, -apple-system, sans-serif",
368            textShadow: "none",
369            borderRadius: "5px",
370            transition: "all 0.15s ease",
371          },
372          functionKey: {
373            background: "linear-gradient(to bottom, #f2f2f4, #eaeaec)",
374            color: "#666666",
375            fontSize: "9px",
376            boxShadow: "0 2px 3px rgba(0, 0, 0, 0.06), inset 0 1px 0 rgba(255, 255, 255, 0.8)",
377            border: "1px solid rgba(0, 0, 0, 0.08)",
378            fontWeight: "500",
379            fontFamily: "system-ui, -apple-system, sans-serif",
380            textShadow: "none",
381            borderRadius: "5px",
382            transition: "all 0.15s ease",
383          },
384          modifierKey: {
385            background: "linear-gradient(to bottom, #f2f2f4, #eaeaec)",
386            color: "#555555",
387            boxShadow: "0 2px 3px rgba(0, 0, 0, 0.06), inset 0 1px 0 rgba(255, 255, 255, 0.8)",
388            border: "1px solid rgba(0, 0, 0, 0.08)",
389            fontSize: "10px",
390            fontWeight: "500",
391            fontFamily: "system-ui, -apple-system, sans-serif",
392            textShadow: "none",
393            borderRadius: "5px",
394            transition: "all 0.15s ease",
395          },
396          spaceKey: {
397            background: "linear-gradient(to bottom, #ffffff, #f8f8f8)",
398            color: "#333333",
399            boxShadow: "0 2px 3px rgba(0, 0, 0, 0.06), inset 0 1px 0 rgba(255, 255, 255, 0.8)",
400            border: "1px solid rgba(0, 0, 0, 0.08)",
401            fontSize: "11px",
402            fontWeight: "500",
403            fontFamily: "system-ui, -apple-system, sans-serif",
404            textShadow: "none",
405            borderRadius: "5px",
406            transition: "all 0.15s ease",
407          },
408          arrowKey: {
409            background: "linear-gradient(to bottom, #f2f2f4, #eaeaec)",
410            color: "#555555",
411            boxShadow: "0 2px 3px rgba(0, 0, 0, 0.06), inset 0 1px 0 rgba(255, 255, 255, 0.8)",
412            border: "1px solid rgba(0, 0, 0, 0.08)",
413            fontSize: "11px",
414            fontWeight: "500",
415            fontFamily: "system-ui, -apple-system, sans-serif",
416            textShadow: "none",
417            borderRadius: "5px",
418            transition: "all 0.15s ease",
419          },
420        }
421
422      case "retro":
423        return {
424          keyboard: {
425            background: "linear-gradient(to bottom, #f5f0e8, #e8e0d0)",
426            boxShadow: "0 8px 20px rgba(120, 100, 80, 0.2), 0 4px 8px rgba(120, 100, 80, 0.1), inset 0 1px 0 #fff",
427            border: "2px solid #d0c0a0",
428            borderRadius: "8px",
429            padding: "8px",
430          },
431          key: {
432            background: "linear-gradient(to bottom, #fff8e8, #f0e8d8)",
433            color: "#705030",
434            boxShadow: "0 4px 0 #c0b090, inset 0 1px 0 #fffaf0",
435            textShadow: "none",
436            border: "1px solid #d0c0a0",
437            fontFamily: '"Courier New", monospace',
438            fontWeight: "600",
439            fontSize: "11px",
440            borderRadius: "6px",
441            transition: "all 0.12s ease-out",
442            marginBottom: "4px",
443          },
444          keyPressed: {
445            background: "linear-gradient(to bottom, #e8d8c0, #d8c8b0)",
446            boxShadow: "0 0 0 #c0b090, inset 0 1px 2px rgba(120, 100, 80, 0.2)",
447            color: "#604020",
448            textShadow: "none",
449            border: "1px solid #c0b090",
450            transform: "translateY(4px)",
451            fontSize: "11px",
452            fontWeight: "600",
453            fontFamily: '"Courier New", monospace',
454            borderRadius: "6px",
455            marginBottom: "0px",
456            transition: "all 0.05s ease",
457          },
458          keyHover: {
459            background: "linear-gradient(to bottom, #fffaf0, #f8f0e0)",
460            boxShadow: "0 4px 0 #c0b090, inset 0 1px 0 #fffaf0",
461            color: "#604020",
462            border: "1px solid #c0b090",
463            fontSize: "11px",
464            fontWeight: "600",
465            fontFamily: '"Courier New", monospace',
466            textShadow: "none",
467            borderRadius: "6px",
468            transition: "all 0.12s ease-out",
469            marginBottom: "4px",
470          },
471          keyActive: {
472            background: "linear-gradient(to bottom, #fff8e8, #f0e8d8)",
473            boxShadow: `0 4px 0 #c0b090, 0 0 15px ${activeKeyGlowColor}, inset 0 1px 0 #fffaf0`,
474            color: activeKeyGlowColor,
475            textShadow: `0 0 5px ${activeKeyGlowColor}`,
476            border: `1px solid ${activeKeyGlowColor}`,
477            fontSize: "11px",
478            fontWeight: "600",
479            fontFamily: '"Courier New", monospace',
480            borderRadius: "6px",
481            transition: "all 0.12s ease-out",
482            marginBottom: "4px",
483          },
484          specialKey: {
485            background: "linear-gradient(to bottom, #f0e8d8, #e0d0c0)",
486            color: "#604020",
487            boxShadow: "0 4px 0 #c0b090, inset 0 1px 0 #fffaf0",
488            border: "1px solid #d0c0a0",
489            fontSize: "10px",
490            fontWeight: "600",
491            fontFamily: '"Courier New", monospace',
492            textShadow: "none",
493            borderRadius: "6px",
494            transition: "all 0.12s ease-out",
495            marginBottom: "4px",
496          },
497          functionKey: {
498            background: "linear-gradient(to bottom, #e8d8c0, #d8c8b0)",
499            color: "#604020",
500            fontSize: "9px",
501            boxShadow: "0 4px 0 #c0b090, inset 0 1px 0 #fffaf0",
502            border: "1px solid #d0c0a0",
503            fontWeight: "600",
504            fontFamily: '"Courier New", monospace',
505            textShadow: "none",
506            borderRadius: "6px",
507            transition: "all 0.12s ease-out",
508            marginBottom: "4px",
509          },
510          modifierKey: {
511            background: "linear-gradient(to bottom, #e8d8c0, #d8c8b0)",
512            color: "#604020",
513            boxShadow: "0 4px 0 #c0b090, inset 0 1px 0 #fffaf0",
514            border: "1px solid #d0c0a0",
515            fontSize: "10px",
516            fontWeight: "600",
517            fontFamily: '"Courier New", monospace',
518            textShadow: "none",
519            borderRadius: "6px",
520            transition: "all 0.12s ease-out",
521            marginBottom: "4px",
522          },
523          spaceKey: {
524            background: "linear-gradient(to bottom, #fff8e8, #f0e8d8)",
525            color: "#705030",
526            boxShadow: "0 4px 0 #c0b090, inset 0 1px 0 #fffaf0",
527            border: "1px solid #d0c0a0",
528            fontSize: "11px",
529            fontWeight: "600",
530            fontFamily: '"Courier New", monospace',
531            textShadow: "none",
532            borderRadius: "6px",
533            transition: "all 0.12s ease-out",
534            marginBottom: "4px",
535          },
536          arrowKey: {
537            background: "linear-gradient(to bottom, #e8d8c0, #d8c8b0)",
538            color: "#604020",
539            boxShadow: "0 4px 0 #c0b090, inset 0 1px 0 #fffaf0",
540            border: "1px solid #d0c0a0",
541            fontSize: "11px",
542            fontWeight: "600",
543            fontFamily: '"Courier New", monospace',
544            textShadow: "none",
545            borderRadius: "6px",
546            transition: "all 0.12s ease-out",
547            marginBottom: "4px",
548          },
549        }
550
551      case "cyberpunk":
552        return {
553          keyboard: {
554            background: `linear-gradient(145deg, ${adjustColorBrightness(
555              keyColor,
556              -5,
557            )}, ${adjustColorBrightness(keyColor, -15)})`,
558            boxShadow: `0 15px 40px rgba(0, 0, 0, 0.3), 0 10px 20px rgba(0, 0, 0, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.05), inset 0 -1px 0 rgba(0, 0, 0, 0.5)`,
559            border: `1px solid ${adjustColorBrightness(keyColor, -20)}`,
560            borderRadius: "12px",
561          },
562          key: {
563            background: `linear-gradient(145deg, ${adjustColorBrightness(keyColor, 10)}, ${keyColor})`,
564            color: keyTextColor,
565            boxShadow: `0 3px 0 ${adjustColorBrightness(
566              keyColor,
567              -20,
568            )}, 0 0 10px rgba(0, 0, 0, 0.2), inset 0 1px 1px rgba(255, 255, 255, 0.1)`,
569            textShadow: `0 0 5px rgba(255, 255, 255, 0.3)`,
570            border: `1px solid ${adjustColorBrightness(keyColor, -10)}`,
571            fontFamily: '"Inter", "SF Pro Display", system-ui, sans-serif',
572            fontWeight: "600",
573            fontSize: "11px",
574            borderRadius: "6px",
575            letterSpacing: "0.5px",
576            transition: "all 0.15s cubic-bezier(0.23, 1, 0.32, 1)",
577          },
578          keyPressed: {
579            background: `linear-gradient(145deg, ${keyPressedColor}, ${adjustColorBrightness(keyPressedColor, -10)})`,
580            boxShadow: `0 0 0 ${adjustColorBrightness(keyColor, -20)}, 0 0 15px rgba(${hexToRgb(
581              accentColor,
582            )}, 0.5), inset 0 1px 2px rgba(0, 0, 0, 0.3)`,
583            color: accentColor,
584            textShadow: `0 0 8px rgba(${hexToRgb(accentColor)}, 0.7)`,
585            border: `1px solid ${adjustColorBrightness(keyPressedColor, 10)}`,
586            transform: "translateY(2px)",
587            fontSize: "11px",
588            fontWeight: "600",
589            fontFamily: '"Inter", "SF Pro Display", system-ui, sans-serif',
590            borderRadius: "6px",
591            letterSpacing: "0.5px",
592            transition: "all 0.08s cubic-bezier(0.23, 1, 0.32, 1)",
593          },
594          keyHover: {
595            background: `linear-gradient(145deg, ${adjustColorBrightness(
596              keyColor,
597              15,
598            )}, ${adjustColorBrightness(keyColor, 5)})`,
599            boxShadow: `0 3px 0 ${adjustColorBrightness(keyColor, -20)}, 0 0 12px rgba(${hexToRgb(
600              accentColor,
601            )}, 0.3), inset 0 1px 1px rgba(255, 255, 255, 0.15)`,
602            color: accentColor,
603            textShadow: `0 0 8px rgba(${hexToRgb(accentColor)}, 0.5)`,
604            border: `1px solid ${adjustColorBrightness(keyColor, 0)}`,
605            fontSize: "11px",
606            fontWeight: "600",
607            fontFamily: '"Inter", "SF Pro Display", system-ui, sans-serif',
608            borderRadius: "6px",
609            letterSpacing: "0.5px",
610            transition: "all 0.15s cubic-bezier(0.23, 1, 0.32, 1)",
611          },
612          keyActive: {
613            background: `linear-gradient(145deg, ${adjustColorBrightness(keyColor, 10)}, ${keyColor})`,
614            boxShadow: `0 3px 0 ${adjustColorBrightness(keyColor, -20)}, 0 0 20px rgba(${hexToRgb(
615              activeKeyGlowColor,
616            )}, ${activeKeyGlowIntensity}), inset 0 1px 1px rgba(255, 255, 255, 0.1)`,
617            color: activeKeyGlowColor,
618            textShadow: `0 0 10px rgba(${hexToRgb(activeKeyGlowColor)}, 0.9)`,
619            border: `1px solid ${activeKeyGlowColor}`,
620            fontSize: "11px",
621            fontWeight: "600",
622            fontFamily: '"Inter", "SF Pro Display", system-ui, sans-serif',
623            borderRadius: "6px",
624            letterSpacing: "0.5px",
625            transition: "all 0.15s cubic-bezier(0.23, 1, 0.32, 1)",
626          },
627          specialKey: {
628            background: `linear-gradient(145deg, ${adjustColorBrightness(
629              keyColor,
630              0,
631            )}, ${adjustColorBrightness(keyColor, -10)})`,
632            color: adjustColorBrightness(keyTextColor, -10),
633            boxShadow: `0 3px 0 ${adjustColorBrightness(
634              keyColor,
635              -20,
636            )}, 0 0 10px rgba(0, 0, 0, 0.2), inset 0 1px 1px rgba(255, 255, 255, 0.1)`,
637            border: `1px solid ${adjustColorBrightness(keyColor, -10)}`,
638            fontSize: "10px",
639            fontWeight: "600",
640            fontFamily: '"Inter", "SF Pro Display", system-ui, sans-serif',
641            textShadow: `0 0 5px rgba(255, 255, 255, 0.3)`,
642            borderRadius: "6px",
643            letterSpacing: "0.5px",
644            transition: "all 0.15s cubic-bezier(0.23, 1, 0.32, 1)",
645          },
646          functionKey: {
647            background: `linear-gradient(145deg, ${adjustColorBrightness(
648              keyColor,
649              -5,
650            )}, ${adjustColorBrightness(keyColor, -15)})`,
651            color: adjustColorBrightness(keyTextColor, -15),
652            fontSize: "9px",
653            boxShadow: `0 3px 0 ${adjustColorBrightness(
654              keyColor,
655              -20,
656            )}, 0 0 10px rgba(0, 0, 0, 0.2), inset 0 1px 1px rgba(255, 255, 255, 0.1)`,
657            border: `1px solid ${adjustColorBrightness(keyColor, -10)}`,
658            fontWeight: "600",
659            fontFamily: '"Inter", "SF Pro Display", system-ui, sans-serif',
660            textShadow: `0 0 5px rgba(255, 255, 255, 0.3)`,
661            borderRadius: "6px",
662            letterSpacing: "0.5px",
663            transition: "all 0.15s cubic-bezier(0.23, 1, 0.32, 1)",
664          },
665          modifierKey: {
666            background: `linear-gradient(145deg, ${adjustColorBrightness(
667              keyColor,
668              -5,
669            )}, ${adjustColorBrightness(keyColor, -15)})`,
670            color: adjustColorBrightness(keyTextColor, -5),
671            boxShadow: `0 3px 0 ${adjustColorBrightness(
672              keyColor,
673              -20,
674            )}, 0 0 10px rgba(0, 0, 0, 0.2), inset 0 1px 1px rgba(255, 255, 255, 0.1)`,
675            border: `1px solid ${adjustColorBrightness(keyColor, -10)}`,
676            fontSize: "10px",
677            fontWeight: "600",
678            fontFamily: '"Inter", "SF Pro Display", system-ui, sans-serif',
679            textShadow: `0 0 5px rgba(255, 255, 255, 0.3)`,
680            borderRadius: "6px",
681            letterSpacing: "0.5px",
682            transition: "all 0.15s cubic-bezier(0.23, 1, 0.32, 1)",
683          },
684          spaceKey: {
685            background: `linear-gradient(145deg, ${adjustColorBrightness(
686              keyColor,
687              5,
688            )}, ${adjustColorBrightness(keyColor, -5)})`,
689            color: keyTextColor,
690            boxShadow: `0 3px 0 ${adjustColorBrightness(
691              keyColor,
692              -20,
693            )}, 0 0 10px rgba(0, 0, 0, 0.2), inset 0 1px 1px rgba(255, 255, 255, 0.1)`,
694            border: `1px solid ${adjustColorBrightness(keyColor, -10)}`,
695            fontSize: "11px",
696            fontWeight: "600",
697            fontFamily: '"Inter", "SF Pro Display", system-ui, sans-serif',
698            textShadow: `0 0 5px rgba(255, 255, 255, 0.3)`,
699            borderRadius: "6px",
700            letterSpacing: "0.5px",
701            transition: "all 0.15s cubic-bezier(0.23, 1, 0.32, 1)",
702          },
703          arrowKey: {
704            background: `linear-gradient(145deg, ${adjustColorBrightness(
705              keyColor,
706              -5,
707            )}, ${adjustColorBrightness(keyColor, -15)})`,
708            color: accentColor,
709            boxShadow: `0 3px 0 ${adjustColorBrightness(
710              keyColor,
711              -20,
712            )}, 0 0 10px rgba(0, 0, 0, 0.2), inset 0 1px 1px rgba(255, 255, 255, 0.1)`,
713            border: `1px solid ${adjustColorBrightness(keyColor, -10)}`,
714            fontSize: "11px",
715            fontWeight: "600",
716            fontFamily: '"Inter", "SF Pro Display", system-ui, sans-serif',
717            textShadow: `0 0 5px rgba(${hexToRgb(accentColor)}, 0.5)`,
718            borderRadius: "6px",
719            letterSpacing: "0.5px",
720            transition: "all 0.15s cubic-bezier(0.23, 1, 0.32, 1)",
721          },
722        }
723      case "neon":
724        return {
725          keyboard: {
726            background: "linear-gradient(to bottom, #121212, #1a1a1a)",
727            boxShadow:
728              "0 15px 40px rgba(0, 0, 0, 0.4), 0 10px 20px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.05)",
729            border: "1px solid #333",
730            borderRadius: "12px",
731            padding: "10px",
732          },
733          key: {
734            background: "linear-gradient(to bottom, #222222, #111111)",
735            color: "#00ffcc",
736            boxShadow: "0 3px 0 #000, 0 0 8px rgba(0, 0, 0, 0.3), inset 0 1px 1px rgba(255, 255, 255, 0.05)",
737            textShadow: "0 0 8px rgba(0, 255, 204, 0.7)",
738            border: "1px solid #333",
739            fontFamily: '"Orbitron", sans-serif',
740            fontWeight: "500",
741            fontSize: "11px",
742            borderRadius: "6px",
743            transition: "all 0.15s ease",
744          },
745          keyPressed: {
746            background: "linear-gradient(to bottom, #111111, #0a0a0a)",
747            boxShadow: "0 0 0 #000, 0 0 15px rgba(0, 255, 204, 0.7), inset 0 1px 2px rgba(0, 0, 0, 0.5)",
748            color: "#ffffff",
749            textShadow: "0 0 10px rgba(0, 255, 204, 1)",
750            border: "1px solid #00ffcc",
751            transform: "translateY(3px)",
752            fontSize: "11px",
753            fontWeight: "500",
754            fontFamily: '"Orbitron", sans-serif',
755            borderRadius: "6px",
756            transition: "all 0.05s ease",
757          },
758          keyHover: {
759            background: "linear-gradient(to bottom, #2a2a2a, #191919)",
760            boxShadow: "0 3px 0 #000, 0 0 12px rgba(0, 255, 204, 0.4), inset 0 1px 1px rgba(255, 255, 255, 0.1)",
761            color: "#ffffff",
762            textShadow: "0 0 8px rgba(0, 255, 204, 0.8)",
763            border: "1px solid #00aa88",
764            fontSize: "11px",
765            fontWeight: "500",
766            fontFamily: '"Orbitron", sans-serif',
767            borderRadius: "6px",
768            transition: "all 0.15s ease",
769          },
770          keyActive: {
771            background: "linear-gradient(to bottom, #222222, #111111)",
772            boxShadow: "0 3px 0 #000, 0 0 20px rgba(0, 255, 204, 0.8), inset 0 1px 1px rgba(255, 255, 255, 0.05)",
773            color: "#ffffff",
774            textShadow: "0 0 15px rgba(0, 255, 204, 1)",
775            border: "1px solid #00ffcc",
776            fontSize: "11px",
777            fontWeight: "500",
778            fontFamily: '"Orbitron", sans-serif',
779            borderRadius: "6px",
780            transition: "all 0.15s ease",
781          },
782          specialKey: {
783            background: "linear-gradient(to bottom, #191919, #0d0d0d)",
784            color: "#ff3399",
785            boxShadow: "0 3px 0 #000, 0 0 8px rgba(0, 0, 0, 0.3), inset 0 1px 1px rgba(255, 255, 255, 0.05)",
786            border: "1px solid #333",
787            fontSize: "10px",
788            fontWeight: "500",
789            fontFamily: '"Orbitron", sans-serif',
790            textShadow: "0 0 8px rgba(255, 51, 153, 0.7)",
791            borderRadius: "6px",
792            transition: "all 0.15s ease",
793          },
794          functionKey: {
795            background: "linear-gradient(to bottom, #191919, #0d0d0d)",
796            color: "#3399ff",
797            fontSize: "9px",
798            boxShadow: "0 3px 0 #000, 0 0 8px rgba(0, 0, 0, 0.3), inset 0 1px 1px rgba(255, 255, 255, 0.05)",
799            border: "1px solid #333",
800            fontWeight: "500",
801            fontFamily: '"Orbitron", sans-serif',
802            textShadow: "0 0 8px rgba(51, 153, 255, 0.7)",
803            borderRadius: "6px",
804            transition: "all 0.15s ease",
805          },
806          modifierKey: {
807            background: "linear-gradient(to bottom, #191919, #0d0d0d)",
808            color: "#ffcc00",
809            boxShadow: "0 3px 0 #000, 0 0 8px rgba(0, 0, 0, 0.3), inset 0 1px 1px rgba(255, 255, 255, 0.05)",
810            border: "1px solid #333",
811            fontSize: "10px",
812            fontWeight: "500",
813            fontFamily: '"Orbitron", sans-serif',
814            textShadow: "0 0 8px rgba(255, 204, 0, 0.7)",
815            borderRadius: "6px",
816            transition: "all 0.15s ease",
817          },
818          spaceKey: {
819            background: "linear-gradient(to bottom, #222222, #111111)",
820            color: "#00ffcc",
821            boxShadow: "0 3px 0 #000, 0 0 8px rgba(0, 0, 0, 0.3), inset 0 1px 1px rgba(255, 255, 255, 0.05)",
822            border: "1px solid #333",
823            fontSize: "11px",
824            fontWeight: "500",
825            fontFamily: '"Orbitron", sans-serif',
826            textShadow: "0 0 8px rgba(0, 255, 204, 0.7)",
827            borderRadius: "6px",
828            transition: "all 0.15s ease",
829          },
830          arrowKey: {
831            background: "linear-gradient(to bottom, #191919, #0d0d0d)",
832            color: "#ff9900",
833            boxShadow: "0 3px 0 #000, 0 0 8px rgba(0, 0, 0, 0.3), inset 0 1px 1px rgba(255, 255, 255, 0.05)",
834            border: "1px solid #333",
835            fontSize: "11px",
836            fontWeight: "500",
837            fontFamily: '"Orbitron", sans-serif',
838            textShadow: "0 0 8px rgba(255, 153, 0, 0.7)",
839            borderRadius: "6px",
840            transition: "all 0.15s ease",
841          },
842        }
843
844      case "pastel":
845        return {
846          keyboard: {
847            background: "linear-gradient(to bottom, #f0e6f6, #e7ddf0)",
848            boxShadow: "0 12px 30px rgba(200, 180, 220, 0.3), 0 8px 15px rgba(200, 180, 220, 0.2)",
849            border: "1px solid #d8cceb",
850            borderRadius: "16px",
851            padding: "12px",
852          },
853          key: {
854            background: "linear-gradient(to bottom, #ffffff, #f5f0f9)",
855            color: "#7b6d8d",
856            boxShadow: "0 3px 0 #d8cceb, 0 0 5px rgba(0, 0, 0, 0.03), inset 0 1px 0 rgba(255, 255, 255, 1)",
857            textShadow: "none",
858            border: "1px solid #e2d7f0",
859            fontFamily: '"Quicksand", "Avenir Next", sans-serif',
860            fontWeight: "500",
861            fontSize: "11px",
862            borderRadius: "10px",
863            transition: "all 0.2s ease",
864          },
865          keyPressed: {
866            background: "linear-gradient(to bottom, #f0e6f9, #e7ddf5)",
867            boxShadow: "0 0 0 #d8cceb, inset 0 1px 2px rgba(0, 0, 0, 0.05)",
868            color: "#9370db",
869            textShadow: "none",
870            border: "1px solid #d8cceb",
871            transform: "translateY(3px)",
872            fontSize: "11px",
873            fontWeight: "500",
874            fontFamily: '"Quicksand", "Avenir Next", sans-serif',
875            borderRadius: "10px",
876            transition: "all 0.1s ease",
877          },
878          keyHover: {
879            background: "linear-gradient(to bottom, #ffffff, #f9f5fc)",
880            boxShadow: "0 3px 0 #d8cceb, 0 0 8px rgba(155, 122, 188, 0.2), inset 0 1px 0 rgba(255, 255, 255, 1)",
881            color: "#9370db",
882            border: "1px solid #d8cceb",
883            fontSize: "11px",
884            fontWeight: "500",
885            fontFamily: '"Quicksand", "Avenir Next", sans-serif',
886            textShadow: "none",
887            borderRadius: "10px",
888            transition: "all 0.2s ease",
889          },
890          keyActive: {
891            background: "linear-gradient(to bottom, #ffffff, #f5f0f9)",
892            boxShadow: "0 3px 0 #d8cceb, 0 0 12px rgba(155, 122, 188, 0.4), inset 0 1px 0 rgba(255, 255, 255, 1)",
893            color: "#9370db",
894            textShadow: "0 0 3px rgba(155, 122, 188, 0.3)",
895            border: "1px solid #b79ce8",
896            fontSize: "11px",
897            fontWeight: "500",
898            fontFamily: '"Quicksand", "Avenir Next", sans-serif',
899            borderRadius: "10px",
900            transition: "all 0.2s ease",
901          },
902          specialKey: {
903            background: "linear-gradient(to bottom, #f9f0fc, #f0e6f6)",
904            color: "#9382ab",
905            boxShadow: "0 3px 0 #d8cceb, 0 0 5px rgba(0, 0, 0, 0.03), inset 0 1px 0 rgba(255, 255, 255, 1)",
906            border: "1px solid #e2d7f0",
907            fontSize: "10px",
908            fontWeight: "500",
909            fontFamily: '"Quicksand", "Avenir Next", sans-serif',
910            textShadow: "none",
911            borderRadius: "10px",
912            transition: "all 0.2s ease",
913          },
914          functionKey: {
915            background: "linear-gradient(to bottom, #f0e6f6, #e7ddf0)",
916            color: "#a58cc4",
917            fontSize: "9px",
918            boxShadow: "0 3px 0 #d8cceb, 0 0 5px rgba(0, 0, 0, 0.03), inset 0 1px 0 rgba(255, 255, 255, 1)",
919            border: "1px solid #e2d7f0",
920            fontWeight: "500",
921            fontFamily: '"Quicksand", "Avenir Next", sans-serif',
922            textShadow: "none",
923            borderRadius: "10px",
924            transition: "all 0.2s ease",
925          },
926          modifierKey: {
927            background: "linear-gradient(to bottom, #f0e6f6, #e7ddf0)",
928            color: "#9382ab",
929            boxShadow: "0 3px 0 #d8cceb, 0 0 5px rgba(0, 0, 0, 0.03), inset 0 1px 0 rgba(255, 255, 255, 1)",
930            border: "1px solid #e2d7f0",
931            fontSize: "10px",
932            fontWeight: "500",
933            fontFamily: '"Quicksand", "Avenir Next", sans-serif',
934            textShadow: "none",
935            borderRadius: "10px",
936            transition: "all 0.2s ease",
937          },
938          spaceKey: {
939            background: "linear-gradient(to bottom, #ffffff, #f5f0f9)",
940            color: "#7b6d8d",
941            boxShadow: "0 3px 0 #d8cceb, 0 0 5px rgba(0, 0, 0, 0.03), inset 0 1px 0 rgba(255, 255, 255, 1)",
942            border: "1px solid #e2d7f0",
943            fontSize: "11px",
944            fontWeight: "500",
945            fontFamily: '"Quicksand", "Avenir Next", sans-serif',
946            textShadow: "none",
947            borderRadius: "10px",
948            transition: "all 0.2s ease",
949          },
950          arrowKey: {
951            background: "linear-gradient(to bottom, #f0e6f6, #e7ddf0)",
952            color: "#b79ce8",
953            boxShadow: "0 3px 0 #d8cceb, 0 0 5px rgba(0, 0, 0, 0.03), inset 0 1px 0 rgba(255, 255, 255, 1)",
954            border: "1px solid #e2d7f0",
955            fontSize: "11px",
956            fontWeight: "500",
957            fontFamily: '"Quicksand", "Avenir Next", sans-serif',
958            textShadow: "none",
959            borderRadius: "10px",
960            transition: "all 0.2s ease",
961          },
962        }
963
964      case "mechanical":
965        return {
966          keyboard: {
967            background: "linear-gradient(to bottom, #2c2c2c, #1a1a1a)",
968            boxShadow: "0 12px 30px rgba(0, 0, 0, 0.5), 0 8px 15px rgba(0, 0, 0, 0.4)",
969            border: "2px solid #000",
970            borderRadius: "8px",
971            padding: "10px",
972          },
973          key: {
974            background: "linear-gradient(to bottom, #363636, #222222)",
975            color: "#ddd",
976            boxShadow: "0 2px 0 #000, inset 0 1px 0 rgba(255, 255, 255, 0.1), inset 0 0 5px rgba(255, 255, 255, 0.05)",
977            textShadow: "none",
978            border: "1px solid #111",
979            fontFamily: '"IBM Plex Mono", monospace',
980            fontWeight: "600",
981            fontSize: "11px",
982            borderRadius: "5px",
983            transition: "all 0.08s ease",
984            height: "40px",
985          },
986          keyPressed: {
987            background: "linear-gradient(to bottom, #222222, #1a1a1a)",
988            boxShadow: "0 0 0 #000, inset 0 0 10px rgba(0, 0, 0, 0.8)",
989            color: "#fff",
990            textShadow: "none",
991            border: "1px solid #000",
992            transform: "translateY(2px)",
993            fontSize: "11px",
994            fontWeight: "600",
995            fontFamily: '"IBM Plex Mono", monospace',
996            borderRadius: "5px",
997            transition: "all 0.02s ease",
998            height: "40px",
999          },
1000          keyHover: {
1001            background: "linear-gradient(to bottom, #3a3a3a, #262626)",
1002            boxShadow: "0 2px 0 #000, inset 0 1px 0 rgba(255, 255, 255, 0.1), inset 0 0 5px rgba(255, 255, 255, 0.08)",
1003            color: "#fff",
1004            border: "1px solid #111",
1005            fontSize: "11px",
1006            fontWeight: "600",
1007            fontFamily: '"IBM Plex Mono", monospace',
1008            textShadow: "none",
1009            borderRadius: "5px",
1010            transition: "all 0.08s ease",
1011            height: "40px",
1012          },
1013          keyActive: {
1014            background: "linear-gradient(to bottom, #363636, #222222)",
1015            boxShadow: "0 2px 0 #000, inset 0 1px 0 rgba(255, 255, 255, 0.1), inset 0 0 5px rgba(255, 255, 255, 0.05)",
1016            color: "#ff7700",
1017            textShadow: "0 0 5px rgba(255, 119, 0, 0.5)",
1018            border: "1px solid #ff7700",
1019            fontSize: "11px",
1020            fontWeight: "600",
1021            fontFamily: '"IBM Plex Mono", monospace',
1022            borderRadius: "5px",
1023            transition: "all 0.08s ease",
1024            height: "40px",
1025          },
1026          specialKey: {
1027            background: "linear-gradient(to bottom, #2d2d2d, #1d1d1d)",
1028            color: "#aaa",
1029            boxShadow: "0 2px 0 #000, inset 0 1px 0 rgba(255, 255, 255, 0.1), inset 0 0 5px rgba(255, 255, 255, 0.05)",
1030            border: "1px solid #111",
1031            fontSize: "10px",
1032            fontWeight: "600",
1033            fontFamily: '"IBM Plex Mono", monospace',
1034            textShadow: "none",
1035            borderRadius: "5px",
1036            transition: "all 0.08s ease",
1037            height: "40px",
1038          },
1039          functionKey: {
1040            background: "linear-gradient(to bottom, #2a2a2a, #1a1a1a)",
1041            color: "#888",
1042            fontSize: "9px",
1043            boxShadow: "0 2px 0 #000, inset 0 1px 0 rgba(255, 255, 255, 0.1), inset 0 0 5px rgba(255, 255, 255, 0.05)",
1044            border: "1px solid #111",
1045            fontWeight: "600",
1046            fontFamily: '"IBM Plex Mono", monospace',
1047            textShadow: "none",
1048            borderRadius: "5px",
1049            transition: "all 0.08s ease",
1050            height: "30px",
1051          },
1052          modifierKey: {
1053            background: "linear-gradient(to bottom, #2d2d2d, #1d1d1d)",
1054            color: "#bbb",
1055            boxShadow: "0 2px 0 #000, inset 0 1px 0 rgba(255, 255, 255, 0.1), inset 0 0 5px rgba(255, 255, 255, 0.05)",
1056            border: "1px solid #111",
1057            fontSize: "10px",
1058            fontWeight: "600",
1059            fontFamily: '"IBM Plex Mono", monospace',
1060            textShadow: "none",
1061            borderRadius: "5px",
1062            transition: "all 0.08s ease",
1063            height: "40px",
1064          },
1065          spaceKey: {
1066            background: "linear-gradient(to bottom, #363636, #222222)",
1067            color: "#ddd",
1068            boxShadow: "0 2px 0 #000, inset 0 1px 0 rgba(255, 255, 255, 0.1), inset 0 0 5px rgba(255, 255, 255, 0.05)",
1069            border: "1px solid #111",
1070            fontSize: "11px",
1071            fontWeight: "600",
1072            fontFamily: '"IBM Plex Mono", monospace',
1073            textShadow: "none",
1074            borderRadius: "5px",
1075            transition: "all 0.08s ease",
1076            height: "40px",
1077          },
1078          arrowKey: {
1079            background: "linear-gradient(to bottom, #2d2d2d, #1d1d1d)",
1080            color: "#ff7700",
1081            boxShadow: "0 2px 0 #000, inset 0 1px 0 rgba(255, 255, 255, 0.1), inset 0 0 5px rgba(255, 255, 255, 0.05)",
1082            border: "1px solid #111",
1083            fontSize: "11px",
1084            fontWeight: "600",
1085            fontFamily: '"IBM Plex Mono", monospace',
1086            textShadow: "none",
1087            borderRadius: "5px",
1088            transition: "all 0.08s ease",
1089            height: "40px",
1090          },
1091        }
1092    }
1093  }
1094  const getKeyStyle = (key: KeyObject, isPressed: boolean, isActive: boolean) => {
1095    const size = key.size || 1
1096    const keyType = getKeyType(key)
1097    let baseStyle = { ...themeStyles.key }
1098    if (keyType === "special" && themeStyles.specialKey) {
1099      baseStyle = { ...baseStyle, ...themeStyles.specialKey }
1100    } else if (keyType === "function" && themeStyles.functionKey) {
1101      baseStyle = { ...baseStyle, ...themeStyles.functionKey }
1102    } else if (keyType === "modifier" && themeStyles.modifierKey) {
1103      baseStyle = { ...baseStyle, ...themeStyles.modifierKey }
1104    } else if (keyType === "space" && themeStyles.spaceKey) {
1105      baseStyle = { ...baseStyle, ...themeStyles.spaceKey }
1106    } else if (keyType === "arrow" && themeStyles.arrowKey) {
1107      baseStyle = { ...baseStyle, ...themeStyles.arrowKey }
1108    }
1109    if (isActive && themeStyles.keyActive) {
1110      baseStyle = { ...baseStyle, ...themeStyles.keyActive }
1111    }
1112    return {
1113      position: "relative" as const,
1114      width: `${calcKeyWidth(size)}px`,
1115      height: `${keyHeight}px`,
1116      ...(isPressed ? { ...baseStyle, ...themeStyles.keyPressed } : baseStyle),
1117      borderRadius: baseStyle.borderRadius || "4px",
1118      display: "flex" as const,
1119      justifyContent: "center" as const,
1120      alignItems: "center" as const,
1121      cursor: "pointer",
1122      userSelect: "none" as const,
1123      transition: `all ${keyPressAnimationDuration}ms cubic-bezier(0.2, 0.8, 0.2, 1)`,
1124      transform: isPressed ? themeStyles.keyPressed.transform || "translateY(2px)" : "translateY(0)",
1125      fontSize: baseStyle.fontSize || "11px",
1126      fontWeight: baseStyle.fontWeight || 500,
1127      fontFamily: baseStyle.fontFamily || "inherit",
1128      padding: "0",
1129      margin: "0",
1130      letterSpacing: baseStyle.letterSpacing || "normal",
1131      willChange: "transform, box-shadow",
1132    }
1133  }
1134  function adjustColorBrightness(hex: string, percent: number): string {
1135    const num = Number.parseInt(hex.replace("#", ""), 16)
1136    const amt = Math.round(2.55 * percent)
1137    const R = (num >> 16) + amt
1138    const G = ((num >> 8) & 0x00ff) + amt
1139    const B = (num & 0x0000ff) + amt
1140
1141    return (
1142      "#" +
1143      (
1144        0x1000000 +
1145        (R < 255 ? (R < 0 ? 0 : R) : 255) * 0x10000 +
1146        (G < 255 ? (G < 0 ? 0 : G) : 255) * 0x100 +
1147        (B < 255 ? (B < 0 ? 0 : B) : 255)
1148      )
1149        .toString(16)
1150        .slice(1)
1151    )
1152  }
1153
1154  function hexToRgb(hex: string): string {
1155    const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
1156    return result
1157      ? `${Number.parseInt(result[1], 16)}, ${Number.parseInt(result[2], 16)}, ${Number.parseInt(result[3], 16)}`
1158      : "0, 255, 255"
1159  }
1160  const getKeyType = (key: KeyObject): string => {
1161    if (!key.code) return "regular"
1162    if (key.type === "numpad") return "numpad"
1163    if (["Space"].includes(key.code)) return "space"
1164    if (["F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "Escape"].includes(key.code))
1165      return "function"
1166    if (
1167      [
1168        "ShiftLeft",
1169        "ShiftRight",
1170        "ControlLeft",
1171        "ControlRight",
1172        "AltLeft",
1173        "AltRight",
1174        "MetaLeft",
1175        "MetaRight",
1176        "CapsLock",
1177      ].includes(key.code)
1178    )
1179      return "modifier"
1180    if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(key.code)) return "arrow"
1181    if (
1182      [
1183        "Backspace",
1184        "Tab",
1185        "Enter",
1186        "Delete",
1187        "Home",
1188        "End",
1189        "PageUp",
1190        "PageDown",
1191        "Insert",
1192        "PrintScreen",
1193        "ScrollLock",
1194        "Pause",
1195        "ContextMenu",
1196      ].includes(key.code)
1197    )
1198      return "special"
1199
1200    return "regular"
1201  }
1202  const isKeyActive = (code: string | undefined): boolean => {
1203    if (!code) return false
1204    return activeKeys.includes(code)
1205  }
1206
1207  const renderKeyIcon = (key: KeyObject) => {
1208    if (!key.icon) return null
1209
1210    switch (key.icon) {
1211      case "windows":
1212        return <Command className="h-3 w-3" />
1213      case "menu":
1214        return <Menu className="h-3 w-3" />
1215      case "capslock":
1216        return <ChevronsUp className="h-3 w-3 mr-1" />
1217      case "arrowup":
1218        return <ArrowUp className="h-3 w-3" />
1219      case "arrowdown":
1220        return <ArrowDown className="h-3 w-3" />
1221      case "arrowleft":
1222        return <ArrowLeft className="h-3 w-3" />
1223      case "arrowright":
1224        return <ArrowRight className="h-3 w-3" />
1225      default:
1226        return null
1227    }
1228  }
1229
1230  const themeStyles = getThemeStyles()
1231  const keyboardLayout = getKeyboardLayout()
1232  const keyUnit = 40
1233  const keySpacing = 6
1234  const keyHeight = 40
1235  const calcKeyWidth = (size: number): number => keyUnit * size + keySpacing * (size - 1)
1236  const keyboardStyle = {
1237    ...themeStyles.keyboard,
1238    display: "flex" as const,
1239    flexDirection: "column" as const,
1240    padding: "20px",
1241    borderRadius: themeStyles.keyboard.borderRadius || "10px",
1242    transform: `perspective(${perspective}px) rotateX(${rotateX}deg)`,
1243    position: "relative" as const,
1244    gap: `${keySpacing}px`,
1245    maxWidth: "fit-content",
1246    transition: "all 0.3s ease",
1247  }
1248  const calculateMainRowWidth = (row: KeyboardRow): number => {
1249    let totalWidth = 0
1250    for (const key of row.keys) {
1251      if (key.spacer) {
1252        totalWidth += calcKeyWidth(key.size)
1253      } else if (key.type === "light") {
1254        totalWidth += 8 + (key.size || 0.5) * 4
1255      } else {
1256        totalWidth += calcKeyWidth(key.size)
1257      }
1258    }
1259    if (row.keys.length > 0) {
1260      totalWidth += (row.keys.length - 1) * keySpacing
1261    }
1262    return totalWidth
1263  }
1264  const calculateMaxMainRowWidth = (): number => {
1265    let maxWidth = 0
1266    for (const row of keyboardLayout) {
1267      if (row.keys.length > 0) {
1268        const rowWidth = calculateMainRowWidth(row)
1269        maxWidth = Math.max(maxWidth, rowWidth)
1270      }
1271    }
1272    return maxWidth
1273  }
1274
1275  const maxMainRowWidth = calculateMaxMainRowWidth()
1276  return (
1277    <div
1278      className={`keyboard-container ${className}`}
1279      style={{
1280        display: "flex",
1281        justifyContent: "center",
1282        alignItems: "center",
1283        padding: "20px",
1284      }}
1285      {...props}
1286    >
1287      <div className="keyboard-wrapper" style={{ display: "flex", gap: "20px" }}>
1288        <div className="keyboard" style={keyboardStyle}>
1289          {keyboardLayout.map((row, rowIndex) => {
1290            if (row.function && !showFunctionKeys) return null
1291            const mainRowWidth = calculateMainRowWidth(row)
1292            return (
1293              <div
1294                key={`row-${rowIndex}`}
1295                className="keyboard-row"
1296                style={{
1297                  display: "flex",
1298                  gap: `${keySpacing}px`,
1299                  position: "relative",
1300                  marginTop: row.function && rowIndex === 0 ? "10px" : "0",
1301                  justifyContent: "flex-start",
1302                }}
1303              >
1304                <div style={{ display: "flex", gap: `${keySpacing}px` }}>
1305                  {row.keys.map((key, keyIndex) => {
1306                    if (key.spacer) {
1307                      return (
1308                        <div
1309                          key={`spacer-${rowIndex}-${keyIndex}`}
1310                          style={{
1311                            width: `${calcKeyWidth(key.size)}px`,
1312                            height: `${keyHeight}px`,
1313                            background: "transparent",
1314                          }}
1315                        />
1316                      )
1317                    }
1318
1319                    if (key.type === "light") {
1320                      return (
1321                        <div
1322                          key={`light-${rowIndex}-${keyIndex}`}
1323                          style={{
1324                            width: "8px",
1325                            height: "8px",
1326                            borderRadius: "50%",
1327                            background: accentColor,
1328                            boxShadow: `0 0 8px rgba(${hexToRgb(accentColor)}, 0.7)`,
1329                            position: "relative",
1330                            marginTop: "4px",
1331                            marginLeft: keyIndex === 0 ? "0" : "6px",
1332                          }}
1333                        />
1334                      )
1335                    }
1336                    const isPressed = pressedKeys.has(key.code || "")
1337                    const isActive = isKeyActive(key.code)
1338                    return (
1339                      <div
1340                        key={`key-${rowIndex}-${keyIndex}`}
1341                        data-key={key.code}
1342                        className={`key ${key.code} ${isActive ? "active" : ""}`}
1343                        style={getKeyStyle(key, isPressed, isActive)}
1344                        onMouseDown={() => key.code && handleKeyDown(key.code)}
1345                        onMouseUp={() => key.code && handleKeyUp(key.code)}
1346                        onMouseLeave={() => key.code && pressedKeys.has(key.code) && handleKeyUp(key.code)}
1347                        onTouchStart={(e) => {
1348                          e.preventDefault()
1349                        }}
1350                        onTouchEnd={() => key.code && handleKeyUp(key.code)}
1351                        onMouseEnter={() => {
1352                          const element = document.querySelector(`.key.${key.code}`) as HTMLElement
1353                          if (element && !pressedKeys.has(key.code || "") && !isActive) {
1354                            Object.assign(element.style, themeStyles.keyHover)
1355                          }
1356                        }}
1357                        onMouseOut={() => {
1358                          const element = document.querySelector(`.key.${key.code}`) as HTMLElement
1359                          if (element && !pressedKeys.has(key.code || "") && !isActive) {
1360                            const keyType = getKeyType(key)
1361                            let baseStyle = { ...themeStyles.key }
1362
1363                            if (keyType === "special" && themeStyles.specialKey) {
1364                              baseStyle = {
1365                                ...baseStyle,
1366                                ...themeStyles.specialKey,
1367                              }
1368                            } else if (keyType === "function" && themeStyles.functionKey) {
1369                              baseStyle = {
1370                                ...baseStyle,
1371                                ...themeStyles.functionKey,
1372                              }
1373                            } else if (keyType === "modifier" && themeStyles.modifierKey) {
1374                              baseStyle = {
1375                                ...baseStyle,
1376                                ...themeStyles.modifierKey,
1377                              }
1378                            } else if (keyType === "space" && themeStyles.spaceKey) {
1379                              baseStyle = {
1380                                ...baseStyle,
1381                                ...themeStyles.spaceKey,
1382                              }
1383                            } else if (keyType === "arrow" && themeStyles.arrowKey) {
1384                              baseStyle = {
1385                                ...baseStyle,
1386                                ...themeStyles.arrowKey,
1387                              }
1388                            }
1389
1390                            Object.assign(element.style, baseStyle)
1391                          }
1392                        }}
1393                      >
1394                        <div style={{ display: "flex", alignItems: "center", justifyContent: "center" }}>
1395                          {renderKeyIcon(key)}
1396                          {key.label && <span>{key.label}</span>}
1397                        </div>
1398                      </div>
1399                    )
1400                  })}
1401                </div>
1402                {showNavigationCluster && row.nav && row.nav.length > 0 && (
1403                  <div
1404                    className="nav-cluster"
1405                    style={{
1406                      display: "flex",
1407                      gap: `${keySpacing}px`,
1408                      marginLeft: `${Math.max(0, maxMainRowWidth - mainRowWidth + keySpacing * 2)}px`,
1409                    }}
1410                  >
1411                    {row.nav.map((key, keyIndex) => {
1412                      if (key.spacer) {
1413                        return (
1414                          <div
1415                            key={`nav-spacer-${rowIndex}-${keyIndex}`}
1416                            style={{
1417                              width: `${calcKeyWidth(key.size)}px`,
1418                              height: `${keyHeight}px`,
1419                              background: "transparent",
1420                            }}
1421                          />
1422                        )
1423                      }
1424
1425                      if (key.type === "light") {
1426                        return (
1427                          <div
1428                            key={`nav-light-${rowIndex}-${keyIndex}`}
1429                            style={{
1430                              width: "8px",
1431                              height: "8px",
1432                              borderRadius: "50%",
1433                              background: accentColor,
1434                              boxShadow: `0 0 8px rgba(${hexToRgb(accentColor)}, 0.7)`,
1435                              position: "relative",
1436                              marginTop: "4px",
1437                              marginLeft: keyIndex === 0 ? "0" : "6px",
1438                            }}
1439                          />
1440                        )
1441                      }
1442                      const isPressed = pressedKeys.has(key.code || "")
1443                      const isActive = isKeyActive(key.code)
1444                      return (
1445                        <div
1446                          key={`nav-key-${rowIndex}-${keyIndex}`}
1447                          data-key={key.code}
1448                          className={`key ${key.code} ${isActive ? "active" : ""}`}
1449                          style={getKeyStyle(key, isPressed, isActive)}
1450                          onMouseDown={() => key.code && handleKeyDown(key.code)}
1451                          onMouseUp={() => key.code && handleKeyUp(key.code)}
1452                          onMouseLeave={() => key.code && pressedKeys.has(key.code) && handleKeyUp(key.code)}
1453                          onTouchStart={(e) => {
1454                            e.preventDefault()
1455                          }}
1456                          onTouchEnd={() => key.code && handleKeyUp(key.code)}
1457                          onMouseEnter={() => {
1458                            const element = document.querySelector(`.key.${key.code}`) as HTMLElement
1459                            if (element && !pressedKeys.has(key.code || "") && !isActive) {
1460                              Object.assign(element.style, themeStyles.keyHover)
1461                            }
1462                          }}
1463                          onMouseOut={() => {
1464                            const element = document.querySelector(`.key.${key.code}`) as HTMLElement
1465                            if (element && !pressedKeys.has(key.code || "") && !isActive) {
1466                              const keyType = getKeyType(key)
1467                              let baseStyle = { ...themeStyles.key }
1468
1469                              if (keyType === "special" && themeStyles.specialKey) {
1470                                baseStyle = {
1471                                  ...baseStyle,
1472                                  ...themeStyles.specialKey,
1473                                }
1474                              } else if (keyType === "function" && themeStyles.functionKey) {
1475                                baseStyle = {
1476                                  ...baseStyle,
1477                                  ...themeStyles.functionKey,
1478                                }
1479                              } else if (keyType === "modifier" && themeStyles.modifierKey) {
1480                                baseStyle = {
1481                                  ...baseStyle,
1482                                  ...themeStyles.modifierKey,
1483                                }
1484                              } else if (keyType === "space" && themeStyles.spaceKey) {
1485                                baseStyle = {
1486                                  ...baseStyle,
1487                                  ...themeStyles.spaceKey,
1488                                }
1489                              } else if (keyType === "arrow" && themeStyles.arrowKey) {
1490                                baseStyle = {
1491                                  ...baseStyle,
1492                                  ...themeStyles.arrowKey,
1493                                }
1494                              }
1495                              Object.assign(element.style, baseStyle)
1496                            }
1497                          }}
1498                        >
1499                          <div style={{ display: "flex", alignItems: "center", justifyContent: "center" }}>
1500                            {renderKeyIcon(key)}
1501                            {key.label && <span>{key.label}</span>}
1502                          </div>
1503                        </div>
1504                      )
1505                    })}
1506                  </div>
1507                )}
1508              </div>
1509            )
1510          })}
1511        </div>
1512      </div>
1513    </div>
1514  )
1515}
1516
1517export default InteractiveKeyboard
1518
3

Final Steps

Update Import Paths

Make sure to update the import paths in the component code to match your project structure. For example, change @/components/ui/button to match your UI components location.

Props

NameTypeDefaultDescription
layoutstringstandardKeyboard layout (standard or compact)
showFunctionKeysbooleantrueShow function keys (F1-F12) row
showNavigationClusterbooleantrueShow navigation key cluster
activeKeysstring[][]Array of key codes to highlight as active
activeKeyGlowColorstring#6366f1Glow color for active keys
activeKeyGlowIntensitynumber0.8Intensity of glow effect for active keys (0-1)
themestringcyberpunkKeyboard theme (cyberpunk, minimal, retro, mechanical, neon, pastel)
keyColorstring#2a2a2aBase color for keyboard keys
keyTextColorstring#ffffffText color for keyboard keys
accentColorstring#6366f1Accent color for special keys and highlights
keyPressedColorstring#333333Color for pressed keys
keyPressAnimationDurationnumber150Duration of key press animation in milliseconds
onKeyPressfunction() => {}Callback function when a key is pressed (code: string, key?: string) => void
onKeyReleasefunction() => {}Callback function when a key is released (code: string, key?: string) => void
classNamestring-Additional CSS classes to apply
allowPhysicalKeyboardbooleantrueAllow physical keyboard integration
perspectivenumber1000CSS perspective value for 3D effect
rotateXnumber10X-axis rotation angle in degrees