Keyboards are an essential part of our daily digital interactions, offering a variety of designs and functionalities to suit different needs. From mechanical to membrane, each type has its unique features and benefits.
In this article, we will explore 10 keyboard examples that highlight the diversity and innovation in keyboard technology. Whether you're a gamer, a writer, or a casual user, there's a keyboard out there for you.
CODE1
Here's the code:
CODETEXT1
CODE2
Here's the code:
CODETEXT2
CODE3
Here's the code:
CODETEXT3
CODE4
Here's the code:
CODETEXT4
CODE5
Here's the code:
CODETEXT5
Designers and developers, elevate your keyboard design with Subframe's drag-and-drop interface. Its intuitive, responsive canvas ensures pixel-perfect UI every time, making it a favorite among professionals.
Join the community of satisfied users and start for free today!
CODE6
Here's the code:
CODETEXT6
CODE7
Here's the code:
CODETEXT7
CODE8
Here's the code:
CODETEXT8
CODE9
Here's the code:
CODETEXT9
CODE10
Here's the code:
CODETEXT10
Ready to elevate your UI design game? With Subframe, you can create stunning, pixel-perfect interfaces, including keyboards, in minutes. Our drag-and-drop editor ensures efficiency and precision.
Don't wait! Start for free and begin designing immediately. Join the community of professionals who trust Subframe for their UI needs.
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>NimbleChat Keyboard</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; user-select: none; } body { display: flex; flex-direction: column; justify-content: flex-end; align-items: center; height: 700px; width: 700px; margin: 0 auto; background: #f9f9fb; overflow: hidden; position: relative; } .chat-container { width: 100%; height: 100%; padding: 20px; display: flex; flex-direction: column; justify-content: flex-end; background: linear-gradient(135deg, #f5f7fa 0%, #e4eaef 100%); border-radius: 10px; overflow: hidden; position: relative; } .chat-header { position: absolute; top: 0; left: 0; width: 100%; padding: 15px 20px; background: #6c5ce7; display: flex; justify-content: space-between; align-items: center; color: white; z-index: 10; box-shadow: 0 2px 5px rgba(0,0,0,0.1); } .chat-header h1 { font-size: 1.2rem; font-weight: 600; } .user-info { display: flex; align-items: center; gap: 10px; } .user-avatar { width: 30px; height: 30px; border-radius: 50%; background: #a29bfe; display: flex; justify-content: center; align-items: center; color: white; font-weight: bold; } .messages-container { flex-grow: 1; overflow-y: auto; padding: 70px 0 10px; display: flex; flex-direction: column; gap: 10px; } .message { max-width: 80%; padding: 12px 16px; border-radius: 18px; margin-bottom: 8px; animation: fadeIn 0.3s ease-out; position: relative; word-wrap: break-word; } .message.sent { align-self: flex-end; background-color: #6c5ce7; color: white; border-bottom-right-radius: 5px; } .message.received { align-self: flex-start; background-color: #e2e2e2; color: #333; border-bottom-left-radius: 5px; } .message-time { font-size: 0.7rem; opacity: 0.7; margin-top: 5px; text-align: right; } .input-container { position: relative; width: 100%; margin-top: 10px; border-radius: 24px; background: white; padding: 12px 15px; display: flex; align-items: center; box-shadow: 0 2px 10px rgba(0,0,0,0.1); transition: all 0.3s ease; } .input-container:focus-within { box-shadow: 0 4px 15px rgba(108, 92, 231, 0.2); } .message-input { flex-grow: 1; border: none; outline: none; font-size: 1rem; background: transparent; padding: 0 10px; max-height: 120px; overflow-y: auto; } .send-btn { background: #6c5ce7; border: none; width: 40px; height: 40px; border-radius: 50%; color: white; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: all 0.2s ease; } .send-btn:hover { background: #5849cc; transform: scale(1.05); } .send-btn:active { transform: scale(0.95); } .send-btn svg { width: 18px; height: 18px; } .keyboard-container { width: 100%; background: #eaecf2; border-radius: 15px 15px 0 0; padding: 10px; box-shadow: 0 -2px 10px rgba(0,0,0,0.1); transition: transform 0.3s ease; } .keyboard-row { display: flex; justify-content: center; margin-bottom: 8px; gap: 6px; } .key { flex-grow: 1; height: 45px; background: white; border-radius: 12px; display: flex; justify-content: center; align-items: center; font-size: 16px; font-weight: 500; color: #444; cursor: pointer; transition: all 0.15s ease; position: relative; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.05); } .key:hover { background: #f5f5f5; } .key:active, .key.pressed { transform: translateY(2px); background: #eee; box-shadow: 0 1px 2px rgba(0,0,0,0.1); } .key.space { flex-grow: 6; } .key.action { background: #e2e0f5; font-weight: 600; color: #6c5ce7; } .key.action:hover { background: #d5d1f0; } .key.send { background: #6c5ce7; color: white; } .key.send:hover { background: #5849cc; } .key-ripple { position: absolute; background: rgba(108, 92, 231, 0.2); border-radius: 50%; transform: scale(0); animation: ripple 0.6s linear; pointer-events: none; } .emojis-row { display: flex; justify-content: space-between; padding: 0 5px; margin-bottom: 10px; } .emoji { font-size: 20px; cursor: pointer; transition: transform 0.2s ease; } .emoji:hover { transform: scale(1.2); } .typing-indicator { display: none; align-self: flex-start; background-color: #e2e2e2; color: #333; padding: 12px 16px; border-radius: 18px; margin-bottom: 8px; animation: typing 1s infinite; border-bottom-left-radius: 5px; } .typing-indicator span { display: inline-block; width: 7px; height: 7px; background-color: #666; border-radius: 50%; margin-right: 3px; animation: dot-pulse 1.5s infinite; } .typing-indicator span:nth-child(2) { animation-delay: 0.2s; } .typing-indicator span:nth-child(3) { animation-delay: 0.4s; } .speed-indicator { position: absolute; top: 60px; right: 20px; background: rgba(108, 92, 231, 0.9); color: white; padding: 5px 10px; border-radius: 15px; font-size: 0.8rem; opacity: 0; transition: opacity 0.3s ease; z-index: 5; } .speed-indicator.show { opacity: 1; } @keyframes ripple { to { transform: scale(4); opacity: 0; } } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } @keyframes typing { 0% { opacity: 0.5; } 50% { opacity: 1; } 100% { opacity: 0.5; } } @keyframes dot-pulse { 0% { transform: scale(1); } 40% { transform: scale(1.5); } 100% { transform: scale(1); } } /* Responsive adjustments */ @media (max-width: 700px) { .keyboard-row { gap: 4px; } .key { height: 40px; font-size: 14px; } .message { max-width: 90%; } } @media (max-width: 500px) { .keyboard-row { gap: 3px; } .key { height: 35px; font-size: 12px; border-radius: 8px; } .chat-header h1 { font-size: 1rem; } } </style> </head> <body> <div class="chat-container"> <div class="chat-header"> <h1>NimbleChat</h1> <div class="user-info"> <span>Alex</span> <div class="user-avatar">A</div> </div> </div> <div class="messages-container"> <div class="message received"> Hey! How's the project coming along? <div class="message-time">10:25 AM</div> </div> <div class="message sent"> Almost done! Just finishing up the design system for the mobile app. <div class="message-time">10:27 AM</div> </div> <div class="message received"> That's great! Can you share a preview of the keyboard interface? <div class="message-time">10:28 AM</div> </div> <div class="typing-indicator"> <span></span> <span></span> <span></span> </div> </div> <div class="speed-indicator"> Fast Typing! Keys adapting. </div> <div class="input-container"> <div class="message-input" contenteditable="true" placeholder="Type a message..."></div> <button class="send-btn"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <line x1="22" y1="2" x2="11" y2="13"></line> <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon> </svg> </button> </div> <div class="keyboard-container"> <div class="emojis-row"> <div class="emoji">😊</div> <div class="emoji">👍</div> <div class="emoji">❤️</div> <div class="emoji">🎉</div> <div class="emoji">🤔</div> <div class="emoji">😂</div> <div class="emoji">👏</div> <div class="emoji">🔥</div> </div> <div class="keyboard-row"> <div class="key" data-key="q">Q</div> <div class="key" data-key="w">W</div> <div class="key" data-key="e">E</div> <div class="key" data-key="r">R</div> <div class="key" data-key="t">T</div> <div class="key" data-key="y">Y</div> <div class="key" data-key="u">U</div> <div class="key" data-key="i">I</div> <div class="key" data-key="o">O</div> <div class="key" data-key="p">P</div> </div> <div class="keyboard-row"> <div class="key" data-key="a">A</div> <div class="key" data-key="s">S</div> <div class="key" data-key="d">D</div> <div class="key" data-key="f">F</div> <div class="key" data-key="g">G</div> <div class="key" data-key="h">H</div> <div class="key" data-key="j">J</div> <div class="key" data-key="k">K</div> <div class="key" data-key="l">L</div> </div> <div class="keyboard-row"> <div class="key action" data-key="shift">⇧</div> <div class="key" data-key="z">Z</div> <div class="key" data-key="x">X</div> <div class="key" data-key="c">C</div> <div class="key" data-key="v">V</div> <div class="key" data-key="b">B</div> <div class="key" data-key="n">N</div> <div class="key" data-key="m">M</div> <div class="key action" data-key="backspace">⌫</div> </div> <div class="keyboard-row"> <div class="key action" data-key="123">123</div> <div class="key action" data-key="emoji">😊</div> <div class="key space" data-key=" ">Space</div> <div class="key action" data-key="return">↵</div> <div class="key send" data-key="send">Send</div> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', () => { const messageInput = document.querySelector('.message-input'); const sendBtn = document.querySelector('.send-btn'); const messagesContainer = document.querySelector('.messages-container'); const keys = document.querySelectorAll('.key'); const typingIndicator = document.querySelector('.typing-indicator'); const speedIndicator = document.querySelector('.speed-indicator'); let keyPressTimestamps = []; let keySize = 45; // Default key size // Function to create ripple effect function createRipple(event, element) { const ripple = document.createElement('span'); ripple.classList.add('key-ripple'); const rect = element.getBoundingClientRect(); const size = Math.max(rect.width, rect.height); ripple.style.width = ripple.style.height = `${size}px`; ripple.style.left = `${event.clientX - rect.left - size / 2}px`; ripple.style.top = `${event.clientY - rect.top - size / 2}px`; element.appendChild(ripple); setTimeout(() => { ripple.remove(); }, 600); } // Add event listeners to all keys keys.forEach(key => { key.addEventListener('click', (e) => { const keyValue = key.getAttribute('data-key'); // Add pressed class for visual feedback key.classList.add('pressed'); setTimeout(() => { key.classList.remove('pressed'); }, 150); // Create ripple effect createRipple(e, key); // Handle different key actions if (keyValue === 'backspace') { // Remove last character const text = messageInput.innerText; if (text.length > 0) { messageInput.innerText = text.slice(0, -1); } } else if (keyValue === 'shift') { // Toggle case of keys keys.forEach(k => { if (k.getAttribute('data-key').length === 1) { const isUppercase = k.innerText === k.innerText.toUpperCase(); k.innerText = isUppercase ? k.innerText.toLowerCase() : k.innerText.toUpperCase(); } }); } else if (keyValue === 'send' || keyValue === 'return') { // Send message sendMessage(); } else if (keyValue === '123' || keyValue === 'emoji') { // These would typically switch keyboard modes // For this demo, we'll just show a brief animation key.classList.add('active'); setTimeout(() => { key.classList.remove('active'); }, 300); } else { // Insert the character at the current position document.execCommand('insertText', false, keyValue); // Track typing speed trackTypingSpeed(); } }); }); // Track typing speed function trackTypingSpeed() { const now = Date.now(); keyPressTimestamps.push(now); // Keep only the last 5 timestamps if (keyPressTimestamps.length > 5) { keyPressTimestamps.shift(); } // Calculate typing speed if we have enough data if (keyPressTimestamps.length >= 3) { const timeSpan = keyPressTimestamps[keyPressTimestamps.length - 1] - keyPressTimestamps[0]; const charsPerMin = (keyPressTimestamps.length - 1) * (60000 / timeSpan); // Adapt key size based on typing speed if (charsPerMin > 200) { // Fast typing - smaller keys adaptKeySize(40); speedIndicator.classList.add('show'); setTimeout(() => { speedIndicator.classList.remove('show'); }, 1500); } else if (charsPerMin < 100) { // Slow typing - larger keys adaptKeySize(50); } else { // Normal typing - default size adaptKeySize(45); } } } // Adapt key size function adaptKeySize(size) { if (keySize === size) return; keySize = size; document.documentElement.style.setProperty('--key-height', `${size}px`); keys.forEach(key => { key.style.height = `${size}px`; key.style.fontSize = `${size / 3}px`; }); } // Handle sending messages sendBtn.addEventListener('click', sendMessage); function sendMessage() { const messageText = messageInput.innerText.trim(); if (messageText) { // Add the sent message to the chat addMessage(messageText, 'sent'); // Clear the input messageInput.innerText = ''; // Show typing indicator setTimeout(() => { typingIndicator.style.display = 'block'; messagesContainer.scrollTop = messagesContainer.scrollHeight; // Auto-reply after a delay setTimeout(() => { typingIndicator.style.display = 'none'; // Choose a response based on message content let response; if (messageText.toLowerCase().includes('keyboard') || messageText.toLowerCase().includes('interface')) { response = "Looks fantastic! The pastel colors and rounded keys are perfect. I love how the keys adapt to typing speed."; } else if (messageText.toLowerCase().includes('design') || messageText.toLowerCase().includes('system')) { response = "The design system looks cohesive. Let's present it to the team tomorrow at the standup."; } else if (messageText.toLowerCase().includes('finish') || messageText.toLowerCase().includes('done')) { response = "Great work! Can we have the final files by 5pm?"; } else { response = "Nice! Let's sync up later today to review all the components."; } addMessage(response, 'received'); }, 2000); }, 500); } } // Add a message to the chat function addMessage(text, type) { const message = document.createElement('div'); message.classList.add('message', type); const now = new Date(); const timeString = `${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')}`; message.innerHTML = ` ${text} <div class="message-time">${timeString}</div> `; typingIndicator.style.display = 'none'; messagesContainer.appendChild(message); messagesContainer.scrollTop = messagesContainer.scrollHeight; } // Handle emoji clicks document.querySelectorAll('.emoji').forEach(emoji => { emoji.addEventListener('click', () => { document.execCommand('insertText', false, emoji.innerText); }); }); // Allow physical keyboard input messageInput.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } else { trackTypingSpeed(); } }); // Emulate focus on the input document.addEventListener('click', () => { messageInput.focus(); }); // Initialize with focus messageInput.focus(); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>NeoInput Gaming Keyboard</title> <style> :root { --primary-bg: #0a0a14; --secondary-bg: #12122a; --key-bg: #1d1d35; --key-border: #2a2a45; --key-text: #e6e6ff; --neon-green: #39ff14; --neon-blue: #14f1ff; --neon-purple: #b114ff; --neon-red: #ff1466; --neon-yellow: #ffdf14; } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Rajdhani', 'Orbitron', sans-serif; user-select: none; } body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background: var(--primary-bg); overflow: hidden; } .container { width: 100%; max-width: 700px; padding: 15px; display: flex; flex-direction: column; align-items: center; height: 700px; } .header { width: 100%; text-align: center; margin-bottom: 15px; color: var(--neon-blue); position: relative; } .header h1 { font-size: 2rem; margin-bottom: 5px; text-transform: uppercase; letter-spacing: 2px; position: relative; display: inline-block; text-shadow: 0 0 10px var(--neon-blue), 0 0 20px var(--neon-blue); } .header p { font-size: 0.9rem; opacity: 0.85; color: var(--key-text); max-width: 450px; margin: 0 auto; } .keyboard { background: var(--secondary-bg); border-radius: 12px; padding: 20px; width: 100%; box-shadow: 0 8px 30px rgba(0, 0, 0, 0.5), 0 0 15px rgba(20, 241, 255, 0.2); position: relative; flex-grow: 1; display: flex; flex-direction: column; } .keyboard::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px; background: linear-gradient(90deg, transparent, var(--neon-blue), transparent); box-shadow: 0 0 10px var(--neon-blue); } .keyboard::after { content: ''; position: absolute; bottom: 0; left: 0; right: 0; height: 1px; background: linear-gradient(90deg, transparent, var(--neon-purple), transparent); box-shadow: 0 0 10px var(--neon-purple); } .display { background: rgba(10, 10, 20, 0.8); border: 1px solid var(--key-border); border-radius: 8px; height: 40px; padding: 10px; color: var(--key-text); font-size: 16px; font-family: monospace; margin-bottom: 15px; position: relative; overflow: hidden; } .display::before { content: ''; position: absolute; top: 0; left: -30px; width: 30px; height: 100%; background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); animation: scan 2s linear infinite; } .input-display { position: relative; margin-bottom: 5px; } #keyboardInput { width: 100%; background: rgba(10, 10, 20, 0.8); border: 1px solid var(--neon-blue); border-radius: 8px; color: var(--neon-green); padding: 10px; font-size: 16px; margin-bottom: 15px; outline: none; text-shadow: 0 0 5px var(--neon-green); box-shadow: 0 0 8px rgba(20, 241, 255, 0.3); caret-color: var(--neon-green); } .key-row { display: flex; justify-content: center; margin-bottom: 8px; width: 100%; } .key { background: var(--key-bg); color: var(--key-text); margin: 0 4px; padding: 10px; border-radius: 5px; min-width: 40px; height: 40px; text-align: center; position: relative; cursor: pointer; transition: all 0.15s ease; display: flex; align-items: center; justify-content: center; box-shadow: 0 3px 0 var(--key-border), 0 0 3px rgba(0, 0, 0, 0.8); border: 1px solid rgba(42, 42, 69, 0.5); text-transform: uppercase; font-weight: 600; font-size: 14px; flex-grow: 1; flex-basis: 0; } .key:hover { transform: translateY(1px); box-shadow: 0 2px 0 var(--key-border); background: #242440; } .key.active { transform: translateY(3px); box-shadow: 0 0 0 var(--key-border); background: #242440; } .key-special { background: rgba(57, 255, 20, 0.1); color: var(--neon-green); border-color: rgba(57, 255, 20, 0.3); } .key-movement { background: rgba(20, 241, 255, 0.1); color: var(--neon-blue); border-color: rgba(20, 241, 255, 0.3); } .key-combat { background: rgba(255, 20, 102, 0.1); color: var(--neon-red); border-color: rgba(255, 20, 102, 0.3); } .key-utility { background: rgba(177, 20, 255, 0.1); color: var(--neon-purple); border-color: rgba(177, 20, 255, 0.3); } .key-action { background: rgba(255, 223, 20, 0.1); color: var(--neon-yellow); border-color: rgba(255, 223, 20, 0.3); } .key.wide { flex-grow: 2; } .key.extra-wide { flex-grow: 3; } .key.super-wide { flex-grow: 7; } .key .key-icon { font-size: 16px; } .pulse { animation: pulse 0.5s ease-out; } .status-bar { display: flex; justify-content: space-between; margin-top: 10px; color: var(--key-text); font-size: 12px; opacity: 0.7; } .battery, .ping, .profile { display: flex; align-items: center; } .battery::before { content: '◢◤'; margin-right: 5px; color: var(--neon-green); } .ping::before { content: '◯'; margin-right: 5px; color: var(--neon-blue); } .profile::before { content: '◆'; margin-right: 5px; color: var(--neon-purple); } .macro-panel { display: flex; justify-content: space-between; margin: 10px 0 15px; } .macro-btn { background: rgba(20, 241, 255, 0.1); color: var(--neon-blue); border: 1px solid rgba(20, 241, 255, 0.3); border-radius: 5px; padding: 5px 10px; font-size: 12px; cursor: pointer; transition: all 0.2s; } .macro-btn:hover { background: rgba(20, 241, 255, 0.2); box-shadow: 0 0 8px rgba(20, 241, 255, 0.4); } .tooltip { position: absolute; background: var(--secondary-bg); border: 1px solid var(--neon-blue); color: var(--key-text); padding: 5px 10px; border-radius: 5px; font-size: 12px; bottom: 50px; left: 50%; transform: translateX(-50%); opacity: 0; pointer-events: none; transition: opacity 0.3s; z-index: 100; white-space: nowrap; } .key:hover .tooltip { opacity: 1; } .rgb-border { position: absolute; top: 3px; right: 3px; bottom: 3px; left: 3px; border-radius: 3px; background: transparent; border: 1px solid transparent; opacity: 0; transition: opacity 0.3s; } .glowing { animation: glow 2s linear infinite; } @keyframes glow { 0% { border-color: var(--neon-green); box-shadow: 0 0 5px var(--neon-green); } 25% { border-color: var(--neon-blue); box-shadow: 0 0 5px var(--neon-blue); } 50% { border-color: var(--neon-purple); box-shadow: 0 0 5px var(--neon-purple); } 75% { border-color: var(--neon-red); box-shadow: 0 0 5px var(--neon-red); } 100% { border-color: var(--neon-green); box-shadow: 0 0 5px var(--neon-green); } } @keyframes pulse { 0% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.1); opacity: 0.9; } 100% { transform: scale(1); opacity: 1; } } @keyframes scan { 0% { left: -30px; } 100% { left: 100%; } } /* For custom profile picker */ .profile-picker { position: absolute; top: 10px; right: 10px; display: flex; gap: 5px; } .profile-option { width: 12px; height: 12px; border-radius: 50%; cursor: pointer; border: 1px solid rgba(255, 255, 255, 0.3); transition: all 0.2s; } .profile-option:hover { transform: scale(1.2); } .profile-option:nth-child(1) { background: var(--neon-green); } .profile-option:nth-child(2) { background: var(--neon-blue); } .profile-option:nth-child(3) { background: var(--neon-purple); } .profile-option:nth-child(4) { background: var(--neon-red); } .mode-indicator { position: absolute; top: 10px; left: 10px; background: rgba(20, 241, 255, 0.2); color: var(--neon-blue); padding: 3px 8px; border-radius: 10px; font-size: 10px; text-transform: uppercase; letter-spacing: 1px; } @media (max-width: 600px) { .key { min-width: 30px; height: 35px; font-size: 12px; padding: 5px; margin: 0 2px; } .key-row { margin-bottom: 5px; } .header h1 { font-size: 1.5rem; } .header p { font-size: 0.8rem; } } </style> </head> <body> <div class="container"> <div class="header"> <h1>NeoInput X7</h1> <p>Ultra-responsive gaming keyboard with customizable macros and dynamic RGB feedback for competitive play</p> </div> <div class="keyboard"> <div class="mode-indicator">FPS Mode</div> <div class="profile-picker"> <div class="profile-option" data-profile="fps"></div> <div class="profile-option" data-profile="mmo"></div> <div class="profile-option" data-profile="racing"></div> <div class="profile-option" data-profile="strategy"></div> </div> <div class="input-display"> <input type="text" id="keyboardInput" placeholder="Key inputs appear here..." readonly> </div> <div class="macro-panel"> <div class="macro-btn" data-action="macro1">Quick Heal</div> <div class="macro-btn" data-action="macro2">Weapon Switch</div> <div class="macro-btn" data-action="macro3">Crouch Jump</div> <div class="macro-btn" data-action="macro4">Ultimate</div> </div> <div class="key-row"> <div class="key key-utility" data-key="esc">Esc <div class="tooltip">Menu</div> <div class="rgb-border"></div> </div> <div class="key key-action" data-key="1">1 <div class="tooltip">Primary Weapon</div> <div class="rgb-border"></div> </div> <div class="key key-action" data-key="2">2 <div class="tooltip">Secondary Weapon</div> <div class="rgb-border"></div> </div> <div class="key key-action" data-key="3">3 <div class="tooltip">Melee</div> <div class="rgb-border"></div> </div> <div class="key key-action" data-key="4">4 <div class="tooltip">Grenade</div> <div class="rgb-border"></div> </div> <div class="key key-action" data-key="5">5 <div class="tooltip">Heal</div> <div class="rgb-border"></div> </div> <div class="key key-utility" data-key="tab">Tab <div class="tooltip">Scoreboard</div> <div class="rgb-border"></div> </div> </div> <div class="key-row"> <div class="key key-movement" data-key="q">Q <div class="tooltip">Ability 1</div> <div class="rgb-border"></div> </div> <div class="key key-movement" data-key="w">W <div class="tooltip">Forward</div> <div class="rgb-border"></div> </div> <div class="key key-movement" data-key="e">E <div class="tooltip">Interact</div> <div class="rgb-border"></div> </div> <div class="key key-movement" data-key="r">R <div class="tooltip">Reload</div> <div class="rgb-border"></div> </div> <div class="key key-combat" data-key="f">F <div class="tooltip">Melee</div> <div class="rgb-border"></div> </div> <div class="key key-combat" data-key="g">G <div class="tooltip">Grenade</div> <div class="rgb-border"></div> </div> <div class="key key-utility" data-key="~">~ <div class="tooltip">Console</div> <div class="rgb-border"></div> </div> </div> <div class="key-row"> <div class="key key-utility" data-key="shift" class="wide">Shift <div class="tooltip">Sprint</div> <div class="rgb-border"></div> </div> <div class="key key-movement" data-key="a">A <div class="tooltip">Left</div> <div class="rgb-border"></div> </div> <div class="key key-movement" data-key="s">S <div class="tooltip">Back</div> <div class="rgb-border"></div> </div> <div class="key key-movement" data-key="d">D <div class="tooltip">Right</div> <div class="rgb-border"></div> </div> <div class="key key-utility" data-key="c">C <div class="tooltip">Crouch</div> <div class="rgb-border"></div> </div> <div class="key key-utility" data-key="v">V <div class="tooltip">Voice</div> <div class="rgb-border"></div> </div> </div> <div class="key-row"> <div class="key key-utility" data-key="ctrl" class="wide">Ctrl <div class="tooltip">Duck</div> <div class="rgb-border"></div> </div> <div class="key key-utility" data-key="alt">Alt <div class="tooltip">Walk</div> <div class="rgb-border"></div> </div> <div class="key key-utility key-special" data-key="space" class="super-wide">Space <div class="tooltip">Jump</div> <div class="rgb-border"></div> </div> <div class="key key-combat" data-key="b">B <div class="tooltip">Buy Menu</div> <div class="rgb-border"></div> </div> </div> <div class="status-bar"> <div class="battery">Battery: 87%</div> <div class="ping">Ping: 23ms</div> <div class="profile">Profile: FPS</div> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { const keys = document.querySelectorAll('.key'); const inputDisplay = document.getElementById('keyboardInput'); const macroButtons = document.querySelectorAll('.macro-btn'); const profileOptions = document.querySelectorAll('.profile-option'); const modeIndicator = document.querySelector('.mode-indicator'); // Game profiles const profiles = { fps: { name: 'FPS Mode', keyMappings: { 'q': 'Ability 1', 'e': 'Interact', 'f': 'Melee', 'c': 'Crouch' } }, mmo: { name: 'MMO Mode', keyMappings: { 'q': 'Spell 1', 'e': 'Spell 2', 'f': 'Special Ability', 'c': 'Character Menu' } }, racing: { name: 'Racing Mode', keyMappings: { 'w': 'Accelerate', 's': 'Brake', 'a': 'Left', 'd': 'Right' } }, strategy: { name: 'Strategy Mode', keyMappings: { 'q': 'Group 1', 'e': 'Group 2', 'f': 'Select All', 'c': 'Camera Lock' } } }; let currentProfile = 'fps'; // For key press animation and sound keys.forEach(key => { key.addEventListener('mousedown', function() { pressKey(this); }); key.addEventListener('mouseup', function() { releaseKey(this); }); key.addEventListener('mouseleave', function() { if (this.classList.contains('active')) { releaseKey(this); } }); }); // Keyboard events for the entire document document.addEventListener('keydown', function(e) { const keyElement = document.querySelector(`.key[data-key="${e.key.toLowerCase()}"]`); if (keyElement && !keyElement.classList.contains('active')) { pressKey(keyElement); e.preventDefault(); } }); document.addEventListener('keyup', function(e) { const keyElement = document.querySelector(`.key[data-key="${e.key.toLowerCase()}"]`); if (keyElement) { releaseKey(keyElement); e.preventDefault(); } }); // Macro buttons macroButtons.forEach(btn => { btn.addEventListener('click', function() { const actionType = this.getAttribute('data-action'); // Visual feedback this.classList.add('pulse'); setTimeout(() => { this.classList.remove('pulse'); }, 500); // Different macro actions switch(actionType) { case 'macro1': simulateMacro(['5', 'g']); break; case 'macro2': simulateMacro(['q', '1']); break; case 'macro3': simulateMacro(['c', 'space']); break; case 'macro4': simulateMacro(['4', 'f']); break; } }); }); // Profile switching profileOptions.forEach(option => { option.addEventListener('click', function() { const profile = this.getAttribute('data-profile'); changeProfile(profile); // Visual feedback this.classList.add('pulse'); setTimeout(() => { this.classList.remove('pulse'); }, 500); }); }); // Simulate key presses for macros function simulateMacro(keySequence) { let index = 0; const interval = setInterval(() => { if (index >= keySequence.length) { clearInterval(interval); return; } const keyElement = document.querySelector(`.key[data-key="${keySequence[index]}"]`); if (keyElement) { pressKey(keyElement); setTimeout(() => { releaseKey(keyElement); }, 150); } index++; }, 200); } // Handle profile changes function changeProfile(profile) { if (profiles[profile]) { currentProfile = profile; modeIndicator.textContent = profiles[profile].name; document.querySelector('.profile').textContent = `Profile: ${profile.toUpperCase()}`; // Update tooltips based on profile Object.keys(profiles[profile].keyMappings).forEach(key => { const keyElement = document.querySelector(`.key[data-key="${key}"]`); if (keyElement) { const tooltip = keyElement.querySelector('.tooltip'); if (tooltip) { tooltip.textContent = profiles[profile].keyMappings[key]; } } }); // Visual feedback document.querySelectorAll('.key').forEach(key => { key.classList.add('pulse'); setTimeout(() => { key.classList.remove('pulse'); }, 500); }); } } function pressKey(keyElement) { keyElement.classList.add('active'); keyElement.classList.add('pulse'); // RGB border effect const rgbBorder = keyElement.querySelector('.rgb-border'); rgbBorder.classList.add('glowing'); rgbBorder.style.opacity = 1; // Add key to input display const keyValue = keyElement.getAttribute('data-key'); inputDisplay.value += `[${keyValue.toUpperCase()}] `; // Scroll input to end inputDisplay.scrollLeft = inputDisplay.scrollWidth; } function releaseKey(keyElement) { keyElement.classList.remove('active'); // Dim RGB border effect after a delay setTimeout(() => { const rgbBorder = keyElement.querySelector('.rgb-border'); rgbBorder.style.opacity = 0; setTimeout(() => { rgbBorder.classList.remove('glowing'); }, 300); }, 200); } // Initial random key glow to attract attention function randomKeyGlow() { const randomKeys = Array.from(keys).sort(() => 0.5 - Math.random()).slice(0, 3); randomKeys.forEach((key, index) => { setTimeout(() => { const rgbBorder = key.querySelector('.rgb-border'); rgbBorder.classList.add('glowing'); rgbBorder.style.opacity = 1; setTimeout(() => { rgbBorder.style.opacity = 0; setTimeout(() => { rgbBorder.classList.remove('glowing'); }, 300); }, 800); }, index * 300); }); } // Initial effect setTimeout(randomKeyGlow, 1000); // Clear input display occasionally setInterval(() => { if (inputDisplay.value.length > 30) { inputDisplay.value = ''; } }, 8000); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>ProKey | Office Productivity Keyboard</title> <style> :root { --primary: #2c5282; --primary-light: #3182ce; --secondary: #718096; --accent: #4fd1c5; --bg: #f7fafc; --text: #2d3748; --light-text: #4a5568; --shadow: 0 4px 6px rgba(0, 0, 0, 0.1); --key-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); --radius: 8px; } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { background: var(--bg); color: var(--text); display: flex; flex-direction: column; align-items: center; justify-content: flex-start; height: 700px; width: 700px; padding: 20px; overflow-x: hidden; } .container { width: 100%; max-width: 660px; margin: 0 auto; } header { text-align: center; margin-bottom: 20px; } h1 { font-size: 28px; color: var(--primary); margin-bottom: 5px; background: linear-gradient(135deg, var(--primary), var(--primary-light)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; } p.tagline { color: var(--light-text); font-size: 16px; margin-bottom: 20px; } .keyboard-container { width: 100%; background: linear-gradient(145deg, #ffffff, #f5f7fa); border-radius: var(--radius); padding: 15px; box-shadow: var(--shadow); position: relative; overflow: hidden; transition: transform 0.3s, box-shadow 0.3s; } .keyboard-container:hover { transform: translateY(-5px); box-shadow: 0 8px 15px rgba(0, 0, 0, 0.1); } .keyboard-container::after { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 5px; background: linear-gradient(90deg, var(--primary), var(--accent)); border-radius: var(--radius) var(--radius) 0 0; } .keyboard { display: grid; grid-gap: 8px; width: 100%; } .row { display: flex; gap: 8px; margin-bottom: 8px; } .key { height: 45px; border-radius: var(--radius); background: white; display: flex; align-items: center; justify-content: center; box-shadow: var(--key-shadow); cursor: pointer; position: relative; overflow: hidden; user-select: none; transition: all 0.15s ease; font-weight: 500; color: var(--text); text-align: center; } .key::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px; background: rgba(255, 255, 255, 0.7); } .key:hover { background: linear-gradient(145deg, #ffffff, #f0f4f8); transform: translateY(-2px); } .key:active { transform: translateY(1px); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); } .key .shortcut { position: absolute; top: 5px; right: 5px; font-size: 8px; color: var(--secondary); opacity: 0.8; } .key .icon { font-size: 12px; margin-right: 5px; } .standard { flex: 1; } .half { flex: 0.5; } .one-half { flex: 1.5; } .two { flex: 2; } .two-half { flex: 2.5; } .function { background: linear-gradient(135deg, #e6f2ff, #c1deff); color: var(--primary); } .shortcut-key { background: linear-gradient(135deg, #e6f7f5, #c1efec); color: #2c7a7b; } .action-key { background: linear-gradient(135deg, #edf2f7, #e2e8f0); color: var(--secondary); } .special-key { background: linear-gradient(135deg, #ebf8ff, #bee3f8); color: var(--primary); } .space-key { background: linear-gradient(135deg, #f0fff4, #c6f6d5); color: #2f855a; } .active-key { background: linear-gradient(135deg, #ebf4ff, #c3dafe); box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1); transform: translateY(1px); color: var(--primary); } .tooltip { position: absolute; bottom: -40px; left: 50%; transform: translateX(-50%); background: rgba(0, 0, 0, 0.8); color: white; padding: 5px 10px; border-radius: 4px; font-size: 12px; opacity: 0; transition: opacity 0.3s, bottom 0.3s; pointer-events: none; white-space: nowrap; z-index: 100; } .key:hover .tooltip { opacity: 1; bottom: -35px; } .controls { display: flex; justify-content: space-between; margin-top: 20px; gap: 10px; } .control-btn { padding: 10px 15px; border: none; border-radius: var(--radius); background: var(--primary); color: white; font-weight: 500; cursor: pointer; transition: all 0.2s; } .control-btn:hover { background: var(--primary-light); } .theme-btn { background: var(--secondary); } .theme-btn:hover { background: #5a6b7e; } .mode-indicator { display: flex; gap: 15px; margin-top: 15px; align-items: center; justify-content: center; } .indicator { width: 12px; height: 12px; border-radius: 50%; background: #cbd5e0; } .indicator.active { background: var(--accent); box-shadow: 0 0 0 2px rgba(79, 209, 197, 0.3); } .indicator-label { font-size: 14px; color: var(--light-text); } .workflow-preview { margin-top: 20px; padding: 15px; background: white; border-radius: var(--radius); box-shadow: var(--shadow); display: none; } .workflow-preview.show { display: block; animation: fadeIn 0.5s ease; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .workflow-title { font-size: 16px; color: var(--primary); margin-bottom: 10px; } .workflow-steps { font-size: 14px; color: var(--light-text); line-height: 1.6; } .step { display: flex; align-items: center; margin-bottom: 5px; } .step-number { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; background: var(--primary-light); color: white; border-radius: 50%; font-size: 12px; margin-right: 10px; flex-shrink: 0; } @media (max-width: 600px) { .container { padding: 10px; } .row { gap: 5px; margin-bottom: 5px; } .key { height: 40px; font-size: 12px; } .controls { flex-direction: column; } } /* KEY PRESS ANIMATION */ @keyframes keyPress { 0% { transform: translateY(0); } 50% { transform: translateY(2px); } 100% { transform: translateY(0); } } .key-pressed { animation: keyPress 0.15s ease; } /* TYPING INDICATOR */ .typing-indicator { height: 30px; display: flex; align-items: center; margin-top: 15px; background: rgba(255, 255, 255, 0.7); padding: 5px 10px; border-radius: var(--radius); box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); } .typing-text { font-family: monospace; color: var(--text); font-size: 14px; } .cursor { display: inline-block; width: 2px; height: 16px; background: var(--primary); margin-left: 2px; animation: blink 1s infinite; } @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } </style> </head> <body> <div class="container"> <header> <h1>ProKey</h1> <p class="tagline">Optimize your workflow with smart keyboard shortcuts</p> </header> <div class="keyboard-container"> <div class="keyboard"> <div class="row"> <div class="key function half" data-key="esc"> <span class="shortcut">Esc</span> Esc <div class="tooltip">Cancel action</div> </div> <div class="key function half" data-key="f1"> <span class="shortcut">F1</span> Help <div class="tooltip">Open help documentation</div> </div> <div class="key function half" data-key="f2"> <span class="shortcut">F2</span> Rename <div class="tooltip">Rename selected item</div> </div> <div class="key function half" data-key="f3"> <span class="shortcut">F3</span> Find <div class="tooltip">Find next occurrence</div> </div> <div class="key function half" data-key="f4"> <span class="shortcut">F4</span> Cell <div class="tooltip">Repeat last action</div> </div> <div class="key function half" data-key="f5"> <span class="shortcut">F5</span> Refresh <div class="tooltip">Refresh content</div> </div> <div class="key function half" data-key="f6"> <span class="shortcut">F6</span> Address <div class="tooltip">Move to address bar</div> </div> <div class="key function half" data-key="f7"> <span class="shortcut">F7</span> Spell <div class="tooltip">Spell check</div> </div> <div class="key function half" data-key="f8"> <span class="shortcut">F8</span> Mode <div class="tooltip">Toggle selection mode</div> </div> <div class="key function half" data-key="f9"> <span class="shortcut">F9</span> Calc <div class="tooltip">Recalculate formulas</div> </div> <div class="key function half" data-key="f10"> <span class="shortcut">F10</span> Menu <div class="tooltip">Activate menu bar</div> </div> <div class="key function half" data-key="f11"> <span class="shortcut">F11</span> Full <div class="tooltip">Toggle fullscreen</div> </div> <div class="key function half" data-key="f12"> <span class="shortcut">F12</span> Dev <div class="tooltip">Developer tools</div> </div> </div> <div class="row"> <div class="key standard" data-key="`"> <span>~</span> <span>`</span> </div> <div class="key standard" data-key="1"> <span>!</span> <span>1</span> </div> <div class="key standard" data-key="2"> <span>@</span> <span>2</span> </div> <div class="key standard" data-key="3"> <span>#</span> <span>3</span> </div> <div class="key standard" data-key="4"> <span>$</span> <span>4</span> </div> <div class="key standard" data-key="5"> <span>%</span> <span>5</span> </div> <div class="key standard" data-key="6"> <span>^</span> <span>6</span> </div> <div class="key standard" data-key="7"> <span>&</span> <span>7</span> </div> <div class="key standard" data-key="8"> <span>*</span> <span>8</span> </div> <div class="key standard" data-key="9"> <span>(</span> <span>9</span> </div> <div class="key standard" data-key="0"> <span>)</span> <span>0</span> </div> <div class="key standard" data-key="-"> <span>_</span> <span>-</span> </div> <div class="key standard" data-key="="> <span>+</span> <span>=</span> </div> <div class="key one-half action-key" data-key="backspace"> <span class="shortcut">←</span> Backspace </div> </div> <div class="row"> <div class="key one-half action-key" data-key="tab"> <span class="shortcut">↹</span> Tab <div class="tooltip">Move between fields</div> </div> <div class="key standard" data-key="q">Q</div> <div class="key standard" data-key="w">W</div> <div class="key standard" data-key="e">E</div> <div class="key standard" data-key="r">R</div> <div class="key standard" data-key="t">T</div> <div class="key standard" data-key="y">Y</div> <div class="key standard" data-key="u">U</div> <div class="key standard" data-key="i">I</div> <div class="key standard" data-key="o">O</div> <div class="key standard" data-key="p">P</div> <div class="key standard" data-key="["> <span>{</span> <span>[</span> </div> <div class="key standard" data-key="]"> <span>}</span> <span>]</span> </div> <div class="key one-half" data-key="\"> <span>|</span> <span>\</span> </div> </div> <div class="row"> <div class="key one-half action-key" data-key="caps"> <span class="shortcut">⇪</span> Caps <div class="tooltip">Toggle capitalization</div> </div> <div class="key standard" data-key="a">A</div> <div class="key standard" data-key="s">S</div> <div class="key standard" data-key="d">D</div> <div class="key standard" data-key="f">F</div> <div class="key standard" data-key="g">G</div> <div class="key standard" data-key="h">H</div> <div class="key standard" data-key="j">J</div> <div class="key standard" data-key="k">K</div> <div class="key standard" data-key="l">L</div> <div class="key standard" data-key=";"> <span>:</span> <span>;</span> </div> <div class="key standard" data-key="'"> <span>"</span> <span>'</span> </div> <div class="key one-half action-key" data-key="enter"> <span class="shortcut">↵</span> Enter <div class="tooltip">Confirm action</div> </div> </div> <div class="row"> <div class="key two action-key" data-key="shift"> <span class="shortcut">⇧</span> Shift <div class="tooltip">Modify key functions</div> </div> <div class="key standard" data-key="z">Z</div> <div class="key standard" data-key="x">X</div> <div class="key standard" data-key="c">C</div> <div class="key standard" data-key="v">V</div> <div class="key standard" data-key="b">B</div> <div class="key standard" data-key="n">N</div> <div class="key standard" data-key="m">M</div> <div class="key standard" data-key=","> <span><</span> <span>,</span> </div> <div class="key standard" data-key="."> <span>></span> <span>.</span> </div> <div class="key standard" data-key="/"> <span>?</span> <span>/</span> </div> <div class="key two action-key" data-key="shift-right"> <span class="shortcut">⇧</span> Shift </div> </div> <div class="row"> <div class="key one-half action-key" data-key="ctrl"> <span class="shortcut">Ctrl</span> Ctrl <div class="tooltip">Control key for shortcuts</div> </div> <div class="key one-half special-key" data-key="win"> <span class="shortcut">⊞</span> Win </div> <div class="key one-half action-key" data-key="alt"> <span class="shortcut">Alt</span> Alt <div class="tooltip">Alternate key functions</div> </div> <div class="key five space-key" data-key="space"> Space <div class="tooltip">Insert space character</div> </div> <div class="key one-half action-key" data-key="alt-right"> <span class="shortcut">Alt</span> Alt </div> <div class="key one-half action-key" data-key="fn"> <span class="shortcut">Fn</span> Fn </div> <div class="key one-half action-key" data-key="ctrl-right"> <span class="shortcut">Ctrl</span> Ctrl </div> </div> </div> <div class="row" style="margin-top: 10px;"> <div class="key shortcut-key one-half" data-key="ctrl+c"> <span class="shortcut">Ctrl+C</span> Copy <div class="tooltip">Copy selection to clipboard</div> </div> <div class="key shortcut-key one-half" data-key="ctrl+v"> <span class="shortcut">Ctrl+V</span> Paste <div class="tooltip">Paste from clipboard</div> </div> <div class="key shortcut-key one-half" data-key="ctrl+z"> <span class="shortcut">Ctrl+Z</span> Undo <div class="tooltip">Undo last action</div> </div> <div class="key shortcut-key one-half" data-key="ctrl+y"> <span class="shortcut">Ctrl+Y</span> Redo <div class="tooltip">Redo last action</div> </div> <div class="key shortcut-key one-half" data-key="ctrl+f"> <span class="shortcut">Ctrl+F</span> Find <div class="tooltip">Find text</div> </div> <div class="key shortcut-key one-half" data-key="ctrl+s"> <span class="shortcut">Ctrl+S</span> Save <div class="tooltip">Save document</div> </div> <div class="key shortcut-key one-half" data-key="ctrl+p"> <span class="shortcut">Ctrl+P</span> Print <div class="tooltip">Print document</div> </div> <div class="key shortcut-key one-half" data-key="ctrl+a"> <span class="shortcut">Ctrl+A</span> All <div class="tooltip">Select all content</div> </div> </div> <div class="typing-indicator"> <span class="typing-text"></span> <span class="cursor"></span> </div> </div> <div class="mode-indicator"> <div class="indicator active"></div> <span class="indicator-label">Standard Mode</span> <div class="indicator"></div> <span class="indicator-label">Excel Mode</span> <div class="indicator"></div> <span class="indicator-label">Word Mode</span> </div> <div class="controls"> <button class="control-btn" id="productivity-mode">Productivity Shortcuts</button> <button class="control-btn theme-btn" id="theme-toggle">Toggle Theme</button> <button class="control-btn" id="workflow-btn">Show Workflow</button> </div> <div class="workflow-preview" id="workflow-preview"> <h3 class="workflow-title">Document Processing Workflow</h3> <div class="workflow-steps"> <div class="step"> <span class="step-number">1</span> <span>Press Ctrl+N to create a new document</span> </div> <div class="step"> <span class="step-number">2</span> <span>Use Tab to navigate between sections</span> </div> <div class="step"> <span class="step-number">3</span> <span>Ctrl+S to save your work frequently</span> </div> <div class="step"> <span class="step-number">4</span> <span>Ctrl+P to print when finished</span> </div> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', () => { const keys = document.querySelectorAll('.key'); const typingText = document.querySelector('.typing-text'); const modeIndicators = document.querySelectorAll('.indicator'); const workflowPreview = document.getElementById('workflow-preview'); const workflowBtn = document.getElementById('workflow-btn'); const themeToggleBtn = document.getElementById('theme-toggle'); const productivityModeBtn = document.getElementById('productivity-mode'); // Sample text for typing demonstration const demoTexts = [ "Formatting quarterly financial report...", "Creating presentation slides for stakeholders...", "Organizing project timeline in spreadsheet...", "Compiling department statistics for review..." ]; let currentText = ""; let currentIndex = 0; let demoTextIndex = 0; let isDeleting = false; let currentMode = 0; // Keyboard click handler keys.forEach(key => { key.addEventListener('click', (e) => { const keyElement = e.currentTarget; // Add pressed animation class keyElement.classList.add('key-pressed'); setTimeout(() => { keyElement.classList.remove('key-pressed'); }, 150); // Handle key press logic const keyValue = keyElement.getAttribute('data-key'); handleKeyPress(keyValue); }); }); // Typing demo effect function typeWriter() { const fullText = demoTexts[demoTextIndex]; if (isDeleting) { currentText = fullText.substring(0, currentIndex - 1); currentIndex--; } else { currentText = fullText.substring(0, currentIndex + 1); currentIndex++; } typingText.textContent = currentText; let typingSpeed = isDeleting ? 30 : 70; if (!isDeleting && currentIndex === fullText.length) { // Start deleting after a delay isDeleting = true; typingSpeed = 1000; // Pause before deleting } else if (isDeleting && currentIndex === 0) { isDeleting = false; demoTextIndex = (demoTextIndex + 1) % demoTexts.length; typingSpeed = 500; // Pause before typing new text } setTimeout(typeWriter, typingSpeed); } // Handle keyboard input function handleKeyPress(key) { // Find all shortcut keys and reset them document.querySelectorAll('.active-key').forEach(el => { el.classList.remove('active-key'); }); // Handle different types of keyboard inputs if (key.startsWith('ctrl+')) { // Handle keyboard shortcuts const ctrlKey = document.querySelector(`[data-key="ctrl"]`); const actionKey = document.querySelector(`[data-key="${key.split('+')[1]}"]`); const shortcutKey = document.querySelector(`[data-key="${key}"]`); if (ctrlKey) ctrlKey.classList.add('active-key'); if (actionKey) actionKey.classList.add('active-key'); if (shortcutKey) shortcutKey.classList.add('active-key'); // Show specific shortcut actions switch(key) { case 'ctrl+c': showTypingEffect("Text copied to clipboard"); break; case 'ctrl+v': showTypingEffect("Pasting content..."); break; case 'ctrl+z': showTypingEffect("Undoing last action"); break; case 'ctrl+s': showTypingEffect("Document saved successfully"); break; case 'ctrl+f': showTypingEffect("Find dialog opened"); break; case 'ctrl+p': showTypingEffect("Preparing document for print"); break; case 'ctrl+a': showTypingEffect("All content selected"); break; default: break; } } else { // Handle regular keys const keyElement = document.querySelector(`[data-key="${key}"]`); if (keyElement) { keyElement.classList.add('active-key'); setTimeout(() => { keyElement.classList.remove('active-key'); }, 200); } // Special key actions switch(key) { case 'enter': showTypingEffect("Command executed"); break; case 'esc': showTypingEffect("Action canceled"); break; case 'tab': showTypingEffect("Moved to next field"); break; case 'f1': showTypingEffect("Opening help documentation"); break; case 'f5': showTypingEffect("Refreshing content"); break; default: // For regular character keys if (key.length === 1) { appendCharacter(key); } break; } } } function showTypingEffect(text) { typingText.textContent = ""; let i = 0; const speed = 50; function typeChar() { if (i < text.length) { typingText.textContent += text.charAt(i); i++; setTimeout(typeChar, speed); } } typeChar(); } function appendCharacter(char) { // This would normally append the character to the typing area // but for demo purposes we'll keep the typing demo running } // Workflow button handler workflowBtn.addEventListener('click', () => { workflowPreview.classList.toggle('show'); workflowBtn.textContent = workflowPreview.classList.contains('show') ? "Hide Workflow" : "Show Workflow"; }); // Theme toggle themeToggleBtn.addEventListener('click', () => { document.documentElement.style.setProperty('--bg', getComputedStyle(document.documentElement).getPropertyValue('--bg') === '#f7fafc' ? '#2d3748' : '#f7fafc'); document.documentElement.style.setProperty('--text', getComputedStyle(document.documentElement).getPropertyValue('--text') === '#2d3748' ? '#f7fafc' : '#2d3748'); document.documentElement.style.setProperty('--light-text', getComputedStyle(document.documentElement).getPropertyValue('--light-text') === '#4a5568' ? '#e2e8f0' : '#4a5568'); }); // Productivity mode toggle productivityModeBtn.addEventListener('click', () => { currentMode = (currentMode + 1) % 3; // Update indicators modeIndicators.forEach((indicator, index) => { indicator.classList.toggle('active', index === currentMode); }); // Update workflow content based on mode const workflows = [ { title: "Document Processing Workflow", steps: [ "Press Ctrl+N to create a new document", "Use Tab to navigate between sections", "Ctrl+S to save your work frequently", "Ctrl+P to print when finished" ] }, { title: "Excel Spreadsheet Workflow", steps: [ "F2 to edit cell contents directly", "Ctrl+Arrow keys to navigate between data regions", "Ctrl+Shift+Arrow to select data ranges", "Alt+= to auto-sum selected cells" ] }, { title: "Word Document Workflow", steps: [ "Ctrl+B, Ctrl+I, Ctrl+U for text formatting", "Ctrl+L/E/R/J for text alignment", "Ctrl+Shift+> to increase font size", "F7 to run spell check" ] } ]; const currentWorkflow = workflows[currentMode]; document.querySelector('.workflow-title').textContent = currentWorkflow.title; const stepsContainer = document.querySelector('.workflow-steps'); stepsContainer.innerHTML = ''; currentWorkflow.steps.forEach((step, index) => { const stepElement = document.createElement('div'); stepElement.className = 'step'; stepElement.innerHTML = ` <span class="step-number">${index + 1}</span> <span>${step}</span> `; stepsContainer.appendChild(stepElement); }); // Show the workflow when changing modes workflowPreview.classList.add('show'); workflowBtn.textContent = "Hide Workflow"; }); // Start the typing demo typeWriter(); // Physical keyboard support document.addEventListener('keydown', (e) => { let keyValue = e.key.toLowerCase(); // Map special keys if (keyValue === 'control') keyValue = 'ctrl'; if (keyValue === 'escape') keyValue = 'esc'; if (keyValue === 'capslock') keyValue = 'caps'; if (keyValue === 'return') keyValue = 'enter'; // Handle certain key combinations if (e.ctrlKey && keyValue !== 'ctrl') { keyValue = `ctrl+${keyValue}`; } // Find the key and simulate a click const keyElement = document.querySelector(`[data-key="${keyValue}"]`); if (keyElement) { keyElement.click(); e.preventDefault(); // Prevent default browser shortcut behaviors } }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Multilingual Smart Keyboard</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', sans-serif; } body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background: linear-gradient(135deg, #f5f7fa 0%, #e4e8ec 100%); padding: 20px; } .container { width: 100%; max-width: 650px; background-color: #fff; border-radius: 18px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); overflow: hidden; padding: 30px; position: relative; } .language-selector { display: flex; justify-content: space-between; margin-bottom: 25px; position: relative; } .language-tab { cursor: pointer; padding: 12px 20px; border-radius: 12px; font-weight: 600; font-size: 14px; transition: all 0.3s ease; color: #555; position: relative; z-index: 1; opacity: 0.7; text-align: center; flex: 1; margin: 0 5px; border: 2px solid transparent; } .language-tab.active { opacity: 1; transform: translateY(-3px); } #en-tab.active { color: #3498db; border-color: #3498db; background-color: rgba(52, 152, 219, 0.08); } #es-tab.active { color: #e74c3c; border-color: #e74c3c; background-color: rgba(231, 76, 60, 0.08); } #fr-tab.active { color: #2ecc71; border-color: #2ecc71; background-color: rgba(46, 204, 113, 0.08); } #cn-tab.active { color: #f39c12; border-color: #f39c12; background-color: rgba(243, 156, 18, 0.08); } .typing-area { background-color: #f7f9fc; border-radius: 12px; padding: 15px; margin-bottom: 25px; border: 2px solid #e6e9ef; min-height: 120px; transition: all 0.3s ease; font-size: 16px; line-height: 1.6; } .typing-area:focus { outline: none; box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2); } .typing-area[data-language="en"] { border-color: #3498db; } .typing-area[data-language="es"] { border-color: #e74c3c; } .typing-area[data-language="fr"] { border-color: #2ecc71; } .typing-area[data-language="cn"] { border-color: #f39c12; } .keyboard { display: grid; grid-template-columns: repeat(10, 1fr); gap: 8px; margin-bottom: 15px; } .key { padding: 12px 5px; background-color: #fff; border-radius: 8px; text-align: center; cursor: pointer; user-select: none; font-weight: 500; font-size: 14px; box-shadow: 0 3px 6px rgba(0, 0, 0, 0.08); transition: all 0.2s ease; display: flex; justify-content: center; align-items: center; position: relative; overflow: hidden; } .key:hover { transform: translateY(-2px); } .key:active { transform: translateY(1px); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); } .key.special { grid-column: span 2; background-color: #f1f3f7; font-weight: 600; } .key.space { grid-column: span 5; } /* Language specific styling */ .keyboard[data-language="en"] .key { border-bottom: 2px solid #3498db; } .keyboard[data-language="es"] .key { border-bottom: 2px solid #e74c3c; } .keyboard[data-language="fr"] .key { border-bottom: 2px solid #2ecc71; } .keyboard[data-language="cn"] .key { border-bottom: 2px solid #f39c12; } .special-feature { margin-top: 20px; background: #f7f9fc; border-radius: 12px; padding: 15px; font-size: 14px; color: #555; display: flex; align-items: center; position: relative; overflow: hidden; } .special-feature:before { content: ''; position: absolute; width: 100%; height: 2px; bottom: 0; left: 0; background: linear-gradient(to right, #3498db, #e74c3c, #2ecc71, #f39c12); animation: rainbow 5s linear infinite; transform-origin: left; } .notification { position: absolute; top: -60px; left: 50%; transform: translateX(-50%); background-color: #333; color: white; padding: 12px 24px; border-radius: 8px; transition: all 0.3s ease; opacity: 0; font-size: 14px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); z-index: 10; } .notification.show { top: 20px; opacity: 1; } .suggestion-bar { display: flex; gap: 10px; margin-bottom: 15px; padding: 5px 10px; overflow-x: auto; scrollbar-width: none; opacity: 0; transform: translateY(10px); transition: all 0.3s ease; } .suggestion-bar.show { opacity: 1; transform: translateY(0); } .suggestion-bar::-webkit-scrollbar { display: none; } .suggestion { background: #f1f3f7; border-radius: 15px; padding: 8px 12px; font-size: 13px; cursor: pointer; white-space: nowrap; transition: all 0.2s ease; } .suggestion:hover { background: #e6e9ef; } .suggestion[data-language="en"] { background-color: rgba(52, 152, 219, 0.1); color: #3498db; } .suggestion[data-language="es"] { background-color: rgba(231, 76, 60, 0.1); color: #e74c3c; } .suggestion[data-language="fr"] { background-color: rgba(46, 204, 113, 0.1); color: #2ecc71; } .suggestion[data-language="cn"] { background-color: rgba(243, 156, 18, 0.1); color: #f39c12; } .feature-icon { background: white; width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-right: 15px; box-shadow: 0 3px 8px rgba(0, 0, 0, 0.08); } .language-indicator { position: absolute; top: 10px; right: 10px; background: #f5f7fa; border-radius: 50%; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; font-size: 10px; font-weight: bold; opacity: 0.7; transition: all 0.3s ease; } .language-indicator[data-language="en"] { background-color: #3498db; color: white; } .language-indicator[data-language="es"] { background-color: #e74c3c; color: white; } .language-indicator[data-language="fr"] { background-color: #2ecc71; color: white; } .language-indicator[data-language="cn"] { background-color: #f39c12; color: white; } .ripple { position: absolute; background: rgba(255, 255, 255, 0.7); border-radius: 50%; transform: scale(0); animation: ripple 0.6s linear; pointer-events: none; } @keyframes ripple { to { transform: scale(2.5); opacity: 0; } } @keyframes rainbow { 0% { transform: translateX(-100%); } 100% { transform: translateX(100%); } } @media (max-width: 650px) { .container { padding: 20px; max-width: 100%; } .keyboard { gap: 5px; } .key { padding: 8px 3px; font-size: 12px; } .language-tab { padding: 8px 10px; font-size: 12px; } } </style> </head> <body> <div class="container"> <div class="notification" id="notification">Language switched to English</div> <div class="language-selector"> <div class="language-tab active" id="en-tab" data-language="en">English</div> <div class="language-tab" id="es-tab" data-language="es">Español</div> <div class="language-tab" id="fr-tab" data-language="fr">Français</div> <div class="language-tab" id="cn-tab" data-language="cn">中文</div> </div> <div class="language-indicator" data-language="en">EN</div> <div class="typing-area" id="typing-area" contenteditable="true" data-language="en" spellcheck="false"></div> <div class="suggestion-bar" id="suggestion-bar"> <!-- Suggestions will be dynamically populated --> </div> <div class="keyboard" id="keyboard" data-language="en"> <!-- Keys will be dynamically populated --> </div> <div class="special-feature"> <div class="feature-icon"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke="#555" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M8 12L12 16L16 12" stroke="#555" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M12 8V16" stroke="#555" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </div> <div> <strong>Smart Language Detection:</strong> Double-click any key to automatically detect and switch to the language of your recent typing pattern. </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Current language let currentLanguage = 'en'; // Language layouts const layouts = { en: [ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '?', 'shift', '123', 'space', 'backspace', 'return' ], es: [ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ñ', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '?', 'shift', '¿¡', 'space', 'backspace', 'return' ], fr: [ 'a', 'z', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', 'q', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'w', 'x', 'c', 'v', 'b', 'n', 'ç', ',', '.', '?', 'shift', 'é è ê', 'space', 'backspace', 'return' ], cn: [ '七', '万', '父', '戈', '木', '大', '土', '王', '目', '日', '一', '三', '丁', '厂', '工', '弓', '己', '口', '马', '门', '山', '夕', '也', '女', '又', '纟', '小', '水', '火', '已', 'shift', '拼音', 'space', 'backspace', 'return' ] }; // Language-specific suggestions const suggestions = { en: ['Hello!', 'Thank you', 'How are you?', 'Good morning', 'See you soon'], es: ['¡Hola!', 'Gracias', '¿Cómo estás?', 'Buenos días', 'Hasta pronto'], fr: ['Bonjour!', 'Merci', 'Comment ça va?', 'Bon matin', 'À bientôt'], cn: ['你好!', '谢谢', '你好吗?', '早上好', '再见'] }; // DOM elements const keyboard = document.getElementById('keyboard'); const typingArea = document.getElementById('typing-area'); const notification = document.getElementById('notification'); const suggestionBar = document.getElementById('suggestion-bar'); const languageIndicator = document.querySelector('.language-indicator'); // Initialize keyboard function renderKeyboard() { keyboard.innerHTML = ''; layouts[currentLanguage].forEach(key => { const keyElement = document.createElement('div'); keyElement.className = 'key'; keyElement.textContent = key; // Add special classes for certain keys if (['shift', '123', '¿¡', 'é è ê', '拼音'].includes(key)) { keyElement.classList.add('special'); } else if (key === 'space') { keyElement.classList.add('space'); keyElement.textContent = ''; } else if (key === 'backspace') { keyElement.classList.add('special'); keyElement.innerHTML = '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M21 4H8L1 12L8 20H21C21.5304 20 22.0391 19.7893 22.4142 19.4142C22.7893 19.0391 23 18.5304 23 18V6C23 5.46957 22.7893 4.96086 22.4142 4.58579C22.0391 4.21071 21.5304 4 21 4Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M18 9L13 14M13 9L18 14" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'; } else if (key === 'return') { keyElement.classList.add('special'); keyElement.innerHTML = '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9 10L4 15L9 20" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M20 4V11C20 12.0609 19.5786 13.0783 18.8284 13.8284C18.0783 14.5786 17.0609 15 16 15H4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'; } // Add ripple effect to the key keyElement.addEventListener('mousedown', createRipple); // Add key functionality keyElement.addEventListener('click', () => handleKeyPress(key)); // Add to keyboard keyboard.appendChild(keyElement); }); // Set the keyboard's language attribute keyboard.setAttribute('data-language', currentLanguage); } // Initialize suggestions function renderSuggestions() { suggestionBar.innerHTML = ''; suggestions[currentLanguage].forEach(suggestion => { const suggestionElement = document.createElement('div'); suggestionElement.className = 'suggestion'; suggestionElement.setAttribute('data-language', currentLanguage); suggestionElement.textContent = suggestion; suggestionElement.addEventListener('click', () => { insertTextAtCursor(suggestion + ' '); suggestionBar.classList.remove('show'); }); suggestionBar.appendChild(suggestionElement); }); if (typingArea.textContent.trim().length > 0) { suggestionBar.classList.add('show'); } else { suggestionBar.classList.remove('show'); } } // Handle key presses function handleKeyPress(key) { switch(key) { case 'space': insertTextAtCursor(' '); break; case 'backspace': deleteLastCharacter(); break; case 'return': insertTextAtCursor('\n'); break; case 'shift': case '123': case '¿¡': case 'é è ê': case '拼音': // Toggle special characters (simplified for this demo) showNotification(`Special characters for ${currentLanguage.toUpperCase()} activated`); break; default: insertTextAtCursor(key); } // Show suggestions if there's text if (typingArea.textContent.trim().length > 0) { suggestionBar.classList.add('show'); } else { suggestionBar.classList.remove('show'); } } // Insert text at cursor position function insertTextAtCursor(text) { const selection = window.getSelection(); const range = selection.getRangeAt(0); range.deleteContents(); const textNode = document.createTextNode(text); range.insertNode(textNode); // Move cursor to the end range.setStartAfter(textNode); range.setEndAfter(textNode); selection.removeAllRanges(); selection.addRange(range); typingArea.focus(); } // Delete the last character function deleteLastCharacter() { const selection = window.getSelection(); if (selection.rangeCount > 0) { const range = selection.getRangeAt(0); if (range.collapsed) { // If nothing is selected, delete the last character range.setStart(range.startContainer, Math.max(0, range.startOffset - 1)); range.deleteContents(); } else { // If text is selected, delete the selection range.deleteContents(); } } typingArea.focus(); } // Create ripple effect function createRipple(event) { const key = event.currentTarget; const ripple = document.createElement('span'); ripple.classList.add('ripple'); const rect = key.getBoundingClientRect(); const size = Math.max(rect.width, rect.height); ripple.style.width = ripple.style.height = `${size}px`; ripple.style.left = `${event.clientX - rect.left - size/2}px`; ripple.style.top = `${event.clientY - rect.top - size/2}px`; key.appendChild(ripple); setTimeout(() => { ripple.remove(); }, 600); } // Show notification function showNotification(message) { notification.textContent = message; notification.classList.add('show'); setTimeout(() => { notification.classList.remove('show'); }, 2000); } // Switch language function switchLanguage(language) { currentLanguage = language; // Update the UI document.querySelectorAll('.language-tab').forEach(tab => { tab.classList.remove('active'); }); document.getElementById(`${language}-tab`).classList.add('active'); // Update the notification const languages = { en: 'English', es: 'Spanish', fr: 'French', cn: 'Chinese' }; showNotification(`Language switched to ${languages[language]}`); // Update typing area typingArea.setAttribute('data-language', language); // Update language indicator languageIndicator.setAttribute('data-language', language); languageIndicator.textContent = language.toUpperCase(); // Render the keyboard and suggestions renderKeyboard(); renderSuggestions(); } // Set up event listeners document.querySelectorAll('.language-tab').forEach(tab => { tab.addEventListener('click', () => { switchLanguage(tab.getAttribute('data-language')); }); }); // Double-click on any key to trigger smart language detection keyboard.addEventListener('dblclick', () => { // Simulate language detection (cycles through languages) const languages = ['en', 'es', 'fr', 'cn']; const currentIndex = languages.indexOf(currentLanguage); const nextIndex = (currentIndex + 1) % languages.length; switchLanguage(languages[nextIndex]); showNotification(`Smart detection activated: ${languages[nextIndex].toUpperCase()}`); }); // Initialize the keyboard and suggestions renderKeyboard(); renderSuggestions(); // Focus on typing area typingArea.focus(); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>AccessKey - An Inclusive Keyboard Experience</title> <style> *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } :root { --primary: #2B3A67; --secondary: #F4D35E; --accent: #F95738; --light: #F0F6F6; --dark: #151515; --success: #57A773; --keyboard-radius: 16px; --key-radius: 10px; --shadow: 0 4px 12px rgba(0, 0, 0, 0.2); --key-shadow: 0 4px 0 rgba(0, 0, 0, 0.25); --key-press-shadow: 0 2px 0 rgba(0, 0, 0, 0.25); } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; background-color: var(--light); height: 100vh; display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 1rem; overflow: hidden; color: var(--dark); } .container { width: 100%; max-width: 700px; height: 700px; display: flex; flex-direction: column; justify-content: space-between; padding: 1.5rem; background-color: #fff; border-radius: 24px; box-shadow: var(--shadow); transition: transform 0.3s ease; } .header { text-align: center; margin-bottom: 1rem; } .logo { display: flex; align-items: center; justify-content: center; gap: 0.75rem; margin-bottom: 1rem; } .logo-icon { width: 40px; height: 40px; background-color: var(--accent); border-radius: 8px; display: flex; align-items: center; justify-content: center; } .logo-text { font-size: 1.8rem; font-weight: 700; color: var(--primary); } .title { font-size: 1.4rem; font-weight: 600; margin-bottom: 0.5rem; } .subtitle { font-size: 1rem; color: #555; margin-bottom: 1rem; } .mode-toggle { display: flex; justify-content: center; gap: 15px; margin-bottom: 10px; } .mode-btn { background: none; border: 2px solid var(--primary); border-radius: 20px; padding: 8px 16px; font-size: 0.9rem; font-weight: 600; color: var(--primary); cursor: pointer; transition: all 0.2s ease; } .mode-btn.active { background-color: var(--primary); color: var(--light); } .text-display { width: 100%; height: 80px; background-color: var(--light); border: 2px solid var(--primary); border-radius: var(--keyboard-radius); padding: 1rem; font-size: 1.5rem; margin-bottom: 1.5rem; overflow: auto; resize: none; transition: all 0.3s ease; } .text-display.high-contrast { background-color: var(--dark); color: var(--light); border-color: var(--secondary); } .keyboard { flex-grow: 1; display: flex; flex-direction: column; gap: 10px; width: 100%; background-color: var(--primary); border-radius: var(--keyboard-radius); padding: 15px; transition: all 0.3s ease; } .keyboard.high-contrast { background-color: var(--dark); } .keyboard-row { display: flex; justify-content: center; gap: 8px; } .key { flex: 1; height: 60px; display: flex; align-items: center; justify-content: center; font-size: 1.4rem; font-weight: 700; background-color: var(--light); color: var(--dark); border: none; border-radius: var(--key-radius); cursor: pointer; box-shadow: var(--key-shadow); transition: all 0.15s ease; user-select: none; position: relative; overflow: hidden; } .key:active { transform: translateY(3px); box-shadow: var(--key-press-shadow); } .key.wide { flex: 2; } .key.extra-wide { flex: 3; } .key.special { background-color: var(--secondary); } .key.action { background-color: var(--accent); color: var(--light); } .key.high-contrast { background-color: var(--secondary); color: var(--dark); border: 2px solid var(--light); } .key.special.high-contrast { background-color: var(--accent); color: var(--light); } .key.action.high-contrast { background-color: var(--success); color: var(--light); } .key span { position: relative; z-index: 2; } .ripple { position: absolute; background-color: rgba(255, 255, 255, 0.5); border-radius: 50%; transform: scale(0); animation: ripple 0.6s linear; } @keyframes ripple { to { transform: scale(4); opacity: 0; } } .settings { display: flex; justify-content: space-between; margin-top: 1rem; } .settings-btn { background: none; border: none; color: var(--primary); font-size: 0.9rem; font-weight: 600; cursor: pointer; display: flex; align-items: center; gap: 0.5rem; padding: 8px 12px; border-radius: 20px; transition: all 0.2s ease; } .settings-btn:hover { background-color: rgba(43, 58, 103, 0.1); } /* Tooltip styles */ .tooltip { position: relative; display: inline-block; } .tooltip .tooltiptext { visibility: hidden; width: 200px; background-color: var(--dark); color: var(--light); text-align: center; border-radius: 6px; padding: 8px; position: absolute; z-index: 1; bottom: 125%; left: 50%; transform: translateX(-50%); opacity: 0; transition: opacity 0.3s; font-size: 0.85rem; font-weight: normal; } .tooltip:hover .tooltiptext { visibility: visible; opacity: 1; } /* Audio feedback indicator */ .audio-indicator { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0); width: 80px; height: 80px; background-color: rgba(244, 211, 94, 0.3); border-radius: 50%; display: flex; align-items: center; justify-content: center; z-index: 100; pointer-events: none; } .audio-indicator.active { animation: pulse 0.5s ease-out; } @keyframes pulse { 0% { transform: translate(-50%, -50%) scale(0); opacity: 1; } 100% { transform: translate(-50%, -50%) scale(1.5); opacity: 0; } } .visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0; } .feedback-text { position: absolute; bottom: 10px; left: 50%; transform: translateX(-50%); font-size: 0.9rem; background-color: var(--dark); color: var(--light); padding: 8px 16px; border-radius: 20px; opacity: 0; transition: opacity 0.3s ease; } .feedback-text.show { opacity: 1; } @media (max-width: 600px) { .container { padding: 1rem; } .logo-text { font-size: 1.5rem; } .key { height: 50px; font-size: 1.2rem; } .title { font-size: 1.2rem; } .subtitle { font-size: 0.9rem; } } @media (max-height: 700px) { .keyboard-row .key { height: 45px; } } </style> </head> <body> <div class="container"> <div class="header"> <div class="logo"> <div class="logo-icon"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M20 5H4C2.89543 5 2 5.89543 2 7V17C2 18.1046 2.89543 19 4 19H20C21.1046 19 22 18.1046 22 17V7C22 5.89543 21.1046 5 20 5Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M7 10H7.01" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M12 10H12.01" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M17 10H17.01" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M7 14H17" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </div> <span class="logo-text">AccessKey</span> </div> <h1 class="title">Inclusive Typing For Everyone</h1> <p class="subtitle">Large, easy-to-read keys with haptic & audio feedback</p> </div> <div class="mode-toggle"> <button class="mode-btn active tooltip" id="standard-mode"> <span>Standard Mode</span> <span class="tooltiptext">Regular keyboard layout with enhanced visibility</span> </button> <button class="mode-btn tooltip" id="high-contrast-mode"> <span>High Contrast</span> <span class="tooltiptext">Yellow on black contrast for visual accessibility</span> </button> </div> <textarea class="text-display" id="text-display" placeholder="Your text will appear here..." readonly></textarea> <div class="keyboard" id="keyboard"> <div class="keyboard-row"> <button class="key" data-key="1"><span>1</span></button> <button class="key" data-key="2"><span>2</span></button> <button class="key" data-key="3"><span>3</span></button> <button class="key" data-key="4"><span>4</span></button> <button class="key" data-key="5"><span>5</span></button> <button class="key" data-key="6"><span>6</span></button> <button class="key" data-key="7"><span>7</span></button> <button class="key" data-key="8"><span>8</span></button> <button class="key" data-key="9"><span>9</span></button> <button class="key" data-key="0"><span>0</span></button> </div> <div class="keyboard-row"> <button class="key" data-key="q"><span>Q</span></button> <button class="key" data-key="w"><span>W</span></button> <button class="key" data-key="e"><span>E</span></button> <button class="key" data-key="r"><span>R</span></button> <button class="key" data-key="t"><span>T</span></button> <button class="key" data-key="y"><span>Y</span></button> <button class="key" data-key="u"><span>U</span></button> <button class="key" data-key="i"><span>I</span></button> <button class="key" data-key="o"><span>O</span></button> <button class="key" data-key="p"><span>P</span></button> </div> <div class="keyboard-row"> <button class="key" data-key="a"><span>A</span></button> <button class="key" data-key="s"><span>S</span></button> <button class="key" data-key="d"><span>D</span></button> <button class="key" data-key="f"><span>F</span></button> <button class="key" data-key="g"><span>G</span></button> <button class="key" data-key="h"><span>H</span></button> <button class="key" data-key="j"><span>J</span></button> <button class="key" data-key="k"><span>K</span></button> <button class="key" data-key="l"><span>L</span></button> </div> <div class="keyboard-row"> <button class="key special" data-key="shift"><span>⇧</span></button> <button class="key" data-key="z"><span>Z</span></button> <button class="key" data-key="x"><span>X</span></button> <button class="key" data-key="c"><span>C</span></button> <button class="key" data-key="v"><span>V</span></button> <button class="key" data-key="b"><span>B</span></button> <button class="key" data-key="n"><span>N</span></button> <button class="key" data-key="m"><span>M</span></button> <button class="key special" data-key="backspace"><span>⌫</span></button> </div> <div class="keyboard-row"> <button class="key special wide" data-key="?123"><span>?123</span></button> <button class="key" data-key=","><span>,</span></button> <button class="key extra-wide" data-key="space"><span>Space</span></button> <button class="key" data-key="."><span>.</span></button> <button class="key action wide" data-key="enter"><span>Enter</span></button> </div> </div> <div class="settings"> <button class="settings-btn tooltip" id="audio-toggle"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M11 5L6 9H2V15H6L11 19V5Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M15.54 8.46C16.4774 9.39764 17.004 10.6692 17.004 11.995C17.004 13.3208 16.4774 14.5924 15.54 15.53" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> <span>Audio: On</span> <span class="tooltiptext">Toggle audio feedback when pressing keys</span> </button> <button class="settings-btn tooltip" id="haptic-toggle"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M4 9H20" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M4 15H20" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M8 5V19" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M16 5V19" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> <span>Haptic: On</span> <span class="tooltiptext">Toggle vibration feedback when pressing keys</span> </button> </div> </div> <div class="audio-indicator" id="audio-indicator"></div> <div class="feedback-text" id="feedback-text"></div> <script> // DOM Elements const textDisplay = document.getElementById('text-display'); const keyboard = document.getElementById('keyboard'); const keys = document.querySelectorAll('.key'); const standardModeBtn = document.getElementById('standard-mode'); const highContrastModeBtn = document.getElementById('high-contrast-mode'); const audioToggleBtn = document.getElementById('audio-toggle'); const hapticToggleBtn = document.getElementById('haptic-toggle'); const audioIndicator = document.getElementById('audio-indicator'); const feedbackText = document.getElementById('feedback-text'); // State variables let isCapital = false; let audioEnabled = true; let hapticEnabled = true; let currentText = ''; let keySound; // Initialize audio context window.AudioContext = window.AudioContext || window.webkitAudioContext; const audioContext = new AudioContext(); // Function to create key sound function createKeySound() { const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); oscillator.type = 'sine'; oscillator.frequency.setValueAtTime(440, audioContext.currentTime); oscillator.connect(gainNode); gainNode.connect(audioContext.destination); gainNode.gain.setValueAtTime(0.1, audioContext.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.1); oscillator.start(); oscillator.stop(audioContext.currentTime + 0.1); } // Function to provide haptic feedback function hapticFeedback() { if (hapticEnabled && 'vibrate' in navigator) { navigator.vibrate(30); } } // Function to provide audio feedback function audioFeedback() { if (audioEnabled) { createKeySound(); audioIndicator.classList.add('active'); setTimeout(() => { audioIndicator.classList.remove('active'); }, 500); } } // Function to show feedback text function showFeedbackText(text) { feedbackText.textContent = text; feedbackText.classList.add('show'); setTimeout(() => { feedbackText.classList.remove('show'); }, 1500); } // Function to add ripple effect to keys function createRipple(event) { const button = event.currentTarget; const ripple = document.createElement('span'); const rect = button.getBoundingClientRect(); const size = Math.max(rect.width, rect.height); const x = event.clientX - rect.left - size / 2; const y = event.clientY - rect.top - size / 2; ripple.style.width = ripple.style.height = `${size}px`; ripple.style.left = `${x}px`; ripple.style.top = `${y}px`; ripple.classList.add('ripple'); button.appendChild(ripple); setTimeout(() => { ripple.remove(); }, 600); } // Function to handle key press function handleKeyPress(key) { hapticFeedback(); audioFeedback(); switch(key) { case 'backspace': currentText = currentText.slice(0, -1); break; case 'enter': currentText += '\n'; break; case 'space': currentText += ' '; break; case 'shift': isCapital = !isCapital; keys.forEach(k => { const keyData = k.dataset.key; if (keyData && keyData.length === 1 && keyData.match(/[a-z]/i)) { k.querySelector('span').textContent = isCapital ? keyData.toUpperCase() : keyData.toLowerCase(); } }); showFeedbackText(isCapital ? 'Caps Lock: ON' : 'Caps Lock: OFF'); break; case '?123': showFeedbackText('Number keys are available in the top row'); break; default: currentText += isCapital ? key.toUpperCase() : key; } textDisplay.value = currentText; textDisplay.scrollTop = textDisplay.scrollHeight; } // Event listeners for keyboard buttons keys.forEach(key => { key.addEventListener('click', (e) => { const keyValue = key.dataset.key; createRipple(e); handleKeyPress(keyValue); }); }); // Toggle between standard and high contrast modes standardModeBtn.addEventListener('click', () => { standardModeBtn.classList.add('active'); highContrastModeBtn.classList.remove('active'); keyboard.classList.remove('high-contrast'); textDisplay.classList.remove('high-contrast'); keys.forEach(key => key.classList.remove('high-contrast')); showFeedbackText('Standard mode activated'); }); highContrastModeBtn.addEventListener('click', () => { highContrastModeBtn.classList.add('active'); standardModeBtn.classList.remove('active'); keyboard.classList.add('high-contrast'); textDisplay.classList.add('high-contrast'); keys.forEach(key => key.classList.add('high-contrast')); showFeedbackText('High contrast mode activated'); }); // Toggle audio feedback audioToggleBtn.addEventListener('click', () => { audioEnabled = !audioEnabled; audioToggleBtn.querySelector('span').textContent = `Audio: ${audioEnabled ? 'On' : 'Off'}`; showFeedbackText(`Audio feedback ${audioEnabled ? 'enabled' : 'disabled'}`); if (audioEnabled) { // Resume audio context if it was suspended if (audioContext.state === 'suspended') { audioContext.resume(); } } }); // Toggle haptic feedback hapticToggleBtn.addEventListener('click', () => { hapticEnabled = !hapticEnabled; hapticToggleBtn.querySelector('span').textContent = `Haptic: ${hapticEnabled ? 'On' : 'Off'}`; showFeedbackText(`Haptic feedback ${hapticEnabled ? 'enabled' : 'disabled'}`); if (hapticEnabled && 'vibrate' in navigator) { navigator.vibrate(50); } }); // Handle physical keyboard input for demo purposes document.addEventListener('keydown', (e) => { if (e.key === 'Backspace') { const backspaceKey = document.querySelector('[data-key="backspace"]'); backspaceKey.click(); e.preventDefault(); } else if (e.key === 'Enter') { const enterKey = document.querySelector('[data-key="enter"]'); enterKey.click(); e.preventDefault(); } else if (e.key === ' ') { const spaceKey = document.querySelector('[data-key="space"]'); spaceKey.click(); e.preventDefault(); } else if (e.key === 'Shift') { const shiftKey = document.querySelector('[data-key="shift"]'); shiftKey.click(); e.preventDefault(); } else if (e.key.length === 1) { const key = e.key.toLowerCase(); const keyElement = document.querySelector(`[data-key="${key}"]`); if (keyElement) { keyElement.click(); e.preventDefault(); } } }); // Initial placeholder text for the display area setTimeout(() => { const welcomeText = "Welcome to AccessKey! Tap any key to start typing..."; let i = 0; const typeInterval = setInterval(() => { if (i < welcomeText.length) { currentText += welcomeText[i]; textDisplay.value = currentText; i++; } else { clearInterval(typeInterval); } }, 50); }, 1000); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>AR/VR Immersive Keyboard</title> <style> :root { --primary-color: #6C3CE9; --secondary-color: #25CAED; --accent-color: #F637EC; --dark-color: #1A1A2E; --light-color: #E6EFFF; --glow-color: rgba(108, 60, 233, 0.6); --key-base-color: rgba(26, 26, 46, 0.7); --key-top-color: rgba(38, 38, 68, 0.8); } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { background: linear-gradient(135deg, var(--dark-color), #0F0F1A); height: 100vh; width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; overflow: hidden; perspective: 1000px; } .container { width: 100%; max-width: 700px; height: 700px; display: flex; flex-direction: column; align-items: center; justify-content: center; position: relative; } .info-panel { position: absolute; top: 20px; left: 50%; transform: translateX(-50%); background: rgba(26, 26, 46, 0.3); backdrop-filter: blur(10px); border-radius: 15px; padding: 15px 25px; color: var(--light-color); width: 90%; max-width: 600px; text-align: center; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); border: 1px solid rgba(255, 255, 255, 0.1); z-index: 10; transition: all 0.5s ease; } .info-panel h1 { font-size: 24px; font-weight: 600; margin-bottom: 8px; background: linear-gradient(45deg, var(--secondary-color), var(--primary-color)); -webkit-background-clip: text; background-clip: text; color: transparent; } .info-panel p { font-size: 14px; line-height: 1.5; opacity: 0.9; margin-bottom: 10px; } .status-indicator { display: flex; align-items: center; justify-content: center; gap: 5px; margin-top: 5px; } .status-dot { height: 8px; width: 8px; background-color: var(--accent-color); border-radius: 50%; animation: pulse 2s infinite; } @keyframes pulse { 0% { transform: scale(0.8); opacity: 0.7; } 50% { transform: scale(1.2); opacity: 1; } 100% { transform: scale(0.8); opacity: 0.7; } } .keyboard-wrapper { position: relative; transform-style: preserve-3d; transform: rotateX(20deg); transition: transform 0.5s ease; margin-top: 120px; } .keyboard { display: grid; grid-template-columns: repeat(14, 1fr); gap: 8px; width: 95%; max-width: 650px; margin: 0 auto; padding: 15px; background: rgba(26, 26, 46, 0.2); backdrop-filter: blur(5px); border-radius: 15px; box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4), 0 0 80px rgba(108, 60, 233, 0.1), inset 0 0 10px rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.05); transform-style: preserve-3d; } .key { aspect-ratio: 1/1; position: relative; cursor: pointer; transform-style: preserve-3d; transform: translateZ(0); transition: transform 0.15s ease, box-shadow 0.15s ease; } .key.wide { grid-column: span 2; } .key.extra-wide { grid-column: span 3; } .key.super-wide { grid-column: span 7; } .key-face { position: absolute; width: 100%; height: 100%; border-radius: 8px; display: flex; align-items: center; justify-content: center; color: var(--light-color); font-weight: 500; font-size: 16px; user-select: none; text-shadow: 0 0 5px rgba(0, 0, 0, 0.5); overflow: hidden; } .key-top { transform: translateZ(5px); background: var(--key-top-color); box-shadow: 0 0 15px rgba(108, 60, 233, 0.1); border: 1px solid rgba(255, 255, 255, 0.1); backdrop-filter: blur(2px); } .key-right { transform: rotateY(90deg) translateZ(calc(100% - 4px)); background: var(--key-base-color); width: 8px; right: -4px; } .key-left { transform: rotateY(-90deg) translateZ(4px); background: var(--key-base-color); width: 8px; left: -4px; } .key-bottom { transform: rotateX(-90deg) translateZ(calc(100% - 4px)); background: var(--key-base-color); height: 8px; bottom: -4px; } .key-front { transform: rotateX(90deg) translateZ(4px); background: var(--key-base-color); height: 8px; top: -4px; } .key:hover { transform: translateZ(2px); z-index: 1; } .key:hover .key-top { box-shadow: 0 0 10px var(--glow-color), 0 0 20px rgba(108, 60, 233, 0.2); background: rgba(48, 48, 78, 0.9); } .key.active { transform: translateZ(-5px); } .key.active .key-top { box-shadow: 0 0 15px var(--glow-color), 0 0 30px rgba(108, 60, 233, 0.4); background: rgba(58, 58, 88, 0.9); } .ripple { position: absolute; border-radius: 50%; background: radial-gradient(circle, var(--glow-color) 10%, transparent 70%); transform: scale(0); animation: rippleEffect 0.8s ease-out; pointer-events: none; } @keyframes rippleEffect { to { transform: scale(2.5); opacity: 0; } } .hand-tracker { position: absolute; width: 40px; height: 40px; border-radius: 50%; border: 2px solid var(--secondary-color); box-shadow: 0 0 10px var(--secondary-color), 0 0 20px rgba(37, 202, 237, 0.4); pointer-events: none; opacity: 0; z-index: 100; filter: blur(1px); transition: opacity 0.3s ease; } .mode-switcher { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); display: flex; align-items: center; gap: 15px; background: rgba(26, 26, 46, 0.3); backdrop-filter: blur(10px); border-radius: 12px; padding: 10px 15px; z-index: 10; } .switch-btn { padding: 8px 15px; border-radius: 8px; background: transparent; color: var(--light-color); border: 1px solid rgba(255, 255, 255, 0.1); cursor: pointer; transition: all 0.3s ease; } .switch-btn.active { background: var(--primary-color); box-shadow: 0 0 15px rgba(108, 60, 233, 0.4); } .floating-particle { position: absolute; width: 5px; height: 5px; background: rgba(255, 255, 255, 0.6); border-radius: 50%; pointer-events: none; animation: float 3s infinite ease-in-out; opacity: 0.7; } @keyframes float { 0%, 100% { transform: translateY(0) translateX(0); } 50% { transform: translateY(-20px) translateX(10px); } } .gesture-hint { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(108, 60, 233, 0.2); backdrop-filter: blur(5px); padding: 20px; border-radius: 15px; color: var(--light-color); text-align: center; font-size: 18px; border: 1px solid rgba(255, 255, 255, 0.1); box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); z-index: 100; opacity: 0; transition: opacity 0.5s ease, transform 0.5s ease; pointer-events: none; } .gesture-hint.show { opacity: 1; transform: translate(-50%, -50%) scale(1); } #text-display { width: 90%; max-width: 600px; height: 60px; background: rgba(26, 26, 46, 0.4); backdrop-filter: blur(5px); border-radius: 10px; color: var(--light-color); border: 1px solid rgba(255, 255, 255, 0.1); margin-top: 20px; padding: 10px 15px; font-size: 18px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } @media (max-width: 600px) { .keyboard { grid-template-columns: repeat(10, 1fr); width: 90%; gap: 5px; padding: 10px; } .key-face { font-size: 12px; } .info-panel h1 { font-size: 18px; } .info-panel p { font-size: 12px; } } .key-icon { font-size: 14px; } </style> </head> <body> <div class="container"> <div class="info-panel"> <h1>AR/VR Immersive Keyboard</h1> <p>Experience next-gen typing with our spatial 3D interface. Wave your hands to type or tap for precision input.</p> <div class="status-indicator"> <div class="status-dot"></div> <span>Spatial tracking active</span> </div> </div> <div id="text-display"></div> <div class="keyboard-wrapper"> <div class="keyboard" id="keyboard"></div> </div> <div class="hand-tracker" id="hand-tracker"></div> <div class="gesture-hint" id="gesture-hint"> Wave your hand to activate gesture typing </div> <div class="mode-switcher"> <button class="switch-btn active" data-mode="tap">Tap Mode</button> <button class="switch-btn" data-mode="gesture">Gesture Mode</button> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Base key layout const keyLayout = [ { key: '`', label: '`' }, { key: '1', label: '1' }, { key: '2', label: '2' }, { key: '3', label: '3' }, { key: '4', label: '4' }, { key: '5', label: '5' }, { key: '6', label: '6' }, { key: '7', label: '7' }, { key: '8', label: '8' }, { key: '9', label: '9' }, { key: '0', label: '0' }, { key: '-', label: '-' }, { key: '=', label: '=' }, { key: 'Backspace', label: '⌫', wide: true }, { key: 'Tab', label: 'Tab', wide: true }, { key: 'q', label: 'Q' }, { key: 'w', label: 'W' }, { key: 'e', label: 'E' }, { key: 'r', label: 'R' }, { key: 't', label: 'T' }, { key: 'y', label: 'Y' }, { key: 'u', label: 'U' }, { key: 'i', label: 'I' }, { key: 'o', label: 'O' }, { key: 'p', label: 'P' }, { key: '[', label: '[' }, { key: ']', label: ']' }, { key: '\\', label: '\\' }, { key: 'CapsLock', label: 'Caps', wide: true }, { key: 'a', label: 'A' }, { key: 's', label: 'S' }, { key: 'd', label: 'D' }, { key: 'f', label: 'F' }, { key: 'g', label: 'G' }, { key: 'h', label: 'H' }, { key: 'j', label: 'J' }, { key: 'k', label: 'K' }, { key: 'l', label: 'L' }, { key: ';', label: ';' }, { key: '\'', label: '\'' }, { key: 'Enter', label: 'Enter', extra-wide: true }, { key: 'Shift', label: 'Shift', extra-wide: true }, { key: 'z', label: 'Z' }, { key: 'x', label: 'X' }, { key: 'c', label: 'C' }, { key: 'v', label: 'V' }, { key: 'b', label: 'B' }, { key: 'n', label: 'N' }, { key: 'm', label: 'M' }, { key: ',', label: ',' }, { key: '.', label: '.' }, { key: '/', label: '/' }, { key: 'Shift', label: 'Shift', extra-wide: true }, { key: 'Control', label: 'Ctrl', wide: true }, { key: 'Meta', label: '⌘', wide: true }, { key: 'Alt', label: 'Alt', wide: true }, { key: ' ', label: 'Space', super-wide: true }, { key: 'Alt', label: 'Alt', wide: true }, { key: 'Control', label: 'Ctrl', wide: true } ]; // DOM Elements const keyboard = document.getElementById('keyboard'); const handTracker = document.getElementById('hand-tracker'); const textDisplay = document.getElementById('text-display'); const gestureHint = document.getElementById('gesture-hint'); const modeSwitchers = document.querySelectorAll('.switch-btn'); const keyboardWrapper = document.querySelector('.keyboard-wrapper'); // State let currentMode = 'tap'; let currentText = ''; let capsLockActive = false; let floatingParticles = []; // Create keyboard function createKeyboard() { keyboard.innerHTML = ''; keyLayout.forEach(keyObj => { const keyElement = document.createElement('div'); keyElement.className = 'key'; keyElement.dataset.key = keyObj.key; if (keyObj.wide) keyElement.classList.add('wide'); if (keyObj['extra-wide']) keyElement.classList.add('extra-wide'); if (keyObj['super-wide']) keyElement.classList.add('super-wide'); // Create key faces (for 3D effect) const faces = ['top', 'right', 'left', 'bottom', 'front']; faces.forEach(face => { const faceElement = document.createElement('div'); faceElement.className = `key-face key-${face}`; if (face === 'top') { if (keyObj.icon) { faceElement.innerHTML = `<span class="key-icon">${keyObj.icon}</span>`; } else { faceElement.textContent = keyObj.label; } } keyElement.appendChild(faceElement); }); // Add click event keyElement.addEventListener('click', () => { pressKey(keyObj.key); createRippleEffect(keyElement, event); }); // Add touch event for mobile keyElement.addEventListener('touchstart', (e) => { pressKey(keyObj.key); createRippleEffect(keyElement, e.touches[0]); e.preventDefault(); }); keyboard.appendChild(keyElement); }); } // Handle key press function pressKey(key) { const keyElement = document.querySelector(`.key[data-key="${key}"]`); if (keyElement) { keyElement.classList.add('active'); setTimeout(() => { keyElement.classList.remove('active'); }, 150); } // Handle special keys switch (key) { case 'Backspace': currentText = currentText.slice(0, -1); break; case 'Enter': currentText += '\n'; break; case 'CapsLock': capsLockActive = !capsLockActive; if (capsLockActive) { keyElement.querySelector('.key-top').style.boxShadow = '0 0 15px var(--accent-color)'; } else { keyElement.querySelector('.key-top').style.boxShadow = ''; } break; case 'Tab': currentText += ' '; break; case 'Space': currentText += ' '; break; case 'Shift': case 'Control': case 'Alt': case 'Meta': // No action for these keys in this demo break; default: if (key.length === 1) { if (capsLockActive) { currentText += key.toUpperCase(); } else { currentText += key; } } } updateTextDisplay(); } // Create ripple effect on key press function createRippleEffect(element, event) { const ripple = document.createElement('div'); ripple.className = 'ripple'; // If event is defined (from click), position the ripple if (event) { const rect = element.getBoundingClientRect(); const size = Math.max(rect.width, rect.height); ripple.style.width = ripple.style.height = `${size}px`; ripple.style.left = `${event.clientX - rect.left - size / 2}px`; ripple.style.top = `${event.clientY - rect.top - size / 2}px`; } else { // Center the ripple if no event ripple.style.width = ripple.style.height = '100%'; ripple.style.left = ripple.style.top = '0'; } element.appendChild(ripple); // Remove the ripple after animation setTimeout(() => { ripple.remove(); }, 800); } // Update text display function updateTextDisplay() { textDisplay.textContent = currentText; } // Hand tracking simulation function initHandTracking() { // Mouse movement for demo purpose document.addEventListener('mousemove', (e) => { // Only show in gesture mode if (currentMode === 'gesture') { handTracker.style.opacity = '1'; handTracker.style.left = `${e.clientX}px`; handTracker.style.top = `${e.clientY}px`; // Check if hovering over keys in gesture mode const hoveredKey = document.elementFromPoint(e.clientX, e.clientY); if (hoveredKey && hoveredKey.closest('.key')) { const keyElement = hoveredKey.closest('.key'); keyElement.style.transform = 'translateZ(8px)'; setTimeout(() => { keyElement.style.transform = ''; }, 300); } } else { handTracker.style.opacity = '0'; } }); // Simulate gesture typing with click in gesture mode document.addEventListener('click', (e) => { if (currentMode === 'gesture') { const clickedElement = document.elementFromPoint(e.clientX, e.clientY); if (clickedElement && clickedElement.closest('.key')) { const keyElement = clickedElement.closest('.key'); const key = keyElement.dataset.key; pressKey(key); createRippleEffect(keyElement, e); createFloatingParticles(e.clientX, e.clientY); } } }); // Hide hand tracker when mouse leaves window document.addEventListener('mouseout', () => { handTracker.style.opacity = '0'; }); } // Mode switching function initModeSwitching() { modeSwitchers.forEach(btn => { btn.addEventListener('click', () => { modeSwitchers.forEach(b => b.classList.remove('active')); btn.classList.add('active'); currentMode = btn.dataset.mode; // Change keyboard appearance based on mode if (currentMode === 'gesture') { keyboard.style.background = 'rgba(26, 26, 46, 0.4)'; keyboard.style.boxShadow = '0 20px 40px rgba(0, 0, 0, 0.5), 0 0 80px rgba(246, 55, 236, 0.2)'; keyboardWrapper.style.transform = 'rotateX(30deg)'; // Show gesture hint gestureHint.classList.add('show'); setTimeout(() => { gestureHint.classList.remove('show'); }, 3000); } else { keyboard.style.background = 'rgba(26, 26, 46, 0.2)'; keyboard.style.boxShadow = '0 20px 40px rgba(0, 0, 0, 0.4), 0 0 80px rgba(108, 60, 233, 0.1)'; keyboardWrapper.style.transform = 'rotateX(20deg)'; } }); }); } // Create floating particles for visual feedback function createFloatingParticles(x, y) { for (let i = 0; i < 5; i++) { const particle = document.createElement('div'); particle.className = 'floating-particle'; // Random positions around click const offsetX = Math.random() * 40 - 20; const offsetY = Math.random() * 40 - 20; particle.style.left = `${x + offsetX}px`; particle.style.top = `${y + offsetY}px`; // Random sizes const size = Math.random() * 8 + 3; particle.style.width = `${size}px`; particle.style.height = `${size}px`; // Random colors const colors = ['var(--primary-color)', 'var(--secondary-color)', 'var(--accent-color)']; particle.style.background = colors[Math.floor(Math.random() * colors.length)]; // Random animation duration const duration = Math.random() * 2 + 1; particle.style.animationDuration = `${duration}s`; document.body.appendChild(particle); // Remove particle after animation setTimeout(() => { particle.remove(); }, duration * 1000); } } // Create keyboard 3D tilt effect function initKeyboardTilt() { const container = document.querySelector('.container'); container.addEventListener('mousemove', (e) => { const rect = container.getBoundingClientRect(); const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; const moveX = (e.clientX - centerX) / 20; const moveY = (e.clientY - centerY) / 20; keyboard.style.transform = `translateZ(20px) rotateX(${-moveY}deg) rotateY(${moveX}deg)`; }); container.addEventListener('mouseleave', () => { keyboard.style.transform = 'translateZ(0) rotateX(0) rotateY(0)'; }); } // Initialize everything createKeyboard(); initHandTracking(); initModeSwitching(); initKeyboardTilt(); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Artistic Expression Keyboard</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { display: flex; flex-direction: column; justify-content: center; align-items: center; min-height: 100vh; background: #111; overflow: hidden; padding: 20px; } .container { width: 100%; max-width: 700px; height: 700px; display: flex; flex-direction: column; justify-content: space-between; gap: 15px; padding: 20px; position: relative; } .header { text-align: center; color: #fff; z-index: 1; margin-bottom: 10px; } h1 { font-size: 2.5rem; margin-bottom: 10px; background: linear-gradient(45deg, #FF5F6D, #FFC371, #7F7FD5); -webkit-background-clip: text; background-clip: text; color: transparent; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); } .subtitle { font-size: 1.1rem; line-height: 1.5; font-weight: 300; margin-bottom: 20px; color: #ddd; } .canvas-wrapper { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; overflow: hidden; } canvas { position: absolute; width: 100%; height: 100%; } .keyboard-container { width: 100%; flex-grow: 1; display: flex; flex-direction: column; gap: 5px; background: rgba(20, 20, 20, 0.7); padding: 15px; border-radius: 20px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5); backdrop-filter: blur(10px); z-index: 1; } .keyboard-row { display: flex; gap: 5px; width: 100%; } .key { flex-grow: 1; aspect-ratio: 1; border-radius: 10px; background: rgba(40, 40, 40, 0.7); display: flex; justify-content: center; align-items: center; font-size: 1.2rem; font-weight: bold; cursor: pointer; user-select: none; overflow: hidden; position: relative; transition: all 0.3s ease; box-shadow: 0 4px 0 rgba(0, 0, 0, 0.3); } .key.special { flex-grow: 2; } .key.space { flex-grow: 3; } .key:hover { transform: translateY(-2px); box-shadow: 0 6px 0 rgba(0, 0, 0, 0.3); } .key:active, .key.pressed { transform: translateY(2px); box-shadow: 0 2px 0 rgba(0, 0, 0, 0.3); } .key.animated { animation: pulse 0.5s ease; } .key-char { position: relative; z-index: 2; font-size: 1.4rem; font-weight: 600; color: white; text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); } .key-bg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(135deg, var(--start-color), var(--end-color)); opacity: 0.7; z-index: 1; } .flourish-particle { position: absolute; background-size: contain; background-repeat: no-repeat; background-position: center; pointer-events: none; z-index: 10; } .text-display { width: 100%; min-height: 70px; padding: 15px; background: rgba(255, 255, 255, 0.15); border-radius: 10px; font-size: 1.2rem; color: white; margin-bottom: 15px; box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.2); overflow-wrap: break-word; position: relative; z-index: 3; } .cursor { display: inline-block; width: 2px; height: 1.2em; background-color: white; margin-left: 2px; animation: blink 1s infinite; vertical-align: middle; } .controls { display: flex; justify-content: space-between; margin-top: 10px; gap: 10px; } .control-btn { padding: 8px 15px; background: linear-gradient(135deg, #7F7FD5, #91EAE4); border: none; border-radius: 8px; color: white; font-weight: bold; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2); } .control-btn:hover { transform: translateY(-2px); box-shadow: 0 6px 8px rgba(0, 0, 0, 0.2); } .control-btn:active { transform: translateY(1px); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); } @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.1); } 100% { transform: scale(1); } } .theme-selector { display: flex; justify-content: center; gap: 10px; margin-top: 10px; } .theme { width: 25px; height: 25px; border-radius: 50%; cursor: pointer; box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); transition: transform 0.3s ease; } .theme:hover { transform: scale(1.2); } .theme.active { border: 2px solid white; } #theme1 { background: linear-gradient(135deg, #FF5F6D, #FFC371); } #theme2 { background: linear-gradient(135deg, #7F7FD5, #91EAE4); } #theme3 { background: linear-gradient(135deg, #EECDA3, #EF629F); } #theme4 { background: linear-gradient(135deg, #43C6AC, #F8FFAE); } /* Media Queries */ @media (max-width: 600px) { .container { padding: 10px; } h1 { font-size: 2rem; } .subtitle { font-size: 0.9rem; } .keyboard-container { padding: 10px; } .key-char { font-size: 1rem; } } @media (max-height: 700px) { .container { height: auto; } } </style> </head> <body> <div class="container"> <div class="canvas-wrapper"> <canvas id="gradient-canvas"></canvas> <canvas id="flourish-canvas"></canvas> </div> <div class="header"> <h1>Artistic Expression Keyboard</h1> <p class="subtitle">Where every keystroke becomes a creative flourish. Each key triggers unique animations, colors, and patterns.</p> </div> <div class="text-display" id="text-display"><span class="cursor"></span></div> <div class="keyboard-container"> <div class="keyboard-row"> <div class="key" data-key="q"><div class="key-bg"></div><span class="key-char">Q</span></div> <div class="key" data-key="w"><div class="key-bg"></div><span class="key-char">W</span></div> <div class="key" data-key="e"><div class="key-bg"></div><span class="key-char">E</span></div> <div class="key" data-key="r"><div class="key-bg"></div><span class="key-char">R</span></div> <div class="key" data-key="t"><div class="key-bg"></div><span class="key-char">T</span></div> <div class="key" data-key="y"><div class="key-bg"></div><span class="key-char">Y</span></div> <div class="key" data-key="u"><div class="key-bg"></div><span class="key-char">U</span></div> <div class="key" data-key="i"><div class="key-bg"></div><span class="key-char">I</span></div> <div class="key" data-key="o"><div class="key-bg"></div><span class="key-char">O</span></div> <div class="key" data-key="p"><div class="key-bg"></div><span class="key-char">P</span></div> </div> <div class="keyboard-row"> <div class="key" data-key="a"><div class="key-bg"></div><span class="key-char">A</span></div> <div class="key" data-key="s"><div class="key-bg"></div><span class="key-char">S</span></div> <div class="key" data-key="d"><div class="key-bg"></div><span class="key-char">D</span></div> <div class="key" data-key="f"><div class="key-bg"></div><span class="key-char">F</span></div> <div class="key" data-key="g"><div class="key-bg"></div><span class="key-char">G</span></div> <div class="key" data-key="h"><div class="key-bg"></div><span class="key-char">H</span></div> <div class="key" data-key="j"><div class="key-bg"></div><span class="key-char">J</span></div> <div class="key" data-key="k"><div class="key-bg"></div><span class="key-char">K</span></div> <div class="key" data-key="l"><div class="key-bg"></div><span class="key-char">L</span></div> </div> <div class="keyboard-row"> <div class="key special" data-key="shift"><div class="key-bg"></div><span class="key-char">⇧</span></div> <div class="key" data-key="z"><div class="key-bg"></div><span class="key-char">Z</span></div> <div class="key" data-key="x"><div class="key-bg"></div><span class="key-char">X</span></div> <div class="key" data-key="c"><div class="key-bg"></div><span class="key-char">C</span></div> <div class="key" data-key="v"><div class="key-bg"></div><span class="key-char">V</span></div> <div class="key" data-key="b"><div class="key-bg"></div><span class="key-char">B</span></div> <div class="key" data-key="n"><div class="key-bg"></div><span class="key-char">N</span></div> <div class="key" data-key="m"><div class="key-bg"></div><span class="key-char">M</span></div> <div class="key special" data-key="backspace"><div class="key-bg"></div><span class="key-char">⌫</span></div> </div> <div class="keyboard-row"> <div class="key" data-key="1"><div class="key-bg"></div><span class="key-char">1</span></div> <div class="key" data-key="2"><div class="key-bg"></div><span class="key-char">2</span></div> <div class="key" data-key="3"><div class="key-bg"></div><span class="key-char">3</span></div> <div class="key space" data-key=" "><div class="key-bg"></div><span class="key-char">space</span></div> <div class="key" data-key="."><div class="key-bg"></div><span class="key-char">.</span></div> <div class="key" data-key=","><div class="key-bg"></div><span class="key-char">,</span></div> <div class="key" data-key="?"><div class="key-bg"></div><span class="key-char">?</span></div> </div> </div> <div class="controls"> <button class="control-btn" id="clear-btn">Clear Text</button> <div class="theme-selector"> <div class="theme active" id="theme1"></div> <div class="theme" id="theme2"></div> <div class="theme" id="theme3"></div> <div class="theme" id="theme4"></div> </div> <button class="control-btn" id="toggle-flourish">Toggle Flourish</button> </div> </div> <script> document.addEventListener('DOMContentLoaded', () => { // Initialize canvas const gradientCanvas = document.getElementById('gradient-canvas'); const gradientCtx = gradientCanvas.getContext('2d'); const flourishCanvas = document.getElementById('flourish-canvas'); const flourishCtx = flourishCanvas.getContext('2d'); // Set canvas size function resizeCanvas() { gradientCanvas.width = gradientCanvas.offsetWidth; gradientCanvas.height = gradientCanvas.offsetHeight; flourishCanvas.width = flourishCanvas.offsetWidth; flourishCanvas.height = flourishCanvas.offsetHeight; } window.addEventListener('resize', resizeCanvas); resizeCanvas(); // Background animation let gradientColors = { theme1: ['#FF5F6D', '#FFC371', '#7F7FD5', '#FF5F6D'], theme2: ['#7F7FD5', '#91EAE4', '#86A8E7', '#7F7FD5'], theme3: ['#EECDA3', '#EF629F', '#EECDA3', '#EF629F'], theme4: ['#43C6AC', '#F8FFAE', '#43C6AC', '#F8FFAE'] }; let currentTheme = 'theme1'; let currentColors = gradientColors[currentTheme]; function animateGradient() { const width = gradientCanvas.width; const height = gradientCanvas.height; const time = Date.now() * 0.001; gradientCtx.clearRect(0, 0, width, height); const gradient = gradientCtx.createLinearGradient( width * Math.sin(time * 0.1) * 0.5 + width * 0.5, height * Math.cos(time * 0.1) * 0.5 + height * 0.5, width * Math.sin(time * 0.1 + Math.PI) * 0.5 + width * 0.5, height * Math.cos(time * 0.1 + Math.PI) * 0.5 + height * 0.5 ); for (let i = 0; i < currentColors.length; i++) { gradient.addColorStop(i / (currentColors.length - 1), currentColors[i]); } gradientCtx.fillStyle = gradient; gradientCtx.fillRect(0, 0, width, height); requestAnimationFrame(animateGradient); } animateGradient(); // Initialize key backgrounds with gradients const keys = document.querySelectorAll('.key'); const colorPairs = [ ['#FF5F6D', '#FFC371'], ['#7F7FD5', '#91EAE4'], ['#EECDA3', '#EF629F'], ['#43C6AC', '#F8FFAE'], ['#FF8008', '#FFC837'], ['#4776E6', '#8E54E9'], ['#00B4DB', '#0083B0'], ['#FF416C', '#FF4B2B'], ['#654EA3', '#EAAFC8'], ['#DD5E89', '#F7BB97'] ]; keys.forEach(key => { const randomIndex = Math.floor(Math.random() * colorPairs.length); const keyBg = key.querySelector('.key-bg'); keyBg.style.setProperty('--start-color', colorPairs[randomIndex][0]); keyBg.style.setProperty('--end-color', colorPairs[randomIndex][1]); }); // Text display functionality const textDisplay = document.getElementById('text-display'); let textContent = ''; let cursorElement = textDisplay.querySelector('.cursor'); function updateTextDisplay() { textDisplay.innerHTML = textContent + '<span class="cursor"></span>'; } // Flourish animation const flourishShapes = [ 'circle', 'triangle', 'square', 'star', 'spiral', 'brush', 'splash', 'wave', 'zigzag', 'line' ]; const flourishColors = [ '#FF5F6D', '#FFC371', '#7F7FD5', '#91EAE4', '#EECDA3', '#EF629F', '#43C6AC', '#F8FFAE' ]; let flourishEnabled = true; function createFlourish(x, y, keyChar) { if (!flourishEnabled) return; // Calculate position based on key const key = document.querySelector(`.key[data-key="${keyChar.toLowerCase()}"]`); if (!key) return; const keyRect = key.getBoundingClientRect(); const containerRect = document.querySelector('.container').getBoundingClientRect(); x = keyRect.left + keyRect.width / 2 - containerRect.left; y = keyRect.top + keyRect.height / 2 - containerRect.top; // Determine number of particles for this flourish const particleCount = Math.floor(Math.random() * 8) + 5; for (let i = 0; i < particleCount; i++) { const shape = flourishShapes[Math.floor(Math.random() * flourishShapes.length)]; const color = flourishColors[Math.floor(Math.random() * flourishColors.length)]; const size = Math.random() * 20 + 10; const angle = Math.random() * Math.PI * 2; const distance = Math.random() * 100 + 20; const duration = Math.random() * 1000 + 500; const startX = x; const startY = y; const endX = x + Math.cos(angle) * distance; const endY = y + Math.sin(angle) * distance; const particle = document.createElement('div'); particle.classList.add('flourish-particle'); particle.style.width = `${size}px`; particle.style.height = `${size}px`; particle.style.left = `${startX}px`; particle.style.top = `${startY}px`; particle.style.opacity = '1'; // Set shape and color if (shape === 'circle') { particle.style.borderRadius = '50%'; particle.style.backgroundColor = color; } else if (shape === 'square') { particle.style.backgroundColor = color; } else if (shape === 'triangle') { particle.style.backgroundColor = 'transparent'; particle.style.width = '0'; particle.style.height = '0'; particle.style.borderLeft = `${size/2}px solid transparent`; particle.style.borderRight = `${size/2}px solid transparent`; particle.style.borderBottom = `${size}px solid ${color}`; } else if (shape === 'star') { particle.style.backgroundColor = color; particle.style.clipPath = 'polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%)'; } else { particle.style.backgroundColor = color; } document.querySelector('.container').appendChild(particle); // Animate the particle const startTime = Date.now(); function animateParticle() { const elapsed = Date.now() - startTime; const progress = Math.min(elapsed / duration, 1); const currentX = startX + (endX - startX) * progress; const currentY = startY + (endY - startY) * progress; particle.style.left = `${currentX}px`; particle.style.top = `${currentY}px`; particle.style.opacity = 1 - progress; if (progress < 1) { requestAnimationFrame(animateParticle); } else { particle.remove(); } } requestAnimationFrame(animateParticle); } } // Keyboard functionality keys.forEach(key => { key.addEventListener('click', () => { handleKeyPress(key.dataset.key, key); }); }); function handleKeyPress(char, keyElement) { if (!keyElement) { keyElement = document.querySelector(`.key[data-key="${char.toLowerCase()}"]`); } if (keyElement) { // Visual feedback keyElement.classList.add('pressed'); keyElement.classList.add('animated'); setTimeout(() => { keyElement.classList.remove('pressed'); keyElement.classList.remove('animated'); }, 200); // Create flourish animation createFlourish(0, 0, keyElement.dataset.key); } // Handle special keys if (char === 'backspace') { textContent = textContent.slice(0, -1); } else if (char === 'shift') { // Just visual effect, no actual action } else { // Add character to text textContent += char; } updateTextDisplay(); } // Keyboard event handling document.addEventListener('keydown', (e) => { let key = e.key.toLowerCase(); if (key === 'backspace') { e.preventDefault(); handleKeyPress('backspace'); } else if (key === 'shift') { handleKeyPress('shift'); } else if (key === ' ') { e.preventDefault(); handleKeyPress(' '); } else if (/^[a-z0-9,.\?]$/.test(key)) { handleKeyPress(key); } }); // Theme selectors const themes = document.querySelectorAll('.theme'); themes.forEach(theme => { theme.addEventListener('click', () => { themes.forEach(t => t.classList.remove('active')); theme.classList.add('active'); currentTheme = theme.id; currentColors = gradientColors[currentTheme]; // Update key colors based on theme keys.forEach(key => { const keyBg = key.querySelector('.key-bg'); const themeColorStart = currentColors[0]; const themeColorEnd = currentColors[1]; keyBg.style.setProperty('--start-color', themeColorStart); keyBg.style.setProperty('--end-color', themeColorEnd); }); }); }); // Clear text button document.getElementById('clear-btn').addEventListener('click', () => { textContent = ''; updateTextDisplay(); }); // Toggle flourish button const toggleFlourish = document.getElementById('toggle-flourish'); toggleFlourish.addEventListener('click', () => { flourishEnabled = !flourishEnabled; toggleFlourish.textContent = flourishEnabled ? 'Toggle Flourish' : 'Enable Flourish'; }); }); </script> </body> </html>
<html> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> :root { --primary-color: #1a1a1a; --secondary-color: #2a2a2a; --accent-color: #4a4a4a; --text-color: #f0f0f0; --highlight-color: rgba(255, 255, 255, 0.15); --key-active: rgba(255, 255, 255, 0.25); --key-shadow: rgba(0, 0, 0, 0.2); --transition-speed: 0.2s; } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } body { background-color: var(--primary-color); color: var(--text-color); display: flex; flex-direction: column; justify-content: center; align-items: center; min-height: 100vh; overflow: hidden; padding: 20px; } .device-container { width: 100%; max-width: 700px; height: 700px; display: flex; flex-direction: column; justify-content: center; align-items: center; position: relative; } .smartwatch { width: 250px; height: 300px; background-color: var(--primary-color); border-radius: 40px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); overflow: hidden; position: relative; border: 2px solid var(--accent-color); transition: transform 0.5s ease-in-out; } .watch-screen { width: 100%; height: 100%; background-color: var(--primary-color); padding: 15px; display: flex; flex-direction: column; justify-content: space-between; } .status-bar { display: flex; justify-content: space-between; margin-bottom: 10px; font-size: 12px; } .time { font-weight: bold; } .icons { display: flex; gap: 5px; } .battery-icon { width: 20px; height: 10px; border: 1px solid var(--text-color); border-radius: 2px; position: relative; } .battery-level { height: 100%; width: 60%; background-color: var(--text-color); transition: width 0.5s ease; } .keyboard-container { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; } .input-area { margin-bottom: 10px; padding: 8px; background-color: var(--secondary-color); border-radius: 8px; min-height: 60px; color: var(--text-color); font-size: 14px; overflow-wrap: break-word; position: relative; } .cursor { display: inline-block; width: 2px; height: 14px; background-color: var(--text-color); vertical-align: middle; animation: blink 1s infinite; } @keyframes blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } } .keyboard { display: grid; grid-template-rows: repeat(4, 1fr); gap: 6px; width: 100%; } .keyboard-row { display: grid; grid-template-columns: repeat(10, 1fr); gap: 4px; width: 100%; } .keyboard-row:last-child { grid-template-columns: 2fr 6fr 2fr; } .key { background-color: rgba(255, 255, 255, 0.08); border-radius: 6px; display: flex; justify-content: center; align-items: center; font-size: 12px; color: var(--text-color); user-select: none; cursor: pointer; height: 30px; transition: all var(--transition-speed) ease; backdrop-filter: blur(4px); position: relative; overflow: hidden; } .key:hover, .key:active { background-color: var(--key-active); } .key::before { content: ""; position: absolute; top: 50%; left: 50%; width: 0; height: 0; background-color: var(--highlight-color); border-radius: 50%; transform: translate(-50%, -50%); transition: width 0.4s ease, height 0.4s ease; z-index: -1; } .key:active::before { width: 200%; height: 200%; } .key.space { grid-column: span 6; } .keyboard-suggestion { display: flex; gap: 8px; margin-bottom: 8px; justify-content: space-between; } .suggestion { flex: 1; background-color: var(--secondary-color); border-radius: 6px; padding: 6px; font-size: 12px; text-align: center; cursor: pointer; transition: background-color var(--transition-speed) ease; } .suggestion:hover { background-color: var(--accent-color); } .description { max-width: 600px; text-align: center; margin-top: 40px; line-height: 1.6; opacity: 0; transform: translateY(20px); transition: opacity 0.6s ease, transform 0.6s ease; } .feature-list { display: flex; flex-wrap: wrap; justify-content: center; gap: 20px; margin-top: 30px; } .feature { background-color: var(--secondary-color); padding: 15px; border-radius: 8px; width: 150px; height: 150px; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; font-size: 14px; transition: transform 0.3s ease, background-color 0.3s ease; opacity: 0; transform: translateY(20px); } .feature:hover { transform: translateY(-5px); background-color: var(--accent-color); } .feature-icon { font-size: 24px; margin-bottom: 10px; } .feature-title { font-weight: bold; margin-bottom: 5px; } .button-container { margin-top: 20px; display: flex; gap: 15px; opacity: 0; transform: translateY(20px); transition: opacity 0.6s ease, transform 0.6s ease; } .button { background-color: var(--accent-color); color: var(--text-color); border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; transition: background-color 0.3s ease, transform 0.3s ease; } .button:hover { background-color: var(--secondary-color); transform: translateY(-2px); } .keyboard-mode { position: absolute; top: 10px; right: 10px; font-size: 12px; padding: 5px 10px; background-color: var(--secondary-color); border-radius: 10px; opacity: 0.7; } .ripple { position: absolute; border-radius: 50%; background-color: rgba(255, 255, 255, 0.4); transform: scale(0); animation: ripple 0.6s linear; pointer-events: none; } @keyframes ripple { to { transform: scale(4); opacity: 0; } } .adaptive-background { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; background-image: radial-gradient(circle at 50% 50%, var(--secondary-color) 0%, var(--primary-color) 70%); opacity: 0.5; transition: all 0.5s ease; } @media (max-width: 700px) { .feature-list { gap: 10px; } .feature { width: 120px; height: 120px; padding: 10px; font-size: 12px; } .description { font-size: 14px; padding: 0 20px; } } .watch-band { position: absolute; width: 80px; height: 60px; background-color: #333; border-radius: 10px; z-index: -1; } .watch-band-top { top: -30px; left: 50%; transform: translateX(-50%); } .watch-band-bottom { bottom: -30px; left: 50%; transform: translateX(-50%); } .watch-button { position: absolute; width: 8px; height: 40px; background-color: var(--accent-color); right: -8px; top: 120px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; } .bubble { position: absolute; border-radius: 50%; background-color: rgba(255, 255, 255, 0.03); transform: translate(-50%, -50%); pointer-events: none; } </style> </head> <body> <div class="device-container"> <div class="adaptive-background"></div> <div class="smartwatch"> <div class="watch-band watch-band-top"></div> <div class="watch-band watch-band-bottom"></div> <div class="watch-button"></div> <div class="watch-screen"> <div class="status-bar"> <div class="time">10:42</div> <div class="icons"> <span class="wifi-icon">📶</span> <div class="battery-icon"> <div class="battery-level"></div> </div> </div> </div> <div class="keyboard-mode">Minimal Mode</div> <div class="keyboard-container"> <div class="input-area" id="input-area"> <span id="typed-text"></span><span class="cursor"></span> </div> <div class="keyboard-suggestion"> <div class="suggestion" data-word="Quick">Quick</div> <div class="suggestion" data-word="Reply">Reply</div> <div class="suggestion" data-word="Later">Later</div> </div> <div class="keyboard"> <div class="keyboard-row"> <div class="key" data-key="q">q</div> <div class="key" data-key="w">w</div> <div class="key" data-key="e">e</div> <div class="key" data-key="r">r</div> <div class="key" data-key="t">t</div> <div class="key" data-key="y">y</div> <div class="key" data-key="u">u</div> <div class="key" data-key="i">i</div> <div class="key" data-key="o">o</div> <div class="key" data-key="p">p</div> </div> <div class="keyboard-row"> <div class="key" data-key="a">a</div> <div class="key" data-key="s">s</div> <div class="key" data-key="d">d</div> <div class="key" data-key="f">f</div> <div class="key" data-key="g">g</div> <div class="key" data-key="h">h</div> <div class="key" data-key="j">j</div> <div class="key" data-key="k">k</div> <div class="key" data-key="l">l</div> <div class="key" data-key=".">.</div> </div> <div class="keyboard-row"> <div class="key" data-key="z">z</div> <div class="key" data-key="x">x</div> <div class="key" data-key="c">c</div> <div class="key" data-key="v">v</div> <div class="key" data-key="b">b</div> <div class="key" data-key="n">n</div> <div class="key" data-key="m">m</div> <div class="key" data-key="!">!</div> <div class="key" data-key="?">?</div> <div class="key" data-key="⌫">⌫</div> </div> <div class="keyboard-row"> <div class="key" data-key="123">#</div> <div class="key space" data-key=" ">space</div> <div class="key" data-key="↵">↵</div> </div> </div> </div> </div> </div> <div class="description"> <h2>Minimalist Wearable Keyboard</h2> <p>A sleek, flat layout designed specifically for small wearable displays. Featuring adaptive transparency, subtle visual feedback, and gesture-based interactions for effortless typing on the go.</p> </div> <div class="feature-list"> <div class="feature"> <div class="feature-icon">👆</div> <div class="feature-title">Adaptive Keys</div> <p>Keys respond to context and adjust transparency based on frequency</p> </div> <div class="feature"> <div class="feature-icon">✨</div> <div class="feature-title">Visual Feedback</div> <p>Subtle visual ripples provide tactile-like feedback</p> </div> <div class="feature"> <div class="feature-icon">🔄</div> <div class="feature-title">Context Aware</div> <p>Predictive text adapts to your messaging style</p> </div> </div> <div class="button-container"> <button class="button" id="try-keyboard">Try Different Modes</button> <button class="button" id="reset-keyboard">Reset Demo</button> </div> </div> <script> document.addEventListener('DOMContentLoaded', () => { const keys = document.querySelectorAll('.key'); const suggestions = document.querySelectorAll('.suggestion'); const typedText = document.getElementById('typed-text'); const inputArea = document.getElementById('input-area'); const adaptiveBackground = document.querySelector('.adaptive-background'); const tryKeyboardButton = document.getElementById('try-keyboard'); const resetKeyboardButton = document.getElementById('reset-keyboard'); const batteryLevel = document.querySelector('.battery-level'); const keyboardMode = document.querySelector('.keyboard-mode'); const smartwatch = document.querySelector('.smartwatch'); const deviceContainer = document.querySelector('.device-container'); let currentMode = 'minimal'; const modes = ['minimal', 'dark', 'adaptive', 'gesture']; let lastTypedTime = Date.now(); let currentSentence = ''; // Create random bubbles in the background for (let i = 0; i < 15; i++) { createBubble(); } // Show elements with animation setTimeout(() => { document.querySelector('.description').style.opacity = '1'; document.querySelector('.description').style.transform = 'translateY(0)'; }, 300); setTimeout(() => { document.querySelector('.button-container').style.opacity = '1'; document.querySelector('.button-container').style.transform = 'translateY(0)'; }, 500); document.querySelectorAll('.feature').forEach((feature, index) => { setTimeout(() => { feature.style.opacity = '1'; feature.style.transform = 'translateY(0)'; }, 700 + (index * 100)); }); // Set random battery level setBatteryLevel(); function createBubble() { const bubble = document.createElement('div'); bubble.classList.add('bubble'); const size = Math.random() * 100 + 20; const x = Math.random() * window.innerWidth; const y = Math.random() * window.innerHeight; bubble.style.width = `${size}px`; bubble.style.height = `${size}px`; bubble.style.left = `${x}px`; bubble.style.top = `${y}px`; bubble.style.opacity = Math.random() * 0.1; deviceContainer.appendChild(bubble); // Animate the bubble animateBubble(bubble); } function animateBubble(bubble) { const x = parseFloat(bubble.style.left); const y = parseFloat(bubble.style.top); const newX = x + (Math.random() * 2 - 1); const newY = y + (Math.random() * 2 - 1); bubble.style.left = `${newX}px`; bubble.style.top = `${newY}px`; requestAnimationFrame(() => animateBubble(bubble)); } function setBatteryLevel() { const level = Math.floor(Math.random() * 60 + 40); batteryLevel.style.width = `${level}%`; } function createRipple(event, element) { const ripple = document.createElement('div'); ripple.classList.add('ripple'); const rect = element.getBoundingClientRect(); const size = Math.max(rect.width, rect.height); ripple.style.width = ripple.style.height = `${size}px`; ripple.style.left = `${event.clientX - rect.left - size / 2}px`; ripple.style.top = `${event.clientY - rect.top - size / 2}px`; element.appendChild(ripple); setTimeout(() => { ripple.remove(); }, 600); } function changeMode() { const currentIndex = modes.indexOf(currentMode); const nextIndex = (currentIndex + 1) % modes.length; currentMode = modes[nextIndex]; keyboardMode.textContent = `${currentMode.charAt(0).toUpperCase() + currentMode.slice(1)} Mode`; // Apply different styles based on mode switch(currentMode) { case 'dark': document.documentElement.style.setProperty('--primary-color', '#000000'); document.documentElement.style.setProperty('--secondary-color', '#111111'); document.documentElement.style.setProperty('--accent-color', '#333333'); break; case 'adaptive': document.documentElement.style.setProperty('--primary-color', '#1a1a2e'); document.documentElement.style.setProperty('--secondary-color', '#16213e'); document.documentElement.style.setProperty('--accent-color', '#0f3460'); break; case 'gesture': document.documentElement.style.setProperty('--primary-color', '#2d3436'); document.documentElement.style.setProperty('--secondary-color', '#636e72'); document.documentElement.style.setProperty('--accent-color', '#b2bec3'); break; default: // minimal document.documentElement.style.setProperty('--primary-color', '#1a1a1a'); document.documentElement.style.setProperty('--secondary-color', '#2a2a2a'); document.documentElement.style.setProperty('--accent-color', '#4a4a4a'); } // Animate the change smartwatch.style.transform = 'scale(0.95)'; setTimeout(() => { smartwatch.style.transform = 'scale(1)'; }, 300); } function resetDemo() { typedText.textContent = ''; currentSentence = ''; currentMode = 'minimal'; keyboardMode.textContent = 'Minimal Mode'; document.documentElement.style.setProperty('--primary-color', '#1a1a1a'); document.documentElement.style.setProperty('--secondary-color', '#2a2a2a'); document.documentElement.style.setProperty('--accent-color', '#4a4a4a'); setBatteryLevel(); // Animation for reset smartwatch.style.transform = 'scale(0.9) rotate(5deg)'; setTimeout(() => { smartwatch.style.transform = 'scale(1) rotate(0)'; }, 300); } // Add event listeners to keys keys.forEach(key => { key.addEventListener('click', (e) => { const keyValue = key.getAttribute('data-key'); createRipple(e, key); // Change background gradient position based on clicked key const rect = key.getBoundingClientRect(); const x = rect.left + rect.width / 2; const y = rect.top + rect.height / 2; adaptiveBackground.style.backgroundImage = `radial-gradient(circle at ${x}px ${y}px, var(--secondary-color) 0%, var(--primary-color) 70%)`; // Handle different key types if (keyValue === '⌫') { currentSentence = currentSentence.slice(0, -1); } else if (keyValue === '↵') { currentSentence += '\n'; } else if (keyValue === '123') { // Toggle to numbers (not implemented fully) key.classList.add('active'); setTimeout(() => key.classList.remove('active'), 200); } else { currentSentence += keyValue; } typedText.textContent = currentSentence; lastTypedTime = Date.now(); }); }); // Add event listeners to suggestions suggestions.forEach(suggestion => { suggestion.addEventListener('click', (e) => { const word = suggestion.getAttribute('data-word'); currentSentence += word + ' '; typedText.textContent = currentSentence; createRipple(e, suggestion); }); }); // Mode changing button tryKeyboardButton.addEventListener('click', (e) => { createRipple(e, tryKeyboardButton); changeMode(); }); // Reset button resetKeyboardButton.addEventListener('click', (e) => { createRipple(e, resetKeyboardButton); resetDemo(); }); // Add global keyboard support document.addEventListener('keydown', (e) => { if (e.key === 'Backspace') { const backspaceKey = document.querySelector('.key[data-key="⌫"]'); backspaceKey.click(); e.preventDefault(); } else if (e.key === 'Enter') { const enterKey = document.querySelector('.key[data-key="↵"]'); enterKey.click(); e.preventDefault(); } else if (e.key === ' ') { const spaceKey = document.querySelector('.key.space'); spaceKey.click(); e.preventDefault(); } else if (e.key.length === 1) { const key = document.querySelector(`.key[data-key="${e.key.toLowerCase()}"]`); if (key) { key.click(); e.preventDefault(); } } }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>ShopKey - E-commerce Keyboard</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; } :root { --primary: #4a6cfa; --primary-light: #eef1ff; --secondary: #ff6b6b; --text: #2d3748; --text-light: #718096; --white: #ffffff; --shadow: 0 4px 6px rgba(0, 0, 0, 0.1); --shadow-hover: 0 10px 15px rgba(0, 0, 0, 0.1); --gradient: linear-gradient(135deg, #4a6cfa 0%, #bc61f5 100%); } body { background-color: #f8fafc; height: 100vh; width: 100vw; display: flex; flex-direction: column; align-items: center; justify-content: center; overflow: hidden; padding: 1rem; } .container { max-width: 700px; width: 100%; height: 700px; display: flex; flex-direction: column; position: relative; overflow: hidden; } .app-wrapper { flex: 1; background-color: var(--white); border-radius: 16px; box-shadow: var(--shadow); overflow: hidden; display: flex; flex-direction: column; position: relative; } .app-header { padding: 1.5rem; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #f0f0f0; } .logo { display: flex; align-items: center; gap: 0.5rem; } .logo-icon { width: 32px; height: 32px; background: var(--gradient); border-radius: 8px; display: flex; align-items: center; justify-content: center; color: white; } .logo-text { font-weight: 700; color: var(--text); font-size: 1.25rem; } .user-avatar { width: 36px; height: 36px; border-radius: 50%; background-color: var(--primary-light); display: flex; align-items: center; justify-content: center; color: var(--primary); font-weight: 600; transition: transform 0.3s ease; } .user-avatar:hover { transform: scale(1.05); } .app-content { flex: 1; padding: 1rem; overflow-y: auto; position: relative; } .search-bar { position: relative; margin-bottom: 1.5rem; } .search-input { width: 100%; padding: 0.75rem 1rem 0.75rem 3rem; border-radius: 10px; border: 1px solid #e2e8f0; background-color: #f8fafc; font-size: 1rem; color: var(--text); transition: all 0.3s ease; } .search-input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px rgba(74, 108, 250, 0.1); } .search-icon { position: absolute; left: 1rem; top: 50%; transform: translateY(-50%); color: var(--text-light); } .product-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1rem; margin-bottom: 2rem; } .product-card { background-color: var(--white); border-radius: 12px; overflow: hidden; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); transition: all 0.3s ease; cursor: pointer; } .product-card:hover { transform: translateY(-5px); box-shadow: var(--shadow-hover); } .product-image { width: 100%; height: 150px; background-color: #f8fafc; display: flex; align-items: center; justify-content: center; overflow: hidden; } .product-image img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.5s ease; } .product-card:hover .product-image img { transform: scale(1.05); } .product-details { padding: 1rem; } .product-title { font-weight: 600; color: var(--text); margin-bottom: 0.25rem; font-size: 0.9rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .product-price { font-weight: 700; color: var(--primary); font-size: 1rem; } .keyboard-container { position: absolute; bottom: 0; left: 0; width: 100%; background-color: #e9ecef; border-top-left-radius: 16px; border-top-right-radius: 16px; padding: 1rem; transform: translateY(100%); transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1); z-index: 100; box-shadow: 0 -5px 15px rgba(0, 0, 0, 0.1); } .keyboard-container.active { transform: translateY(0); } .keyboard-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; } .keyboard-title { font-weight: 600; color: var(--text); display: flex; align-items: center; gap: 0.5rem; } .keyboard-logo { font-size: 1.25rem; color: var(--primary); } .keyboard-close { background: none; border: none; color: var(--text-light); font-size: 1.25rem; cursor: pointer; width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; transition: all 0.3s ease; } .keyboard-close:hover { background-color: rgba(0, 0, 0, 0.05); color: var(--text); } .suggestions { display: flex; gap: 0.5rem; margin-bottom: 1rem; overflow-x: auto; padding-bottom: 0.5rem; scrollbar-width: thin; } .suggestions::-webkit-scrollbar { height: 4px; } .suggestions::-webkit-scrollbar-thumb { background-color: rgba(0, 0, 0, 0.2); border-radius: 4px; } .suggestion-chip { background-color: white; border-radius: 20px; padding: 0.5rem 1rem; font-size: 0.85rem; white-space: nowrap; color: var(--text); border: 1px solid #e2e8f0; cursor: pointer; transition: all 0.2s ease; } .suggestion-chip:hover { background-color: var(--primary-light); border-color: var(--primary); color: var(--primary); transform: translateY(-2px); } .keyboard { display: grid; grid-template-columns: repeat(10, 1fr); gap: 0.5rem; } .keyboard-row { display: flex; justify-content: center; gap: 0.5rem; margin-bottom: 0.5rem; width: 100%; } .key { flex: 1; height: 44px; background-color: white; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 1rem; color: var(--text); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); cursor: pointer; user-select: none; transition: all 0.15s ease; position: relative; overflow: hidden; } .key:hover { transform: translateY(-2px); box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); } .key:active { transform: translateY(1px); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); } .key.special { background-color: #f1f5f9; } .key.action { background: var(--gradient); color: white; } .key.half { flex: 0.5; } .key.one-half { flex: 1.5; } .key.double { flex: 2; } .key.triple { flex: 3; } .key.space { flex: 4; } .key .ripple { position: absolute; border-radius: 50%; background-color: rgba(255, 255, 255, 0.5); transform: scale(0); animation: ripple 0.5s linear; } .key.brand-key { background: var(--primary-light); color: var(--primary); font-weight: 600; } .quick-actions { display: flex; gap: 0.5rem; margin: 1rem 0; } .quick-action { flex: 1; background-color: var(--white); border-radius: 8px; padding: 0.75rem; display: flex; flex-direction: column; align-items: center; gap: 0.5rem; transition: all 0.3s ease; cursor: pointer; } .quick-action:hover { transform: translateY(-3px); box-shadow: var(--shadow); } .quick-action-icon { width: 36px; height: 36px; border-radius: 50%; background-color: var(--primary-light); display: flex; align-items: center; justify-content: center; color: var(--primary); font-size: 1.25rem; } .quick-action-text { font-size: 0.75rem; font-weight: 500; color: var(--text); text-align: center; } .tooltip { position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); background-color: rgba(0, 0, 0, 0.8); color: white; padding: 0.5rem 0.75rem; border-radius: 4px; font-size: 0.75rem; white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity 0.2s ease; margin-bottom: 8px; } .tooltip::after { content: ''; position: absolute; top: 100%; left: 50%; transform: translateX(-50%); border-width: 4px; border-style: solid; border-color: rgba(0, 0, 0, 0.8) transparent transparent transparent; } .key:hover .tooltip { opacity: 1; } .search-results { position: absolute; top: calc(100% + 5px); left: 0; width: 100%; background-color: white; border-radius: 8px; box-shadow: var(--shadow); z-index: 5; max-height: 300px; overflow-y: auto; opacity: 0; transform: translateY(-10px); pointer-events: none; transition: all 0.3s ease; } .search-results.active { opacity: 1; transform: translateY(0); pointer-events: auto; } .result-item { padding: 0.75rem 1rem; display: flex; align-items: center; gap: 1rem; cursor: pointer; border-bottom: 1px solid #f0f0f0; transition: background-color 0.2s ease; } .result-item:last-child { border-bottom: none; } .result-item:hover { background-color: #f8fafc; } .result-image { width: 40px; height: 40px; border-radius: 6px; background-color: #f1f5f9; overflow: hidden; } .result-image img { width: 100%; height: 100%; object-fit: cover; } .result-info { flex: 1; } .result-title { font-weight: 500; color: var(--text); margin-bottom: 0.25rem; font-size: 0.9rem; } .result-price { font-weight: 600; color: var(--primary); font-size: 0.85rem; } .result-badge { font-size: 0.7rem; padding: 0.2rem 0.5rem; border-radius: 4px; background-color: var(--primary-light); color: var(--primary); margin-left: 0.5rem; } @keyframes ripple { to { transform: scale(4); opacity: 0; } } @media (max-width: 480px) { .product-grid { grid-template-columns: 1fr; } .key { height: 38px; font-size: 0.9rem; } .quick-actions { flex-wrap: wrap; } .quick-action { min-width: 60px; } } @media (max-height: 700px) { .keyboard-container { padding: 0.75rem; } .keyboard-row { margin-bottom: 0.4rem; gap: 0.4rem; } .key { height: 38px; } } </style> </head> <body> <div class="container"> <div class="app-wrapper"> <div class="app-header"> <div class="logo"> <div class="logo-icon"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"> <path d="M8 1a2.5 2.5 0 0 1 2.5 2.5V4h-5v-.5A2.5 2.5 0 0 1 8 1zm3.5 3v-.5a3.5 3.5 0 1 0-7 0V4H1v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V4h-3.5z"/> </svg> </div> <div class="logo-text">ShopKey</div> </div> <div class="user-avatar">AK</div> </div> <div class="app-content"> <div class="search-bar"> <input type="text" class="search-input" placeholder="Search for products..." id="search-input"> <div class="search-icon"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"> <path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"/> </svg> </div> <div class="search-results"> <div class="result-item"> <div class="result-image"> <img src="https://images.unsplash.com/photo-1542291026-7eec264c27ff?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=100&q=80" alt="Red Sneakers"> </div> <div class="result-info"> <div class="result-title">Nike Air Max Pulse</div> <div class="result-price">$129.99 <span class="result-badge">Free Shipping</span></div> </div> </div> <div class="result-item"> <div class="result-image"> <img src="https://images.unsplash.com/photo-1606107557195-0e29a4b5b4aa?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=100&q=80" alt="Green Sneakers"> </div> <div class="result-info"> <div class="result-title">Nike Air Zoom Pegasus</div> <div class="result-price">$89.99</div> </div> </div> <div class="result-item"> <div class="result-image"> <img src="https://images.unsplash.com/photo-1539185441755-769473a23570?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=100&q=80" alt="Casual Sneakers"> </div> <div class="result-info"> <div class="result-title">Nike Revolution 6</div> <div class="result-price">$65.99 <span class="result-badge">Sale</span></div> </div> </div> </div> </div> <div class="quick-actions"> <div class="quick-action"> <div class="quick-action-icon"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"> <path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/> <path d="M10.97 4.97a.235.235 0 0 0-.02.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-1.071-1.05z"/> </svg> </div> <div class="quick-action-text">Best Sellers</div> </div> <div class="quick-action"> <div class="quick-action-icon"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"> <path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zM4.5 7.5a.5.5 0 0 1 0-1h7a.5.5 0 0 1 0 1h-7z"/> </svg> </div> <div class="quick-action-text">Deals</div> </div> <div class="quick-action"> <div class="quick-action-icon"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"> <path d="M8 1a2 2 0 0 1 2 2v2H6V3a2 2 0 0 1 2-2zm3 4V3a3 3 0 1 0-6 0v2H3.36a1.5 1.5 0 0 0-1.483 1.277L.85 13.13A2.5 2.5 0 0 0 3.322 16h9.355a2.5 2.5 0 0 0 2.473-2.87l-1.028-6.853A1.5 1.5 0 0 0 12.64 5H11z"/> </svg> </div> <div class="quick-action-text">New Arrivals</div> </div> <div class="quick-action"> <div class="quick-action-icon"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"> <path d="M13.902.334a.5.5 0 0 1-.28.65l-2.254.902-.4 1.927c.376.095.715.215.972.367.228.135.56.396.56.82 0 .046-.004.09-.011.132l-.962 9.068a1.28 1.28 0 0 1-.524.93c-.488.34-1.494.87-3.01.87-1.516 0-2.522-.53-3.01-.87a1.28 1.28 0 0 1-.524-.93L3.51 5.132A.78.78 0 0 1 3.5 5c0-.424.332-.685.56-.82.262-.154.607-.276.99-.372l-.399-1.93L2.4.985A.5.5 0 1 1 2.97.335l2.354.952 1.674-1.73a.5.5 0 0 1 .722 0l1.674 1.73 2.355-.952a.5.5 0 0 1 .58.017zM4.46 11.234 4.5 5.5c0-.245.255-.5.75-.5s.75.255.75.5c0 .245-.255.5-.75.5s-.75-.255-.75-.5L5.5 11c0 .245-.255.5-.75.5s-.75-.255-.75-.5 .255-.5.75-.5.75.255.75.5z"/> </svg> </div> <div class="quick-action-text">Trending</div> </div> </div> <h3 style="margin-bottom: 1rem; color: var(--text); font-size: 1.1rem;">Popular Now</h3> <div class="product-grid"> <div class="product-card"> <div class="product-image"> <img src="https://images.unsplash.com/photo-1600185365926-3a2ce3cdb9eb?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=300&q=80" alt="Wireless Headphones"> </div> <div class="product-details"> <div class="product-title">Sony WH-1000XM5</div> <div class="product-price">$349.99</div> </div> </div> <div class="product-card"> <div class="product-image"> <img src="https://images.unsplash.com/photo-1526170375885-4d8ecf77b99f?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=300&q=80" alt="Polaroid Camera"> </div> <div class="product-details"> <div class="product-title">Polaroid Now Instant Camera</div> <div class="product-price">$99.99</div> </div> </div> <div class="product-card"> <div class="product-image"> <img src="https://images.unsplash.com/photo-1546868871-7041f2a55e12?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=300&q=80" alt="Smart Watch"> </div> <div class="product-details"> <div class="product-title">Apple Watch Series 8</div> <div class="product-price">$399.99</div> </div> </div> <div class="product-card"> <div class="product-image"> <img src="https://images.unsplash.com/photo-1505740420928-5e560c06d30e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=300&q=80" alt="Wireless Earbuds"> </div> <div class="product-details"> <div class="product-title">AirPods Pro (2nd gen)</div> <div class="product-price">$249.99</div> </div> </div> </div> </div> <div class="keyboard-container"> <div class="keyboard-header"> <div class="keyboard-title"> <span class="keyboard-logo"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"> <path d="M14 5a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h12zM2 4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2H2z"/> <path d="M13 10.25a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5a.25.25 0 0 1-.25-.25v-.5zm0-2a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5a.25.25 0 0 1-.25-.25v-.5zm-5 0A.25.25 0 0 1 8.25 8h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 8 8.75v-.5zm0 2a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5a.25.25 0 0 1-.25-.25v-.5zm-5-2A.25.25 0 0 1 3.25 8h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 3 8.75v-.5zm0 2a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 3 10.75v-.5zm-1-4a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 2 6.75v-.5zm0 2a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 2 8.75v-.5zm5 0a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 7 8.75v-.5zm5 0a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 12 8.75v-.5zm-10 0a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 2 8.75v-.5zm0 2a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 2 10.75v-.5zm5 0a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 7 10.75v-.5zm5 0a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 12 10.75v-.5zm-10-2a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 2 8.75v-.5zm5 0a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 7 8.75v-.5zm5 0a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 12 8.75v-.5zm0-2a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 12 6.75v-.5zm-10 0a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 2 6.75v-.5zm0 2a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 2 8.75v-.5zm5-2a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 7 6.75v-.5zm-5 4a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 2 10.75v-.5zm5 0a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 7 10.75v-.5zm5 0a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5A.25.25 0 0 1 12 10.75v-.5zm5-2a.25.25 0 0 1 .25-.25h.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-.5a.25.25 0 0 1-.25-.25v-.5z"/> </svg> </span> <span>ShopKey Keyboard</span> </div> <button class="keyboard-close" id="keyboard-close"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>LinguaKeys - Interactive Language Learning Keyboard</title> <style> :root { --primary: #4255ff; --secondary: #ff914d; --correct: #42d392; --incorrect: #ff5252; --neutral: #f7f9fc; --text-dark: #2d3748; --text-light: #ffffff; --accent-1: #9c6ade; --accent-2: #ffcf5c; --border-radius: 12px; --shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.08); --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Nunito', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } body { background-color: var(--neutral); display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; color: var(--text-dark); padding: 16px; overflow-x: hidden; } .app-container { max-width: 700px; width: 100%; background: white; border-radius: var(--border-radius); box-shadow: var(--shadow); display: flex; flex-direction: column; height: 650px; position: relative; overflow: hidden; } .header { padding: 16px 20px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid rgba(0,0,0,0.06); background: linear-gradient(135deg, var(--primary), var(--accent-1)); color: var(--text-light); } .logo { font-weight: 800; font-size: 20px; display: flex; align-items: center; } .logo svg { margin-right: 8px; } .language-select { background: rgba(255, 255, 255, 0.2); border: none; color: white; padding: 8px 12px; border-radius: 20px; font-weight: 600; cursor: pointer; transition: var(--transition); } .language-select:hover { background: rgba(255, 255, 255, 0.3); } .content { flex: 1; padding: 24px; display: flex; flex-direction: column; overflow-y: auto; } .progress-bar { height: 8px; background: #e2e8f0; border-radius: 4px; margin-bottom: 20px; overflow: hidden; } .progress-fill { height: 100%; background: linear-gradient(to right, var(--accent-2), var(--secondary)); width: 35%; border-radius: 4px; transition: width 0.5s ease; } .challenge { text-align: center; margin-bottom: 24px; } .challenge h2 { font-size: 18px; color: var(--text-dark); margin-bottom: 8px; } .word-to-type { font-size: 32px; font-weight: 700; color: var(--primary); margin-bottom: 16px; display: flex; justify-content: center; align-items: center; min-height: 60px; } .word-hint { font-size: 16px; color: #718096; font-style: italic; margin-bottom: 24px; } .input-area { position: relative; margin-bottom: 32px; } .typing-input { width: 100%; font-size: 20px; padding: 16px; border: 2px solid #e2e8f0; border-radius: var(--border-radius); outline: none; transition: var(--transition); background-color: white; text-align: center; } .typing-input:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(66, 85, 255, 0.2); } .feedback { position: absolute; left: 0; right: 0; bottom: -30px; text-align: center; font-weight: 600; opacity: 0; transform: translateY(-10px); transition: var(--transition); } .feedback.visible { opacity: 1; transform: translateY(0); } .feedback.correct { color: var(--correct); } .feedback.incorrect { color: var(--incorrect); } .keyboard { display: flex; flex-direction: column; gap: 8px; margin-top: auto; user-select: none; } .keyboard-row { display: flex; justify-content: center; gap: 6px; } .key { min-width: 38px; height: 48px; display: flex; align-items: center; justify-content: center; font-size: 18px; background-color: white; border: 1.5px solid #e2e8f0; border-radius: 8px; cursor: pointer; position: relative; overflow: hidden; box-shadow: 0 2px 3px rgba(0,0,0,0.05); transition: var(--transition); } .key:hover { background-color: #f7f9fc; transform: translateY(-2px); box-shadow: 0 4px 6px rgba(0,0,0,0.1); } .key.active { transform: translateY(1px); box-shadow: 0 1px 2px rgba(0,0,0,0.1); background-color: #edf2f7; } .key.highlighted { background-color: var(--accent-2); border-color: var(--accent-2); color: var(--text-dark); animation: pulse 2s infinite; } .key.correct-key { background-color: var(--correct); border-color: var(--correct); color: white; } .key.incorrect-key { background-color: var(--incorrect); border-color: var(--incorrect); color: white; } .key.accent-key { color: var(--primary); font-weight: 700; } .key-special { min-width: 65px; } .key-space { min-width: 180px; } .key-visual-hint { position: absolute; top: -24px; left: 50%; transform: translateX(-50%); background: #2d3748; color: white; padding: 4px 8px; border-radius: 4px; font-size: 12px; white-space: nowrap; opacity: 0; pointer-events: none; transition: var(--transition); } .key:hover .key-visual-hint { opacity: 1; top: -30px; } .ripple { position: absolute; border-radius: 50%; background-color: rgba(255, 255, 255, 0.7); transform: scale(0); animation: ripple 0.6s linear; } .key-popup { position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); background: var(--primary); color: white; padding: 6px 12px; border-radius: var(--border-radius); font-weight: 700; opacity: 0; transition: var(--transition); pointer-events: none; } .help-btn { position: absolute; top: 20px; right: 20px; background: rgba(255, 255, 255, 0.2); border: none; color: white; width: 32px; height: 32px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 700; cursor: pointer; transition: var(--transition); } .help-btn:hover { background: rgba(255, 255, 255, 0.3); } .tooltip { position: absolute; background: white; box-shadow: var(--shadow); border-radius: var(--border-radius); padding: 12px; width: 250px; top: 60px; right: 20px; opacity: 0; transform: translateY(-10px); pointer-events: none; transition: var(--transition); z-index: 10; } .tooltip.visible { opacity: 1; transform: translateY(0); pointer-events: auto; } .tooltip h3 { margin-bottom: 8px; color: var(--primary); } .tooltip p { margin-bottom: 8px; font-size: 14px; } .streak-counter { position: absolute; top: 20px; left: 24px; display: flex; align-items: center; font-weight: 600; } .streak-counter .flame { color: var(--secondary); margin-right: 6px; } @keyframes ripple { to { transform: scale(4); opacity: 0; } } @keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(255, 207, 92, 0.5); } 70% { box-shadow: 0 0 0 10px rgba(255, 207, 92, 0); } 100% { box-shadow: 0 0 0 0 rgba(255, 207, 92, 0); } } @keyframes bounce { 0%, 20%, 50%, 80%, 100% { transform: translateY(0); } 40% { transform: translateY(-20px); } 60% { transform: translateY(-10px); } } @keyframes fadeInUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .character { display: inline-block; transition: var(--transition); } .character.correct { color: var(--correct); } .character.incorrect { color: var(--incorrect); } .character.current { text-decoration: underline; animation: pulse 2s infinite; } .accent-key { position: relative; } .accent-popup { position: absolute; display: flex; bottom: 100%; left: 50%; transform: translateX(-50%); background: white; border: 1px solid #e2e8f0; border-radius: var(--border-radius); padding: 8px; gap: 4px; box-shadow: var(--shadow); opacity: 0; pointer-events: none; transition: var(--transition); z-index: 5; } .accent-popup.visible { opacity: 1; pointer-events: auto; bottom: calc(100% + 5px); } .accent-option { padding: 6px 8px; cursor: pointer; border-radius: 4px; transition: var(--transition); } .accent-option:hover { background: #f7f9fc; } /* Screen reader only */ .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0; } @media (max-width: 480px) { .app-container { height: 600px; } .key { min-width: 28px; height: 40px; font-size: 14px; } .key-special { min-width: 50px; } .key-space { min-width: 120px; } .word-to-type { font-size: 28px; } } </style> </head> <body> <div class="app-container"> <div class="header"> <div class="logo"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M4 6H20M4 12H20M4 18H20" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> LinguaKeys </div> <select class="language-select" id="language-select"> <option value="fr">French 🇫🇷</option> <option value="es">Spanish 🇪🇸</option> <option value="de">German 🇩🇪</option> <option value="it">Italian 🇮🇹</option> <option value="jp">Japanese 🇯🇵</option> </select> <button class="help-btn" id="help-btn" aria-label="Help">?</button> </div> <div class="content"> <div class="progress-bar"> <div class="progress-fill"></div> </div> <div class="challenge"> <h2>Type the French word for:</h2> <div class="word-to-type" id="word-to-type">café</div> <div class="word-hint">Hint: A place where people go to drink coffee</div> </div> <div class="input-area"> <input type="text" class="typing-input" id="typing-input" autocomplete="off" placeholder="Type here..."> <div class="feedback" id="feedback"></div> </div> <div class="keyboard" id="keyboard"> <div class="keyboard-row"> <div class="key" data-key="q">q</div> <div class="key" data-key="w">w</div> <div class="key" data-key="e">e <div class="key-visual-hint">é, è, ê, ë</div> </div> <div class="key" data-key="r">r</div> <div class="key" data-key="t">t</div> <div class="key" data-key="y">y</div> <div class="key" data-key="u">u <div class="key-visual-hint">ú, ù, û, ü</div> </div> <div class="key" data-key="i">i <div class="key-visual-hint">í, ì, î, ï</div> </div> <div class="key" data-key="o">o <div class="key-visual-hint">ó, ò, ô, ö</div> </div> <div class="key" data-key="p">p</div> </div> <div class="keyboard-row"> <div class="key" data-key="a">a <div class="key-visual-hint">á, à, â, ä</div> </div> <div class="key" data-key="s">s</div> <div class="key" data-key="d">d</div> <div class="key" data-key="f">f</div> <div class="key" data-key="g">g</div> <div class="key" data-key="h">h</div> <div class="key" data-key="j">j</div> <div class="key" data-key="k">k</div> <div class="key" data-key="l">l</div> </div> <div class="keyboard-row"> <div class="key key-special" data-key="shift">shift</div> <div class="key" data-key="z">z</div> <div class="key" data-key="x">x</div> <div class="key" data-key="c">c <div class="key-visual-hint">ç</div> </div> <div class="key" data-key="v">v</div> <div class="key" data-key="b">b</div> <div class="key" data-key="n">n <div class="key-visual-hint">ñ</div> </div> <div class="key" data-key="m">m</div> <div class="key key-special" data-key="backspace"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M21 4H8l-7 8 7 8h13a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2z"></path> <line x1="18" y1="9" x2="12" y2="15"></line> <line x1="12" y1="9" x2="18" y2="15"></line> </svg> </div> </div> <div class="keyboard-row"> <div class="key accent-key" data-key="accent"> <span>á, é, ñ</span> </div> <div class="key key-space" data-key=" ">space</div> <div class="key key-special" data-key="enter">enter</div> </div> </div> </div> <div class="tooltip" id="tooltip"> <h3>How to Use LinguaKeys</h3> <p>1. Type the word shown at the top</p> <p>2. For accent characters, click the accent key or hold down a letter</p> <p>3. Get immediate feedback on your typing</p> <p>4. Use the hints when you need help</p> <p>5. The highlighted keys show suggested next letters</p> </div> <div id="accent-popup" class="accent-popup"> <!-- Accent options will be added dynamically --> </div> </div> <script> document.addEventListener('DOMContentLoaded', () => { const typingInput = document.getElementById('typing-input'); const wordToType = document.getElementById('word-to-type'); const keyboard = document.getElementById('keyboard'); const feedback = document.getElementById('feedback'); const progressFill = document.querySelector('.progress-fill'); const helpBtn = document.getElementById('help-btn'); const tooltip = document.getElementById('tooltip'); const accentKey = document.querySelector('.accent-key'); const accentPopup = document.getElementById('accent-popup'); const languageSelect = document.getElementById('language-select'); // Current challenge word and state let currentWord = 'café'; let correctLetterCount = 0; let streak = 0; let lastKeyPressed = null; let accentKeyActive = false; // Language-specific character sets const accentMap = { 'fr': { 'a': ['à', 'â', 'æ'], 'c': ['ç'], 'e': ['é', 'è', 'ê', 'ë'], 'i': ['î', 'ï'], 'o': ['ô', 'œ'], 'u': ['ù', 'û', 'ü'], }, 'es': { 'a': ['á'], 'e': ['é'], 'i': ['í'], 'n': ['ñ'], 'o': ['ó'], 'u': ['ú', 'ü'], '?': ['¿'], '!': ['¡'], }, 'de': { 'a': ['ä'], 'o': ['ö'], 's': ['ß'], 'u': ['ü'], }, 'it': { 'a': ['à'], 'e': ['è', 'é'], 'i': ['ì', 'í'], 'o': ['ò', 'ó'], 'u': ['ù', 'ú'], }, 'jp': { // Simplified for demo purposes 'a': ['あ', 'ア'], 'i': ['い', 'イ'], 'u': ['う', 'ウ'], 'e': ['え', 'エ'], 'o': ['お', 'オ'], } }; // Word challenges by language const challenges = { 'fr': [ { word: 'café', hint: 'A place where people go to drink coffee', translation: 'coffee shop' }, { word: 'bonjour', hint: 'A common greeting', translation: 'hello/good day' }, { word: 'merci', hint: 'What you say after receiving something', translation: 'thank you' }, { word: 'baguette', hint: 'A long French bread', translation: 'baguette' }, { word: 'château', hint: 'A large French country house or castle', translation: 'castle' } ], 'es': [ { word: 'hola', hint: 'A common greeting', translation: 'hello' }, { word: 'gracias', hint: 'What you say after receiving something', translation: 'thank you' }, { word: 'mañana', hint: 'The early part of the day', translation: 'morning/tomorrow' }, { word: 'España', hint: 'A country in Europe', translation: 'Spain' }, { word: '¿cómo estás?', hint: 'Asking someone how they are', translation: 'how are you?' } ], 'de': [ { word: 'Hallo', hint: 'A common greeting', translation: 'hello' }, { word: 'Danke', hint: 'What you say after receiving something', translation: 'thank you' }, { word: 'Straße', hint: 'A road in a city or village', translation: 'street' }, { word: 'Schön', hint: 'Something that looks good', translation: 'beautiful' }, { word: 'Tschüss', hint: 'What you say when leaving', translation: 'bye' } ], 'it': [ { word: 'ciao', hint: 'A common greeting or farewell', translation: 'hello/goodbye' }, { word: 'grazie', hint: 'What you say after receiving something', translation: 'thank you' }, { word: 'pizza', hint: 'A popular Italian food', translation: 'pizza' }, { word: 'caffè', hint: 'A popular beverage', translation: 'coffee' }, { word: 'amore', hint: 'A strong feeling of affection', translation: 'love' } ], 'jp': [ { word: 'こんにちは', hint: 'A common greeting', translation: 'hello' }, { word: 'ありがとう', hint: 'What you say after receiving something', translation: 'thank you' }, { word: '寿司', hint: 'A popular Japanese food with rice and fish', translation: 'sushi' }, { word: '東京', hint: 'The capital city of Japan', translation: 'Tokyo' }, { word: 'さようなら', hint: 'What you say when leaving', translation: 'goodbye' } ] }; // Initialize the challenge function initChallenge() { const language = languageSelect.value; const randomIndex = Math.floor(Math.random() * challenges[language].length); const challenge = challenges[language][randomIndex]; currentWord = challenge.word; document.querySelector('.word-hint').textContent = `Hint: ${challenge.hint}`; document.querySelector('h2').textContent = `Type the ${getLanguageName(language)} word for: ${challenge.translation}`; // Create character spans for the word wordToType.innerHTML = ''; [...currentWord].forEach((char, index) => { const span = document.createElement('span'); span.className = 'character'; span.textContent = char; if (index === 0) span.classList.add('current'); wordToType.appendChild(span); }); correctLetterCount = 0; typingInput.value = ''; typingInput.focus(); updateKeyHighlights(); } function getLanguageName(code) { const names = { 'fr': 'French', 'es': 'Spanish', 'de': 'German', 'it': 'Italian', 'jp': 'Japanese' }; return names[code] || code; } // Handle key clicks on virtual keyboard keyboard.addEventListener('click', (e) => { const key = e.target.closest('.key'); if (!key) return; const keyValue = key.getAttribute('data-key'); if (keyValue === 'backspace') { typingInput.value = typingInput.value.slice(0, -1); checkInput(); } else if (keyValue === 'enter') { if (typingInput.value === currentWord) { showSuccess(); } else { showError(); } } else if (keyValue === 'shift') { // Toggle shift state keyboard.classList.toggle('shift-active'); toggleKeyCaps(); } else if (keyValue === 'accent') { toggleAccentPopup(); } else { // Add ripple effect createRipple(e, key); // Add the key value to the input typingInput.value += keyValue; checkInput(); } // Animate the key press key.classList.add('active'); setTimeout(() => { key.classList.remove('active'); }, 100); typingInput.focus(); }); // Toggle capitalization of keys when shift is pressed function toggleKeyCaps() { const isShiftActive = keyboard.classList.contains('shift-active'); document.querySelectorAll('.key:not(.key-special)').forEach(key => { const keyValue = key.getAttribute('data-key'); if (keyValue && keyValue.length === 1) { key.textContent = isShiftActive ? keyValue.toUpperCase() : keyValue.toLowerCase(); } }); } // Create ripple effect on key press function createRipple(event, element) { const rect = element.getBoundingClientRect(); const x = event.clientX - rect.left; const y = event.clientY - rect.top; const ripple = document.createElement('span'); ripple.className = 'ripple'; ripple.style.left = `${x}px`; ripple.style.top = `${y}px`; element.appendChild(ripple); setTimeout(() => { ripple.remove(); }, 600); } // Show accent popup for the given key function showAccentOptions(key) { const language = languageSelect.value; const letter = key.getAttribute('data-key'); if (accentMap[language] && accentMap[language][letter]) { accentPopup.innerHTML = ''; accentMap[language][letter].forEach(accent => { const option = document.createElement('div'); option.className = 'accent-option'; option.textContent = accent; option.addEventListener('click', () => { typingInput.value += accent; checkInput(); hideAccentPopup(); typingInput.focus(); }); accentPopup.appendChild(option); }); // Position the popup const keyRect = key.getBoundingClientRect(); const keyboardRect = keyboard.getBoundingClientRect(); accentPopup.style.left = `${keyRect.left - keyboardRect.left + keyRect.width/2}px`; accentPopup.style.bottom = `${keyboardRect.bottom - keyRect.top + 10}px`; accentPopup.classList.add('visible'); } } // Toggle accent popup when accent key is clicked function toggleAccentPopup() { if (accentKeyActive) { hideAccentPopup(); } else { accentKeyActive = true; accentKey.classList.add('active'); // Show all accent characters const language = languageSelect.value; accentPopup.innerHTML = ''; // Get all accent characters for the current language let allAccents = []; Object.values(accentMap[language] || {}).forEach(accents => { allAccents = [...allAccents, ...accents]; }); allAccents.forEach(accent => { const option = document.createElement('div'); option.className = 'accent-option'; option.textContent = accent; option.addEventListener('click', () => { typingInput.value += accent; checkInput(); hideAccentPopup(); typingInput.focus(); }); accentPopup.appendChild(option); }); // Position the popup const keyRect = accentKey.getBoundingClientRect(); const keyboardRect = keyboard.getBoundingClientRect(); accentPopup.style.left = `${keyRect.left - keyboardRect.left + keyRect.width/2}px`; accentPopup.classList.add('visible'); } } // Hide accent popup function hideAccentPopup() { accentPopup.classList.remove('visible'); accentKeyActive = false; accentKey.classList.remove('active'); } // Check input against target word function checkInput() { const input = typingInput.value; const chars = document.querySelectorAll('.character'); // Reset all character styling chars.forEach(char => { char.classList.remove('current', 'correct', 'incorrect'); }); // Update progress for (let i = 0; i < input.length && i < currentWord.length; i++) { const isCorrect = input[i] === currentWord[i]; chars[i].classList.add(isCorrect ? 'correct' : 'incorrect'); } // Highlight current character if (input.length < currentWord.length) { chars[input.length].classList.add('current'); } // Check if input exactly matches the word so far if (input === currentWord.substring(0, input.length)) { correctLetterCount = input.length; updateKeyHighlights(); // If the full word is correct if (input === currentWord) { showSuccess(); } } else { // Show error feedback for mistyped character showCharacterError(); } } // Highlight keys that are likely to be typed next function updateKeyHighlights() { // Reset all key highlights document.querySelectorAll('.key').forEach(key => { key.classList.remove('highlighted', 'correct-key', 'incorrect-key'); }); // If we have more letters to type, highlight the next key if (correctLetterCount < currentWord.length) { const nextLetter = currentWord[correctLetterCount].toLowerCase(); const key = document.querySelector(`.key[data-key="${nextLetter}"]`); if (key) { key.classList.add('highlighted'); } else { // If it's a special character or accent, highlight the accent key document.querySelector('.key[data-key="accent"]').classList.add('highlighted'); } } } // Show success feedback function showSuccess() { feedback.textContent = "Perfect! Well done!"; feedback.className = "feedback visible correct"; // Update streak and progress streak++; progressFill.style.width = `${Math.min(streak * 10, 100)}%`; // Celebrate with animation on the word document.querySelectorAll('.character').forEach((char, i) => { setTimeout(() => { char.style.animation = 'bounce 0.8s'; setTimeout(() => { char.style.animation = ''; }, 800); }, i * 100); }); // Show next word after delay setTimeout(() => { feedback.className = "feedback"; initChallenge(); }, 1500); } // Show generic error feedback function showError() { feedback.textContent = "Not quite right. Try again!"; feedback.className = "feedback visible incorrect"; // Reset streak streak = 0; progressFill.style.width = "0%"; setTimeout(() => { feedback.className = "feedback"; }, 2000); } // Show error feedback for individual character function showCharacterError() { const input = typingInput.value; const lastIndex = input.length - 1; if (lastIndex >= 0 && input[lastIndex] !== currentWord[lastIndex]) { // Highlight the key that was incorrectly pressed const wrongKey = document.querySelector(`.key[data-key="${input[lastIndex].toLowerCase()}"]`); if (wrongKey) { wrongKey.classList.add('incorrect-key'); setTimeout(() => { wrongKey.classList.remove('incorrect-key'); updateKeyHighlights(); }, 500); } // Show a brief error message feedback.textContent = "Oops! Try again."; feedback.className = "feedback visible incorrect"; setTimeout(() => { feedback.className = "feedback"; }, 1000); } } // Toggle help tooltip helpBtn.addEventListener('click', () => { tooltip.classList.toggle('visible'); }); // Close tooltip when clicking outside document.addEventListener('click', (e) => { if (!tooltip.contains(e.target) && e.target !== helpBtn) { tooltip.classList.remove('visible'); } // Also close accent popup when clicking outside if (!accentPopup.contains(e.target) && e.target !== accentKey && !e.target.closest('.accent-key')) { hideAccentPopup(); } }); // Handle input changes typingInput.addEventListener('input', checkInput); // Handle language changes languageSelect.addEventListener('change', () => { initChallenge(); }); // Handle physical keyboard input document.addEventListener('keydown', (e) => { // Don't process keyboard shortcuts if (e.ctrlKey || e.metaKey) return; if (e.key === 'Escape') { tooltip.classList.remove('visible'); hideAccentPopup(); return; } // Find and animate the corresponding key let keySelector = `.key[data-key="${e.key.toLowerCase()}"]`; if (