Speech bubbles are a versatile design element that can add personality and clarity to your user interface. Whether you're creating a chat app or an interactive tutorial, the right speech bubble can make all the difference.
In this article, we'll explore 10 speech bubble examples that showcase a range of styles and functionalities. From classic comic book designs to modern, minimalist approaches, these examples will inspire your next project.
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 UI game with Subframe's drag-and-drop interface. Its intuitive, responsive canvas ensures pixel-perfect designs every time, making it a favorite among professionals.
Ready to create stunning speech bubbles effortlessly? 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
Unlock the power of Subframe to design pixel-perfect UIs, including stunning speech bubbles, with unmatched efficiency. Its drag-and-drop interface makes creating beautiful designs a breeze.
Ready to elevate your UI game? Start for free and begin creating immediately!
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Bubble Chat</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; } body { background-color: #f6f8fc; display: flex; justify-content: center; align-items: center; height: 100vh; width: 100%; overflow: hidden; } .chat-container { width: 100%; max-width: 700px; height: 700px; background-color: #fff; border-radius: 18px; box-shadow: 0 12px 28px rgba(0, 0, 0, 0.1); display: flex; flex-direction: column; overflow: hidden; position: relative; } .chat-header { padding: 20px; background: linear-gradient(135deg, #6e8efb, #a777e3); color: white; display: flex; align-items: center; justify-content: space-between; z-index: 10; } .header-left { display: flex; align-items: center; } .avatar { width: 40px; height: 40px; border-radius: 50%; margin-right: 12px; object-fit: cover; border: 2px solid rgba(255, 255, 255, 0.5); } .user-info h2 { font-size: 18px; font-weight: 600; } .user-info p { font-size: 13px; opacity: 0.8; } .header-actions { display: flex; gap: 16px; } .header-actions i { font-size: 22px; cursor: pointer; transition: transform 0.2s ease; } .header-actions i:hover { transform: scale(1.1); } .chat-body { flex: 1; padding: 20px; overflow-y: auto; background-color: #f6f8fc; background-image: radial-gradient(circle at 25% 25%, rgba(110, 142, 251, 0.05) 2%, transparent 5%), radial-gradient(circle at 75% 75%, rgba(167, 119, 227, 0.05) 2%, transparent 5%); background-size: 60px 60px; } .message { display: flex; margin-bottom: 16px; transition: all 0.3s ease; opacity: 0; transform: translateY(20px); } .message.visible { opacity: 1; transform: translateY(0); } .message.incoming { justify-content: flex-start; } .message.outgoing { justify-content: flex-end; } .message-avatar { width: 36px; height: 36px; border-radius: 50%; margin-right: 12px; align-self: flex-end; } .bubble { padding: 14px 18px; border-radius: 20px; max-width: 65%; position: relative; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.06); transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); } .bubble:hover { transform: scale(1.02); } .bubble p { margin: 0; line-height: 1.5; font-size: 15px; } .bubble .time { font-size: 11px; margin-top: 6px; opacity: 0.7; display: flex; align-items: center; justify-content: flex-end; } .time i { font-size: 14px; margin-left: 4px; } /* Incoming message styling */ .incoming .bubble { background: linear-gradient(135deg, #6e8efb, #6e8efb); color: white; border-bottom-left-radius: 4px; } /* Outgoing message styling */ .outgoing .bubble { background: linear-gradient(135deg, #a777e3, #a777e3); color: white; border-bottom-right-radius: 4px; } /* Friend 1 styling */ .friend-1 .bubble { background: linear-gradient(135deg, #13c2c2, #36cfc9); color: white; border-bottom-left-radius: 4px; } /* Friend 2 styling */ .friend-2 .bubble { background: linear-gradient(135deg, #ff7a45, #fa8c16); color: white; border-bottom-left-radius: 4px; } .group-divider { text-align: center; margin: 20px 0; position: relative; opacity: 0; transform: translateY(20px); transition: all 0.3s ease; } .group-divider.visible { opacity: 1; transform: translateY(0); } .group-divider span { background-color: rgba(0, 0, 0, 0.1); color: #555; padding: 5px 12px; border-radius: 15px; font-size: 12px; font-weight: 500; } .chat-input { padding: 15px 20px; background-color: white; display: flex; align-items: center; border-top: 1px solid rgba(0, 0, 0, 0.05); z-index: 2; } .input-actions { display: flex; gap: 14px; color: #6e8efb; } .input-actions i { font-size: 22px; cursor: pointer; transition: all 0.2s ease; } .input-actions i:hover { transform: scale(1.1); color: #a777e3; } .message-input { flex: 1; margin: 0 15px; padding: 12px 18px; border: none; background-color: #f6f8fc; border-radius: 24px; font-size: 15px; transition: all 0.2s ease; } .message-input:focus { outline: none; box-shadow: 0 0 0 2px rgba(110, 142, 251, 0.3); background-color: #f0f4ff; } .send-button { background: linear-gradient(135deg, #6e8efb, #a777e3); color: white; border: none; width: 42px; height: 42px; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 2px 8px rgba(110, 142, 251, 0.4); transition: all 0.2s ease; } .send-button:hover { transform: scale(1.05); box-shadow: 0 4px 12px rgba(110, 142, 251, 0.5); } .send-button i { font-size: 18px; } /* Typing indicator */ .typing-indicator { display: flex; align-items: center; margin-top: 8px; margin-bottom: 16px; opacity: 0; transform: translateY(10px); transition: all 0.3s ease; } .typing-indicator.visible { opacity: 1; transform: translateY(0); } .typing-indicator img { width: 36px; height: 36px; border-radius: 50%; margin-right: 12px; } .typing-bubble { background-color: #e9edf5; padding: 12px 16px; border-radius: 18px; border-bottom-left-radius: 4px; position: relative; display: flex; align-items: center; } .typing-dot { background-color: #9da9c7; width: 8px; height: 8px; border-radius: 50%; margin: 0 2px; animation: typingAnimation 1.5s infinite ease-in-out; } .typing-dot:nth-child(1) { animation-delay: 0s; } .typing-dot:nth-child(2) { animation-delay: 0.3s; } .typing-dot:nth-child(3) { animation-delay: 0.6s; } @keyframes typingAnimation { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-5px); } } /* Reaction styling */ .reaction { position: absolute; bottom: -12px; right: 10px; background: white; padding: 3px 8px; border-radius: 12px; font-size: 13px; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); display: flex; align-items: center; opacity: 0; transform: scale(0.8); transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); } .reaction.visible { opacity: 1; transform: scale(1); } .reaction span { margin-left: 4px; font-weight: 500; color: #555; } /* Custom scrollbar */ .chat-body::-webkit-scrollbar { width: 6px; } .chat-body::-webkit-scrollbar-track { background: transparent; } .chat-body::-webkit-scrollbar-thumb { background-color: rgba(0, 0, 0, 0.1); border-radius: 10px; } .chat-body::-webkit-scrollbar-thumb:hover { background-color: rgba(0, 0, 0, 0.2); } /* Pop-up emoji selector */ .emoji-selector { position: absolute; bottom: 80px; left: 20px; background: white; border-radius: 12px; box-shadow: 0 6px 24px rgba(0, 0, 0, 0.15); padding: 12px; width: 240px; display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px; opacity: 0; transform: translateY(10px); transition: all 0.3s ease; pointer-events: none; z-index: 100; } .emoji-selector.visible { opacity: 1; transform: translateY(0); pointer-events: all; } .emoji-option { width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; font-size: 20px; cursor: pointer; border-radius: 8px; transition: all 0.2s ease; } .emoji-option:hover { background-color: #f0f4ff; transform: scale(1.1); } /* Animation for sending a new message */ @keyframes sendAnimation { 0% { opacity: 0; transform: translateY(20px) scale(0.8); } 100% { opacity: 1; transform: translateY(0) scale(1); } } .message.sending { animation: sendAnimation 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards; } @media (max-width: 700px) { .chat-container { width: 100%; height: 100%; border-radius: 0; } .bubble { max-width: 75%; } .header-actions i:nth-child(3), .header-actions i:nth-child(4) { display: none; } .input-actions i:nth-child(3), .input-actions i:nth-child(4) { display: none; } } /* Adaptive dark mode support */ @media (prefers-color-scheme: dark) { body { background-color: #121212; } .chat-container { background-color: #1f1f1f; box-shadow: 0 12px 28px rgba(0, 0, 0, 0.25); } .chat-body { background-color: #121212; background-image: radial-gradient(circle at 25% 25%, rgba(110, 142, 251, 0.08) 2%, transparent 5%), radial-gradient(circle at 75% 75%, rgba(167, 119, 227, 0.08) 2%, transparent 5%); } .message-input { background-color: #2d2d2d; color: #e0e0e0; } .message-input:focus { background-color: #333; box-shadow: 0 0 0 2px rgba(110, 142, 251, 0.4); } .typing-bubble { background-color: #2d2d2d; } .typing-dot { background-color: #aaa; } .group-divider span { background-color: rgba(255, 255, 255, 0.1); color: #bbb; } .reaction { background: #2d2d2d; } .reaction span { color: #ddd; } .emoji-selector { background-color: #2d2d2d; } .emoji-option:hover { background-color: #3d3d3d; } .chat-input { background-color: #1f1f1f; border-top: 1px solid rgba(255, 255, 255, 0.05); } } </style> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> </head> <body> <div class="chat-container"> <div class="chat-header"> <div class="header-left"> <img src="https://images.unsplash.com/photo-1534528741775-53994a69daeb?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80" alt="User" class="avatar"> <div class="user-info"> <h2>Design Team</h2> <p>Mia, Alex, Sam, You</p> </div> </div> <div class="header-actions"> <i class="fas fa-phone"></i> <i class="fas fa-video"></i> <i class="fas fa-info-circle"></i> <i class="fas fa-ellipsis-v"></i> </div> </div> <div class="chat-body" id="chat-body"> <div class="group-divider"> <span>Today, 9:41 AM</span> </div> <!-- Messages will be dynamically added here --> </div> <div class="emoji-selector" id="emoji-selector"> <div class="emoji-option">ð</div> <div class="emoji-option">âĪïļ</div> <div class="emoji-option">ð</div> <div class="emoji-option">ðŪ</div> <div class="emoji-option">ðĒ</div> <div class="emoji-option">ðĨ</div> <div class="emoji-option">ð</div> <div class="emoji-option">âĻ</div> <div class="emoji-option">ðĨģ</div> <div class="emoji-option">ð</div> </div> <div class="chat-input"> <div class="input-actions"> <i class="far fa-grin" id="emoji-button"></i> <i class="fas fa-paperclip"></i> <i class="fas fa-image"></i> <i class="fas fa-microphone"></i> </div> <input type="text" class="message-input" id="message-input" placeholder="Type a message..."> <button class="send-button" id="send-button"> <i class="fas fa-paper-plane"></i> </button> </div> </div> <script> // DOM Elements const chatBody = document.getElementById('chat-body'); const messageInput = document.getElementById('message-input'); const sendButton = document.getElementById('send-button'); const emojiButton = document.getElementById('emoji-button'); const emojiSelector = document.getElementById('emoji-selector'); // Message data const conversations = [ { type: 'incoming', user: 'friend-1', name: 'Mia', avatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', message: 'Hey team! I just pushed the latest gradient updates to our design system. The chat bubbles now have that subtle shadow depth we discussed.', time: '9:42 AM' }, { type: 'incoming', user: 'friend-2', name: 'Alex', avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', message: 'Nice work! I love how the colors differentiate the users. Makes following conversations so much easier.', time: '9:45 AM', reaction: 'ð 2' }, { type: 'outgoing', message: 'The animations look great too. That subtle scale on hover adds just the right amount of interactivity without being distracting.', time: '9:47 AM' }, { type: 'incoming', user: 'friend-1', name: 'Mia', avatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', message: 'Thanks! I kept the animation curves relatively gentle. The cubic-bezier timing function gives it that slight bounce at the end.', time: '9:49 AM' }, { type: 'typing', user: 'friend-2', name: 'Alex', avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80' }, { type: 'incoming', user: 'friend-2', name: 'Alex', avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', message: 'Should we consider adding read receipts or typing indicators? I think it would enhance the real-time feel.', time: '9:52 AM' }, { type: 'divider', text: 'Sam joined the chat' }, { type: 'incoming', user: 'friend-1', name: 'Sam', avatar: 'https://images.unsplash.com/photo-1539571696357-5a69c17a67c6?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', message: 'Hey everyone! Sorry I'm late to the chat. The gradient bubbles look awesome in dark mode too - I just tested it.', time: '10:01 AM' }, { type: 'outgoing', message: 'Welcome, Sam! Yes, I made sure the colors have enough contrast in both light and dark modes. The vibrant gradients really pop against the dark background.', time: '10:03 AM', reaction: 'ðĨ 3' }, { type: 'incoming', user: 'friend-1', name: 'Mia', avatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', message: 'Alex, I just implemented typing indicators! Take a look at the animation - the bouncing dots feel natural, right?', time: '10:05 AM' } ]; // Initialize chat function initChat() { // Set group divider to visible document.querySelector('.group-divider').classList.add('visible'); // Render initial messages with delay let delay = 300; conversations.forEach(msg => { setTimeout(() => { renderMessage(msg); scrollToBottom(); }, delay); delay += 300; }); } // Render a message function renderMessage(messageData) { if (messageData.type === 'divider') { const divider = document.createElement('div'); divider.className = 'group-divider'; divider.innerHTML = `<span>${messageData.text}</span>`; chatBody.appendChild(divider); setTimeout(() => divider.classList.add('visible'), 50); return; } if (messageData.type === 'typing') { const typingIndicator = document.createElement('div'); typingIndicator.className = 'typing-indicator'; typingIndicator.innerHTML = ` <img src="${messageData.avatar}" alt="${messageData.name}" class="typing-avatar"> <div class="typing-bubble"> <div class="typing-dot"></div> <div class="typing-dot"></div> <div class="typing-dot"></div> </div> `; chatBody.appendChild(typingIndicator); setTimeout(() => typingIndicator.classList.add('visible'), 50); return; } const messageDiv = document.createElement('div'); messageDiv.className = `message ${messageData.type}`; if (messageData.user) { messageDiv.classList.add(messageData.user); } let avatarHtml = ''; if (messageData.type === 'incoming') { avatarHtml = `<img src="${messageData.avatar}" alt="${messageData.name}" class="message-avatar">`; } let reactionHtml = ''; if (messageData.reaction) { reactionHtml = `<div class="reaction">${messageData.reaction}</div>`; } messageDiv.innerHTML = ` ${avatarHtml} <div class="bubble"> <p>${messageData.message}</p> <div class="time"> ${messageData.time} ${messageData.type === 'outgoing' ? '<i class="fas fa-check-double"></i>' : ''} </div> ${reactionHtml} </div> `; chatBody.appendChild(messageDiv); // Animate entrance setTimeout(() => { messageDiv.classList.add('visible'); if (reactionHtml) { messageDiv.querySelector('.reaction').classList.add('visible'); } }, 50); } // Send a new message function sendMessage() { const message = messageInput.value.trim(); if (!message) return; const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); const ampm = hours >= 12 ? 'PM' : 'AM'; const formattedHours = hours % 12 || 12; const formattedMinutes = minutes < 10 ? '0' + minutes : minutes; const timeString = `${formattedHours}:${formattedMinutes} ${ampm}`; const newMessage = { type: 'outgoing', message: message, time: timeString }; renderMessage(newMessage); messageInput.value = ''; scrollToBottom(); // Simulate a response after a delay setTimeout(() => { // Show typing indicator const typingData = { type: 'typing', user: 'friend-1', name: 'Mia', avatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80' }; renderMessage(typingData); scrollToBottom(); // Remove typing indicator and show response after a delay setTimeout(() => { document.querySelector('.typing-indicator').remove(); const responseMessage = { type: 'incoming', user: 'friend-1', name: 'Mia', avatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', message: getResponse(message), time: timeString }; renderMessage(responseMessage); scrollToBottom(); }, 2000); }, 1000); } // Get a response based on input function getResponse(message) { const responses = [ "I like how you're thinking! Let's add that to our design system.", "Great point! The animation timing feels just right for that.", "That adds a nice touch to the UI. Very subtle but effective.", "I hadn't considered that perspective. Let's explore it further.", "Your attention to detail is impressive! That's exactly what this design needed." ]; return responses[Math.floor(Math.random() * responses.length)]; } // Scroll to bottom of chat function scrollToBottom() { chatBody.scrollTop = chatBody.scrollHeight; } // Event listeners sendButton.addEventListener('click', sendMessage); messageInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { sendMessage(); } }); emojiButton.addEventListener('click', () => { emojiSelector.classList.toggle('visible'); }); document.querySelectorAll('.emoji-option').forEach(emoji => { emoji.addEventListener('click', () => { messageInput.value += emoji.textContent; messageInput.focus(); emojiSelector.classList.remove('visible'); }); }); // Close emoji selector when clicking outside document.addEventListener('click', (e) => { if (!emojiSelector.contains(e.target) && e.target !== emojiButton) { emojiSelector.classList.remove('visible'); } }); // Initialize chat on load window.onload = initChat; </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Customer Support Chat Interface</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; } body { background-color: #f5f7fa; display: flex; justify-content: center; align-items: center; height: 100vh; width: 100%; color: #333; overflow: hidden; } .chat-container { width: 100%; max-width: 650px; height: 650px; background-color: #fff; border-radius: 16px; box-shadow: 0 12px 28px rgba(0, 0, 0, 0.12); overflow: hidden; display: flex; flex-direction: column; position: relative; } .chat-header { padding: 18px 24px; background: linear-gradient(90deg, #4776E6 0%, #6a52df 100%); color: white; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid rgba(0, 0, 0, 0.1); position: relative; z-index: 2; } .chat-header-left { display: flex; align-items: center; gap: 12px; } .agent-photo { width: 42px; height: 42px; border-radius: 50%; object-fit: cover; border: 2px solid rgba(255, 255, 255, 0.6); } .agent-info h2 { font-size: 16px; font-weight: 600; margin-bottom: 2px; } .agent-info p { font-size: 12px; opacity: 0.9; } .status-badge { padding: 4px 10px; background-color: rgba(255, 255, 255, 0.2); border-radius: 12px; font-size: 12px; font-weight: 500; backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); } .chat-messages { flex: 1; padding: 20px; overflow-y: auto; background-image: url("data:image/svg+xml,%3Csvg width='100' height='100' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M11 18c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm48 25c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm-43-7c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm63 31c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM34 90c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm56-76c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM12 86c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm28-65c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm23-11c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-6 60c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm29 22c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zM32 63c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm57-13c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-9-21c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM60 91c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM35 41c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM12 60c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2z' fill='%23f0f2f5' fill-opacity='0.3' fill-rule='evenodd'/%3E%3C/svg%3E"); background-size: 200px; background-position: center; display: flex; flex-direction: column; gap: 15px; position: relative; } .chat-messages::-webkit-scrollbar { width: 8px; } .chat-messages::-webkit-scrollbar-track { background: rgba(0, 0, 0, 0.05); border-radius: 4px; } .chat-messages::-webkit-scrollbar-thumb { background-color: rgba(0, 0, 0, 0.2); border-radius: 4px; } .message { max-width: 80%; padding: 12px 16px; border-radius: 18px; position: relative; line-height: 1.5; font-size: 14px; animation: fadeIn 0.5s ease-out; transform-origin: bottom; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); opacity: 0; } .message.visible { opacity: 1; } .agent { align-self: flex-start; background-color: #EFF2F7; color: #3a3a3a; border-bottom-left-radius: 4px; margin-left: 8px; } .agent::before { content: ''; position: absolute; bottom: 0; left: -8px; width: 20px; height: 20px; background: linear-gradient(to bottom right, transparent 50%, #EFF2F7 50%); border-bottom-right-radius: 18px; transform: rotate(45deg); z-index: -1; } .customer { align-self: flex-end; background: linear-gradient(90deg, #4776E6 0%, #6a52df 100%); color: white; border-bottom-right-radius: 4px; margin-right: 8px; } .customer::before { content: ''; position: absolute; bottom: 0; right: -8px; width: 20px; height: 20px; background: linear-gradient(to bottom left, transparent 50%, #6a52df 50%); border-bottom-left-radius: 18px; transform: rotate(-45deg); z-index: -1; } .timestamp { font-size: 10px; margin-top: 6px; opacity: 0.7; display: block; text-align: right; } .agent .timestamp { text-align: left; } .typing-indicator { display: inline-flex; align-items: center; gap: 5px; margin-top: 4px; } .typing-indicator span { width: 6px; height: 6px; background-color: rgba(0, 0, 0, 0.3); border-radius: 50%; display: inline-block; animation: pulse 1.5s infinite ease-in-out; } .typing-indicator span:nth-child(2) { animation-delay: 0.2s; } .typing-indicator span:nth-child(3) { animation-delay: 0.4s; } .agent .typing-indicator span { background-color: rgba(0, 0, 0, 0.3); } .customer .typing-indicator span { background-color: rgba(255, 255, 255, 0.6); } .chat-input { padding: 16px; background-color: #fff; border-top: 1px solid rgba(0, 0, 0, 0.1); display: flex; align-items: center; gap: 10px; position: relative; z-index: 2; box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05); } .input-area { flex: 1; position: relative; } .message-input { width: 100%; padding: 14px 20px; border: none; background-color: #f0f2f5; border-radius: 24px; font-size: 14px; resize: none; outline: none; transition: all 0.2s ease; min-height: 50px; max-height: 120px; overflow-y: auto; } .message-input:focus { background-color: #eaeef2; box-shadow: 0 0 0 2px rgba(71, 118, 230, 0.2); } .message-input::-webkit-scrollbar { width: 6px; } .message-input::-webkit-scrollbar-track { background: transparent; } .message-input::-webkit-scrollbar-thumb { background-color: rgba(0, 0, 0, 0.2); border-radius: 3px; } .input-button { background: linear-gradient(90deg, #4776E6 0%, #6a52df 100%); color: white; border: none; width: 50px; height: 50px; border-radius: 50%; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: all 0.2s ease; flex-shrink: 0; box-shadow: 0 2px 8px rgba(71, 118, 230, 0.3); } .input-button:hover { transform: scale(1.05); box-shadow: 0 4px 12px rgba(71, 118, 230, 0.4); } .input-button:active { transform: scale(0.95); } .input-button svg { width: 20px; height: 20px; fill: currentColor; } .attachments { display: flex; gap: 10px; } .attachment-button { background-color: #f0f2f5; border: none; width: 40px; height: 40px; border-radius: 50%; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: all 0.2s ease; color: #666; } .attachment-button:hover { background-color: #e4e7ec; color: #4776E6; } .emoji-picker { position: absolute; bottom: 60px; right: 0; background-color: white; border-radius: 12px; box-shadow: 0 5px 20px rgba(0, 0, 0, 0.15); padding: 10px; display: grid; grid-template-columns: repeat(7, 1fr); gap: 8px; max-width: 300px; z-index: 3; opacity: 0; transform: translateY(10px); pointer-events: none; transition: all 0.2s ease; } .emoji-picker.active { opacity: 1; transform: translateY(0); pointer-events: all; } .emoji { cursor: pointer; font-size: 20px; text-align: center; padding: 5px; border-radius: 6px; transition: all 0.2s ease; } .emoji:hover { background-color: #f0f2f5; transform: scale(1.1); } .day-separator { text-align: center; margin: 20px 0; position: relative; font-size: 12px; color: #888; } .day-separator:before, .day-separator:after { content: ''; position: absolute; top: 50%; width: 30%; height: 1px; background-color: rgba(0, 0, 0, 0.1); } .day-separator:before { left: 0; } .day-separator:after { right: 0; } .rating-request { background-color: #fff; border-radius: 12px; padding: 12px 15px; display: flex; flex-direction: column; align-items: center; gap: 8px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); margin: 15px 0; width: 90%; align-self: center; animation: fadeIn 0.5s ease-out; } .rating-question { font-size: 13px; color: #555; margin-bottom: 3px; } .stars { display: flex; gap: 5px; } .star { cursor: pointer; font-size: 20px; transition: all 0.2s ease; color: #ddd; } .star.active { color: #FFD700; animation: pulse 0.5s ease-out; } .quick-actions { display: flex; gap: 10px; flex-wrap: wrap; margin: 15px 0; width: 100%; justify-content: center; } .quick-action { background-color: #fff; border: 1px solid rgba(0, 0, 0, 0.1); border-radius: 20px; padding: 8px 16px; font-size: 13px; cursor: pointer; transition: all 0.2s ease; white-space: nowrap; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); } .quick-action:hover { background-color: #f7f9fc; border-color: rgba(71, 118, 230, 0.3); transform: translateY(-2px); box-shadow: 0 3px 10px rgba(0, 0, 0, 0.08); } .connecting { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 20px; text-align: center; background-color: #f7f9fc; border-radius: 12px; margin: 20px auto; width: 85%; gap: 10px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); } .connecting-animation { width: 50px; height: 50px; border-radius: 50%; border: 3px solid rgba(71, 118, 230, 0.2); border-top-color: #4776E6; animation: spin 1.5s linear infinite; margin: 10px 0; } .connecting-text { font-size: 14px; color: #666; font-weight: 500; } @keyframes fadeIn { 0% { opacity: 0; transform: translateY(10px) scale(0.95); } 100% { opacity: 1; transform: translateY(0) scale(1); } } @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.3); } 100% { transform: scale(1); } } @keyframes spin { to { transform: rotate(360deg); } } @media (max-width: 650px) { .chat-container { width: 95%; height: 95vh; border-radius: 12px; } .rating-request { width: 95%; } .message { max-width: 90%; } } </style> </head> <body> <div class="chat-container"> <div class="chat-header"> <div class="chat-header-left"> <img src="https://randomuser.me/api/portraits/women/42.jpg" alt="Customer Support Agent" class="agent-photo"> <div class="agent-info"> <h2>Emily Rodriguez</h2> <p>Technical Support Specialist</p> </div> </div> <div class="status-badge">ACTIVE NOW</div> </div> <div class="chat-messages" id="chatMessages"> <!-- Message content will be populated by JS --> </div> <div class="chat-input"> <div class="attachments"> <button class="attachment-button" id="emojiButton" aria-label="Add emoji"> <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"> <circle cx="12" cy="12" r="10"></circle> <path d="M8 14s1.5 2 4 2 4-2 4-2"></path> <line x1="9" y1="9" x2="9.01" y2="9"></line> <line x1="15" y1="9" x2="15.01" y2="9"></line> </svg> </button> <button class="attachment-button" aria-label="Attach file"> <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"> <path d="M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48"></path> </svg> </button> </div> <div class="input-area"> <div class="emoji-picker" id="emojiPicker"> <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 class="emoji">ðĒ</div> <div class="emoji">ð</div> <div class="emoji">â </div> <div class="emoji">â</div> <div class="emoji">ð</div> <div class="emoji">ðĄ</div> </div> <textarea class="message-input" id="messageInput" placeholder="Type your message..." rows="1"></textarea> </div> <button class="input-button" id="sendButton" aria-label="Send message"> <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> <script> document.addEventListener('DOMContentLoaded', function() { const chatMessages = document.getElementById('chatMessages'); const messageInput = document.getElementById('messageInput'); const sendButton = document.getElementById('sendButton'); const emojiButton = document.getElementById('emojiButton'); const emojiPicker = document.getElementById('emojiPicker'); const emojis = document.querySelectorAll('.emoji'); // Initial messages const initialMessages = [ { content: 'Hello! I'm Emily from TechPro Support. How can I assist you today with your device or software issue?', sender: 'agent', time: '10:02 AM' } ]; // Automated conversation flow const conversationFlow = [ { content: 'My laptop keeps freezing when I try to use video editing software. It was working fine until yesterday.', sender: 'customer', time: '10:03 AM', delay: 1000 }, { content: 'I understand how frustrating that can be. Let me help troubleshoot this issue for you.', sender: 'agent', time: '10:03 AM', delay: 2000 }, { content: 'Could you tell me which video editing software you're using and your laptop specifications?', sender: 'agent', time: '10:04 AM', delay: 4000 }, { content: 'I'm using Adobe Premiere Pro 2023. My laptop is a Dell XPS 15 with 16GB RAM and Intel i7 processor.', sender: 'customer', time: '10:05 AM', delay: 5000 }, { content: 'Thanks for that information. Have you installed any new software or updates recently?', sender: 'agent', time: '10:05 AM', delay: 8000 }, { content: 'Actually yes, I updated my graphics drivers yesterday.', sender: 'customer', time: '10:06 AM', delay: 10000 }, { type: 'typing', sender: 'agent', delay: 11000, duration: 2500 }, { content: 'That's likely the cause of the issue. Recent NVIDIA driver updates have been causing conflicts with Adobe software.', sender: 'agent', time: '10:07 AM', delay: 14000 }, { content: 'I'd recommend rolling back to the previous driver version. Would you like me to guide you through that process?', sender: 'agent', time: '10:07 AM', delay: 16000 }, { type: 'quick-actions', delay: 17000 }, { content: 'Yes, please guide me through the rollback process.', sender: 'customer', time: '10:08 AM', delay: 20000 }, { type: 'typing', sender: 'agent', delay: 21000, duration: 3000 }, { content: 'Great! Here's how to roll back your driver:<br>1. Right-click on the Start menu and select "Device Manager"<br>2. Expand "Display adapters"<br>3. Right-click on your graphics card and select "Properties"<br>4. Go to the "Driver" tab and click "Roll Back Driver"<br>5. Follow the on-screen instructions to complete the process', sender: 'agent', time: '10:09 AM', delay: 24000 }, { content: 'After rolling back, restart your laptop and test Adobe Premiere again.', sender: 'agent', time: '10:09 AM', delay: 26000 }, { content: 'I'll try that now. Give me a few minutes to complete the steps.', sender: 'customer', time: '10:10 AM', delay: 28000 }, { type: 'day-separator', content: '10 minutes later', delay: 30000 }, { content: 'It worked! I rolled back the driver and Premiere is running smoothly again. Thank you so much for your help!', sender: 'customer', time: '10:21 AM', delay: 31000 }, { type: 'typing', sender: 'agent', delay: 32000, duration: 2000 }, { content: 'That's excellent news! I'm glad we were able to resolve the issue. This is a common conflict with the latest drivers and Adobe products.', sender: 'agent', time: '10:22 AM', delay: 34000 }, { content: 'For future reference, it's always a good idea to check compatibility before updating drivers for systems you use for professional software.', sender: 'agent', time: '10:22 AM', delay: 36000 }, { type: 'rating', delay: 37000 }, { content: 'Thanks for the tip! I'll definitely be more careful with updates in the future.', sender: 'customer', time: '10:23 AM', delay: 40000 }, { content: 'You're very welcome! Is there anything else I can help you with today?', sender: 'agent', time: '10:24 AM', delay: 42000 } ]; // Display initial messages initialMessages.forEach(msg => { addMessage(msg.content, msg.sender, msg.time); }); // Start conversation flow startConversationFlow(); // Function to handle sending a message function sendMessage() { const message = messageInput.value.trim(); if (message) { // Get current time const now = new Date(); const hours = now.getHours() % 12 || 12; const minutes = now.getMinutes().toString().padStart(2, '0'); const ampm = now.getHours() >= 12 ? 'PM' : 'AM'; const timeString = `${hours}:${minutes} ${ampm}`; // Add the message to the chat addMessage(message, 'customer', timeString); // Clear the input messageInput.value = ''; // Auto resize the input back to default messageInput.style.height = 'auto'; // Simulate reply (for demo purposes) showTypingIndicator('agent'); setTimeout(() => { removeTypingIndicator(); const replies = [ "I understand your concern. Let me look into that for you.", "Thanks for providing those details. I'm checking on possible solutions.", "That's a good question. Let me find the most accurate information for you.", "I appreciate your patience. Let me see how we can resolve this issue effectively." ]; const randomReply = replies[Math.floor(Math.random() * replies.length)]; addMessage(randomReply, 'agent', timeString); }, 2000); } } // Function to add a message to the chat function addMessage(content, sender, time) { const messageDiv = document.createElement('div'); messageDiv.className = `message ${sender}`; // Set inner HTML with content and time messageDiv.innerHTML = ` ${content} <span class="timestamp">${time}</span> `; chatMessages.appendChild(messageDiv); // Scroll to the bottom setTimeout(() => { chatMessages.scrollTop = chatMessages.scrollHeight; messageDiv.classList.add('visible'); }, 10); } // Function to show typing indicator function showTypingIndicator(sender) { const typingDiv = document.createElement('div'); typingDiv.className = `message ${sender} typing`; typingDiv.id = 'typingIndicator'; typingDiv.innerHTML = ` <div class="typing-indicator"> <span></span> <span></span> <span></span> </div> <span class="timestamp">typing...</span> `; chatMessages.appendChild(typingDiv); chatMessages.scrollTop = chatMessages.scrollHeight; setTimeout(() => { typingDiv.classList.add('visible'); }, 10); } // Function to remove typing indicator function removeTypingIndicator() { const typingIndicator = document.getElementById('typingIndicator'); if (typingIndicator) { typingIndicator.remove(); } } // Function to add day separator function addDaySeparator(text) { const separator = document.createElement('div'); separator.className = 'day-separator'; separator.textContent = text; chatMessages.appendChild(separator); // Scroll to the bottom chatMessages.scrollTop = chatMessages.scrollHeight; } // Function to add rating request function addRatingRequest() { const ratingDiv = document.createElement('div'); ratingDiv.className = 'rating-request'; ratingDiv.innerHTML = ` <div class="rating-question">How would you rate the support you received?</div> <div class="stars"> <div class="star" data-rating="1">â </div> <div class="star" data-rating="2">â </div> <div class="star" data-rating="3">â </div> <div class="star" data-rating="4">â </div> <div class="star" data-rating="5">â </div> </div> `; chatMessages.appendChild(ratingDiv); // Add event listeners to stars const stars = ratingDiv.querySelectorAll('.star'); stars.forEach(star => { star.addEventListener('click', function() { const rating = this.getAttribute('data-rating'); stars.forEach(s => { if (s.getAttribute('data-rating') <= rating) { s.classList.add('active'); } else { s.classList.remove('active'); } }); }); star.addEventListener('mouseenter', function() { const rating = this.getAttribute('data-rating'); stars.forEach(s => { if (s.getAttribute('data-rating') <= rating) { s.style.color = '#FFD700'; } }); }); star.addEventListener('mouseleave', function() { stars.forEach(s => { if (!s.classList.contains('active')) { s.style.color = ''; } }); }); }); // Scroll to the bottom chatMessages.scrollTop = chatMessages.scrollHeight; } // Function to add quick actions function addQuickActions() { const actionsDiv = document.createElement('div'); actionsDiv.className = 'quick-actions'; actionsDiv.innerHTML = ` <div class="quick-action">Yes, guide me through it</div> <div class="quick-action">I'll try it myself</div> <div class="quick-action">Can we try another solution?</div> `; chatMessages.appendChild(actionsDiv); // Add event listeners to quick actions const actions = actionsDiv.querySelectorAll('.quick-action'); actions.forEach(action => { action.addEventListener('click', function() { // Get current time const now = new Date(); const hours = now.getHours() % 12 || 12; const minutes = now.getMinutes().toString().padStart(2, '0'); const ampm = now.getHours() >= 12 ? 'PM' : 'AM'; const timeString = `${hours}:${minutes} ${ampm}`; // Add the message addMessage(this.textContent, 'customer', timeString); // Remove the quick actions actionsDiv.remove(); }); }); // Scroll to the bottom chatMessages.scrollTop = chatMessages.scrollHeight; } // Function to start conversation flow function startConversationFlow() { conversationFlow.forEach(item => { setTimeout(() => { switch(item.type) { case 'typing': showTypingIndicator(item.sender); setTimeout(() => removeTypingIndicator(), item.duration); break; case 'day-separator': addDaySeparator(item.content); break; case 'rating': addRatingRequest(); break; case 'quick-actions': addQuickActions(); break; default: addMessage(item.content, item.sender, item.time); break; } }, item.delay); }); } // Event listeners sendButton.addEventListener('click', sendMessage); messageInput.addEventListener('keydown', function(e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }); messageInput.addEventListener('input', function() { this.style.height = 'auto'; this.style.height = (this.scrollHeight) + 'px'; if (this.scrollHeight > 120) { this.style.overflowY = 'auto'; } else { this.style.overflowY = 'hidden'; } }); emojiButton.addEventListener('click', function() { emojiPicker.classList.toggle('active'); }); document.addEventListener('click', function(e) { if (!emojiButton.contains(e.target) && !emojiPicker.contains(e.target)) { emojiPicker.classList.remove('active'); } }); emojis.forEach(emoji => { emoji.addEventListener('click', function() { messageInput.value += this.textContent; emojiPicker.classList.remove('active'); messageInput.focus(); }); }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Garamond', serif; } body { width: 100%; height: 100vh; background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400" viewBox="0 0 400 400"><rect width="400" height="400" fill="%23243142"/><path d="M50,50 L350,50 L350,350 L50,350 Z" fill="none" stroke="%23485a73" stroke-width="2"/><circle cx="200" cy="200" r="100" fill="none" stroke="%23485a73" stroke-width="1.5"/></svg>'); background-color: #1a2433; display: flex; flex-direction: column; justify-content: center; align-items: center; overflow: hidden; perspective: 1000px; } .fantasy-game-container { width: 680px; height: 680px; position: relative; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; overflow: hidden; } .game-scene { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="680" height="680" viewBox="0 0 680 680"><rect width="680" height="680" fill="%232d3e50"/><path d="M0,480 C80,450 160,520 240,480 C320,440 400,500 480,460 C560,420 640,460 680,440 L680,680 L0,680 Z" fill="%231a2940"/><path d="M0,520 C60,510 120,540 180,520 C240,500 300,530 360,510 C420,490 480,520 540,500 C600,480 660,510 680,500 L680,680 L0,680 Z" fill="%23142234"/></svg>'); z-index: 1; } .character { position: absolute; bottom: 50px; width: 120px; height: 150px; z-index: 5; transition: transform 0.5s ease; } .character-1 { left: 100px; background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 150"><path d="M60,30 L40,80 L60,140 L80,80 Z" fill="%23bc5e2a"/><circle cx="60" cy="30" r="25" fill="%23ffd9b3"/><path d="M45,25 Q60,40 75,25" fill="none" stroke="%23333" stroke-width="2"/><circle cx="50" cy="18" r="3" fill="%23333"/><circle cx="70" cy="18" r="3" fill="%23333"/></svg>'); } .character-2 { right: 100px; background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 150"><path d="M60,30 L40,80 L60,140 L80,80 Z" fill="%234b6cb7"/><circle cx="60" cy="30" r="25" fill="%23e8cda8"/><path d="M45,28 Q60,15 75,28" fill="none" stroke="%23333" stroke-width="2"/><circle cx="50" cy="18" r="3" fill="%23333"/><circle cx="70" cy="18" r="3" fill="%23333"/></svg>'); } .dialogue-container { width: 100%; height: 250px; padding: 20px; z-index: 10; display: flex; justify-content: center; align-items: center; position: relative; } .dialogue-bubble { min-width: 200px; max-width: 350px; min-height: 100px; padding: 25px; position: relative; opacity: 0; transform: scale(0.8); transition: opacity 0.3s ease, transform 0.3s ease; filter: drop-shadow(0 8px 15px rgba(0, 0, 0, 0.3)); } .dialogue-bubble.active { opacity: 1; transform: scale(1); } .bubble-content { position: relative; z-index: 2; color: #42210b; font-size: 18px; line-height: 1.4; text-align: center; } .bubble-bg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350 180"><filter id="paper" x="0" y="0" width="100%" height="100%"><feTurbulence type="fractalNoise" baseFrequency="0.04" numOctaves="5" result="noise"/><feDisplacementMap in="SourceGraphic" in2="noise" scale="4" xChannelSelector="R" yChannelSelector="G" result="paper"/></filter><path d="M20,10 C10,10 5,15 5,25 L5,145 C5,155 10,165 20,165 L330,165 C340,165 345,155 345,145 L345,25 C345,15 340,10 330,10 Z" fill="%23f0e6d2" filter="url(%23paper)"/><path d="M20,10 C10,10 5,15 5,25 L5,145 C5,155 10,165 20,165 L330,165 C340,165 345,155 345,145 L345,25 C345,15 340,10 330,10 Z" fill="none" stroke="%23c8aa7c" stroke-width="3" stroke-dasharray="8,5"/></svg>'); background-size: 100% 100%; z-index: 1; } .bubble-pointer { position: absolute; bottom: -20px; width: 40px; height: 30px; background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 30"><path d="M0,0 L20,30 L40,0 Z" fill="%23f0e6d2" stroke="%23c8aa7c" stroke-width="2"/></svg>'); background-size: contain; background-repeat: no-repeat; z-index: 1; } .bubble-left .bubble-pointer { left: 30px; } .bubble-right .bubble-pointer { right: 30px; } .dialogue-left { align-self: flex-start; margin-left: 60px; } .dialogue-right { align-self: flex-end; margin-right: 60px; } .character-name { font-weight: bold; color: #6d4c25; margin-bottom: 8px; font-size: 20px; text-shadow: 1px 1px 0 #f9f3e9; } .particles { position: absolute; pointer-events: none; z-index: 3; } .particle { position: absolute; width: 8px; height: 8px; border-radius: 50%; background-color: rgba(255, 255, 255, 0.6); animation: float 2s ease-out forwards; } @keyframes float { 0% { transform: translate(0, 0) scale(0); opacity: 0.8; } 50% { opacity: 0.6; } 100% { transform: translate(var(--tx), var(--ty)) scale(1); opacity: 0; } } .controls { position: absolute; bottom: 20px; right: 20px; z-index: 20; display: flex; gap: 10px; } .control-btn { background: rgba(240, 230, 210, 0.8); border: 2px solid #c8aa7c; color: #6d4c25; padding: 8px 12px; border-radius: 5px; font-weight: bold; cursor: pointer; transition: all 0.2s ease; font-size: 14px; } .control-btn:hover { background: rgba(240, 230, 210, 1); transform: translateY(-2px); box-shadow: 0 3px 5px rgba(0, 0, 0, 0.2); } .control-btn:active { transform: translateY(0); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); } .dialogue-option { background: rgba(240, 230, 210, 0.8); border: 2px solid #c8aa7c; color: #6d4c25; padding: 8px 15px; border-radius: 5px; margin: 5px; cursor: pointer; transition: all 0.2s ease; text-align: center; font-size: 16px; box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1); } .dialogue-option:hover { background: rgba(240, 230, 210, 1); transform: translateY(-2px); box-shadow: 0 5px 10px rgba(0, 0, 0, 0.15); } .dialogue-options-container { display: flex; flex-direction: column; position: absolute; bottom: 80px; left: 50%; transform: translateX(-50%); width: 80%; z-index: 15; opacity: 0; visibility: hidden; transition: opacity 0.3s ease, visibility 0.3s ease; } .dialogue-options-container.show { opacity: 1; visibility: visible; } .magical-fx { position: absolute; width: 100%; height: 100%; pointer-events: none; z-index: 0; opacity: 0.4; background: radial-gradient(circle at 50% 50%, rgba(100, 137, 255, 0.1) 0%, rgba(77, 54, 209, 0.05) 50%, transparent 80%); } @media (max-width: 700px) { .fantasy-game-container { width: 95%; height: 95vh; } .dialogue-bubble { max-width: 280px; min-height: 80px; padding: 15px; } .bubble-content { font-size: 16px; } .character-name { font-size: 18px; } .character { width: 100px; height: 130px; } .character-1 { left: 40px; } .character-2 { right: 40px; } .dialogue-option { font-size: 14px; padding: 6px 12px; } } </style> </head> <body> <div class="fantasy-game-container"> <div class="magical-fx"></div> <div class="game-scene"></div> <div class="character character-1"></div> <div class="character character-2"></div> <div class="dialogue-container"> <!-- Dialogue bubbles will be inserted here --> </div> <div class="dialogue-options-container"> <div class="dialogue-option" data-response="magic">Tell me about the crystal's magic</div> <div class="dialogue-option" data-response="quest">Ask about the forest quest</div> <div class="dialogue-option" data-response="decline">I'm not ready for this journey</div> </div> <div class="controls"> <button class="control-btn next-btn">Next</button> <button class="control-btn restart-btn">Restart</button> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { const dialogueContainer = document.querySelector('.dialogue-container'); const nextBtn = document.querySelector('.next-btn'); const restartBtn = document.querySelector('.restart-btn'); const optionsContainer = document.querySelector('.dialogue-options-container'); const dialogueOptions = document.querySelectorAll('.dialogue-option'); const character1 = document.querySelector('.character-1'); const character2 = document.querySelector('.character-2'); // Dialogue script const dialogues = [ { speaker: "Eldrin", side: "left", text: "Ah, brave traveler! The whispers of the ancient trees have foretold your arrival to Mystwood. The Crystalline Pendant glows in your presence!" }, { speaker: "Lyra", side: "right", text: "The pendant... it responds to you! Few mortals have ever witnessed its glow. Perhaps you are the one the prophecy speaks of." }, { speaker: "Eldrin", side: "left", text: "The shadows grow longer in Mistbane Forest. The ancient enchantments are fading. We need your help to restore the balance." }, { speaker: "Lyra", side: "right", text: "What say you, traveler? Will you aid us in this quest? The fate of Mystwood may depend upon your decision." }, { type: "options" } ]; const responses = { magic: [ { speaker: "Lyra", side: "right", text: "The Crystal of Azura holds the essence of five elemental spirits. It once protected our realm from dark enchantments." }, { speaker: "Eldrin", side: "left", text: "But the crystal has been shattered, its fragments scattered throughout Mistbane Forest. Without it, our magical barriers weaken." }, { speaker: "Lyra", side: "right", text: "You possess a rare affinity for the crystal's magic. You could help us restore it to its former glory!" } ], quest: [ { speaker: "Eldrin", side: "left", text: "The quest requires venturing into the heart of Mistbane, where the ancient tree Yggdralith stands. Few return from its depths unchanged." }, { speaker: "Lyra", side: "right", text: "You must gather the crystal fragments while overcoming the forest's trials. The spirits will test your courage and wisdom." }, { speaker: "Eldrin", side: "left", text: "We shall provide you with an enchanted map and a moonlight lantern. They will guide you when all other lights fail." } ], decline: [ { speaker: "Eldrin", side: "left", text: "I sense hesitation in your heart. The forest's paths are indeed perilous, and none should walk them unprepared." }, { speaker: "Lyra", side: "right", text: "Perhaps you require time to consider. The pendant has chosen you, but the choice to accept remains yours alone." }, { speaker: "Eldrin", side: "left", text: "Rest at our village tonight. Commune with the ancient spirits in your dreams. Perhaps morning will bring clarity to your decision." } ] }; let currentDialogueIndex = 0; let currentResponsePath = null; function createParticles(element) { const particlesContainer = document.createElement('div'); particlesContainer.classList.add('particles'); element.appendChild(particlesContainer); for (let i = 0; i < 15; i++) { const particle = document.createElement('div'); particle.classList.add('particle'); // Random positions and movements const x = Math.random() * 100; const y = Math.random() * 100; const tx = (Math.random() - 0.5) * 150; const ty = (Math.random() - 0.5) * 150; particle.style.left = `${x}%`; particle.style.top = `${y}%`; particle.style.setProperty('--tx', `${tx}px`); particle.style.setProperty('--ty', `${ty}px`); particle.style.animationDelay = `${Math.random() * 0.5}s`; // Random colors const colors = [ 'rgba(255, 215, 132, 0.8)', // gold 'rgba(173, 216, 230, 0.8)', // light blue 'rgba(144, 238, 144, 0.8)', // light green 'rgba(221, 160, 221, 0.8)' // plum ]; particle.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)]; particlesContainer.appendChild(particle); } // Remove particles after animation completes setTimeout(() => { if (particlesContainer.parentNode === element) { element.removeChild(particlesContainer); } }, 2500); } function showDialogue(dialogueData) { // Clear previous dialogue const existingBubble = dialogueContainer.querySelector('.dialogue-bubble'); if (existingBubble) { existingBubble.classList.remove('active'); setTimeout(() => { dialogueContainer.innerHTML = ''; createDialogueBubble(dialogueData); }, 300); } else { createDialogueBubble(dialogueData); } } function createDialogueBubble(dialogueData) { const bubble = document.createElement('div'); bubble.classList.add('dialogue-bubble'); bubble.classList.add(`dialogue-${dialogueData.side}`); bubble.classList.add(`bubble-${dialogueData.side}`); const bubbleBg = document.createElement('div'); bubbleBg.classList.add('bubble-bg'); const bubblePointer = document.createElement('div'); bubblePointer.classList.add('bubble-pointer'); const content = document.createElement('div'); content.classList.add('bubble-content'); const name = document.createElement('div'); name.classList.add('character-name'); name.textContent = dialogueData.speaker; const text = document.createElement('div'); text.textContent = dialogueData.text; content.appendChild(name); content.appendChild(text); bubble.appendChild(bubbleBg); bubble.appendChild(bubblePointer); bubble.appendChild(content); dialogueContainer.appendChild(bubble); // Animate characters if (dialogueData.side === 'left') { character1.style.transform = 'translateY(-10px)'; character2.style.transform = 'translateY(0)'; } else { character2.style.transform = 'translateY(-10px)'; character1.style.transform = 'translateY(0)'; } // Show with animation and particles setTimeout(() => { bubble.classList.add('active'); createParticles(bubble); }, 50); } function showDialogueOptions() { optionsContainer.classList.add('show'); } function hideDialogueOptions() { optionsContainer.classList.remove('show'); } function nextDialogue() { if (currentResponsePath) { // We're in a response path const currentResponse = responses[currentResponsePath]; if (currentDialogueIndex < currentResponse.length) { showDialogue(currentResponse[currentDialogueIndex]); currentDialogueIndex++; } else { // End of responses, reset restartDialogue(); } } else { // Main dialogue path if (currentDialogueIndex < dialogues.length) { const currentDialogue = dialogues[currentDialogueIndex]; if (currentDialogue.type === 'options') { showDialogueOptions(); } else { showDialogue(currentDialogue); } currentDialogueIndex++; } else { // End of main dialogue, show options if not already shown if (!optionsContainer.classList.contains('show')) { showDialogueOptions(); } } } } function restartDialogue() { dialogueContainer.innerHTML = ''; currentDialogueIndex = 0; currentResponsePath = null; hideDialogueOptions(); // Reset character positions character1.style.transform = 'translateY(0)'; character2.style.transform = 'translateY(0)'; // Start first dialogue after a small delay setTimeout(nextDialogue, 500); } // Event listeners nextBtn.addEventListener('click', nextDialogue); restartBtn.addEventListener('click', restartDialogue); dialogueOptions.forEach(option => { option.addEventListener('click', function() { const responseType = this.getAttribute('data-response'); currentResponsePath = responseType; currentDialogueIndex = 0; hideDialogueOptions(); nextDialogue(); }); }); // Start the dialogue nextDialogue(); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Comic Book Chat</title> <style> @import url('https://fonts.googleapis.com/css2?family=Bangers&family=Comic+Neue:wght@700&display=swap'); * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: 'Comic Neue', cursive; background-color: #f0f0f0; background-image: radial-gradient(circle, #ffeb3b 1px, transparent 1px), radial-gradient(circle, #ff5722 1px, transparent 1px); background-size: 30px 30px, 40px 40px; background-position: 0 0, 15px 15px; height: 100vh; display: flex; flex-direction: column; padding: 15px; max-width: 700px; margin: 0 auto; overflow: hidden; } .chat-container { flex: 1; overflow-y: auto; padding: 10px; display: flex; flex-direction: column; gap: 15px; max-height: 580px; } .chat-header { text-align: center; padding: 10px; margin-bottom: 15px; position: relative; } .chat-header h1 { font-family: 'Bangers', cursive; font-size: 2.5rem; color: #e91e63; text-shadow: 3px 3px 0 #673ab7; letter-spacing: 2px; transform: rotate(-2deg); margin: 0; } .header-decoration { position: absolute; font-size: 1.5rem; color: #ffeb3b; text-shadow: 1px 1px 0 #ff5722; } .decoration-1 { top: 5px; left: 20px; transform: rotate(-15deg); } .decoration-2 { top: 5px; right: 20px; transform: rotate(15deg); } .speech-bubble { position: relative; padding: 15px 20px; border-radius: 10px; max-width: 80%; font-size: 1rem; line-height: 1.4; cursor: pointer; transition: transform 0.2s ease; animation: pop-in 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); transform-origin: center; opacity: 0; animation-fill-mode: forwards; } .speech-bubble.left { align-self: flex-start; background-color: #8bc34a; color: #1a1a1a; clip-path: polygon( 0% 0%, 100% 0%, 100% 75%, 90% 75%, 80% 100%, 75% 75%, 0% 75% ); padding-bottom: 25px; } .speech-bubble.right { align-self: flex-end; background-color: #03a9f4; color: #1a1a1a; clip-path: polygon( 0% 0%, 100% 0%, 100% 75%, 25% 75%, 20% 100%, 10% 75%, 0% 75% ); padding-bottom: 25px; } .thought-bubble { align-self: center; background-color: #f9f9f9; border-radius: 50%; position: relative; padding: 20px; filter: drop-shadow(0 4px 6px rgba(0,0,0,0.1)); } .thought-bubble::before, .thought-bubble::after { content: ''; position: absolute; background-color: #f9f9f9; border-radius: 50%; } .thought-bubble::before { width: 20px; height: 20px; bottom: -15px; right: 40px; } .thought-bubble::after { width: 10px; height: 10px; bottom: -25px; right: 30px; } .action-bubble { align-self: center; background-color: #ff9800; transform: rotate(-2deg); padding: 10px 25px; font-family: 'Bangers', cursive; font-size: 1.3rem; letter-spacing: 1px; clip-path: polygon( 0% 0%, 5% 10%, 15% 5%, 25% 0%, 85% 0%, 95% 10%, 100% 20%, 95% 30%, 100% 40%, 95% 50%, 100% 60%, 95% 70%, 100% 80%, 95% 90%, 85% 100%, 15% 100%, 5% 90%, 0% 80%, 5% 70%, 0% 60%, 5% 50%, 0% 40%, 5% 30%, 0% 20%, 5% 10% ); } .narration-bubble { align-self: center; background-color: #9e9e9e; color: white; font-style: italic; transform: rotate(1deg); clip-path: polygon( 0% 5%, 10% 0%, 90% 0%, 100% 5%, 95% 95%, 90% 100%, 10% 100%, 0% 95% ); } .sound-effect { font-family: 'Bangers', cursive; font-size: 2rem; color: transparent; -webkit-text-stroke: 1px #f44336; letter-spacing: 2px; text-shadow: 3px 3px 0 #ff9800; align-self: center; transform: rotate(-5deg) scale(1.2); animation: pulse 1s infinite alternate; } @keyframes pulse { 0% { transform: rotate(-5deg) scale(1.2); } 100% { transform: rotate(-5deg) scale(1.3); } } @keyframes pop-in { 0% { transform: scale(0); opacity: 0; } 70% { transform: scale(1.1); opacity: 1; } 100% { transform: scale(1); opacity: 1; } } .speech-bubble:hover, .thought-bubble:hover, .action-bubble:hover, .narration-bubble:hover { transform: scale(1.05); z-index: 10; box-shadow: 0 5px 15px rgba(0,0,0,0.2); } .speech-bubble:active, .thought-bubble:active, .action-bubble:active, .narration-bubble:active { transform: scale(0.95); } .bubble-name { font-weight: bold; margin-bottom: 8px; color: #1a1a1a; text-transform: uppercase; font-size: 0.9rem; } .chat-input-container { display: flex; padding: 15px 10px; gap: 10px; background-color: white; border-radius: 10px; margin-top: 10px; position: relative; box-shadow: 0 -2px 10px rgba(0,0,0,0.1); } .chat-input { flex: 1; padding: 12px 15px; border: 2px solid #673ab7; border-radius: 8px; font-family: 'Comic Neue', cursive; font-size: 1rem; outline: none; transition: border-color 0.3s; } .chat-input:focus { border-color: #e91e63; } .send-btn { background-color: #673ab7; color: white; border: none; border-radius: 8px; padding: 8px 20px; font-family: 'Bangers', cursive; font-size: 1.1rem; cursor: pointer; transition: background-color 0.3s, transform 0.2s; letter-spacing: 1px; position: relative; overflow: hidden; } .send-btn:hover { background-color: #e91e63; transform: scale(1.05); } .send-btn:active { transform: scale(0.95); } .send-btn::after { content: ''; position: absolute; top: -50%; left: -50%; width: 200%; height: 200%; background: rgba(255,255,255,0.2); transform: rotate(45deg); transition: all 0.3s; opacity: 0; } .send-btn:hover::after { opacity: 1; transform: rotate(45deg) translate(0, 100%); } .bubble-burst { position: absolute; pointer-events: none; z-index: 100; transform: translate(-50%, -50%); } .burst-ray { position: absolute; background-color: #ffeb3b; height: 2px; width: 20px; transform-origin: left center; } .style-selector { display: flex; gap: 10px; margin-bottom: 10px; overflow-x: auto; padding: 5px 0; -ms-overflow-style: none; scrollbar-width: none; } .style-selector::-webkit-scrollbar { display: none; } .style-btn { border: none; background-color: #673ab7; color: white; padding: 5px 10px; border-radius: 5px; font-family: 'Comic Neue', cursive; cursor: pointer; transition: all 0.2s; white-space: nowrap; font-size: 0.8rem; } .style-btn:hover { background-color: #e91e63; transform: translateY(-2px); } .style-btn.active { background-color: #e91e63; box-shadow: 0 0 0 2px white, 0 0 0 4px #e91e63; } @media (max-width: 480px) { .chat-header h1 { font-size: 2rem; } .speech-bubble { max-width: 85%; font-size: 0.9rem; } .send-btn { padding: 8px 15px; font-size: 0.9rem; } .style-btn { padding: 4px 8px; font-size: 0.7rem; } } /* Star shape burst panels */ .star-burst { position: absolute; background-color: #ffd600; width: 70px; height: 70px; z-index: -1; transform: rotate(22.5deg); } .star-burst:before, .star-burst:after { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: #ffd600; } .star-burst:before { transform: rotate(45deg); } .star-burst:after { transform: rotate(90deg); } .chat-header .star-burst { position: absolute; width: 100px; height: 100px; top: -20px; left: 50%; transform: translateX(-50%) rotate(22.5deg); z-index: -1; } </style> </head> <body> <div class="chat-header"> <div class="star-burst"></div> <span class="header-decoration decoration-1">POW!</span> <h1>Comic Chat</h1> <span class="header-decoration decoration-2">ZAP!</span> </div> <div class="style-selector"> <button class="style-btn active" data-style="left">Hero Speech</button> <button class="style-btn" data-style="right">Villain Speech</button> <button class="style-btn" data-style="thought">Thought</button> <button class="style-btn" data-style="action">Action</button> <button class="style-btn" data-style="narration">Narration</button> <button class="style-btn" data-style="sound">Sound Effect</button> </div> <div class="chat-container" id="chatContainer"> <div class="speech-bubble left" style="animation-delay: 0.1s;"> <div class="bubble-name">Captain Awesome</div> Not so fast, Dr. Mayhem! Your dastardly plans end here! </div> <div class="speech-bubble right" style="animation-delay: 0.3s;"> <div class="bubble-name">Dr. Mayhem</div> You're too late, Captain! My cosmic ray is already powering up! </div> <div class="thought-bubble speech-bubble" style="animation-delay: 0.5s;"> <div class="bubble-name">Captain Awesome</div> I need to find a way to disable that machine before it's fully charged... </div> <div class="sound-effect speech-bubble" style="animation-delay: 0.7s;"> KABOOM! </div> <div class="action-bubble speech-bubble" style="animation-delay: 0.9s;"> Captain leaps toward the control panel! </div> <div class="narration-bubble speech-bubble" style="animation-delay: 1.1s;"> Meanwhile, across town, the city remains unaware of the danger looming above... </div> </div> <div class="chat-input-container"> <input type="text" class="chat-input" placeholder="Type your message here..." id="chatInput"> <button class="send-btn" id="sendButton">SEND!</button> </div> <script> document.addEventListener('DOMContentLoaded', function() { const chatContainer = document.getElementById('chatContainer'); const chatInput = document.getElementById('chatInput'); const sendButton = document.getElementById('sendButton'); const styleButtons = document.querySelectorAll('.style-btn'); let currentStyle = 'left'; let animationDelay = 1.3; // Set active style styleButtons.forEach(button => { button.addEventListener('click', function() { styleButtons.forEach(btn => btn.classList.remove('active')); this.classList.add('active'); currentStyle = this.getAttribute('data-style'); }); }); // Send message function function sendMessage() { const message = chatInput.value.trim(); if (!message) return; // Create bubble based on style const bubble = document.createElement('div'); bubble.classList.add('speech-bubble'); switch(currentStyle) { case 'left': bubble.classList.add('left'); bubble.innerHTML = `<div class="bubble-name">Captain Awesome</div>${message}`; break; case 'right': bubble.classList.add('right'); bubble.innerHTML = `<div class="bubble-name">Dr. Mayhem</div>${message}`; break; case 'thought': bubble.classList.add('thought-bubble'); bubble.innerHTML = `<div class="bubble-name">Thoughts</div>${message}`; break; case 'action': bubble.classList.add('action-bubble'); bubble.textContent = message; break; case 'narration': bubble.classList.add('narration-bubble'); bubble.textContent = message; break; case 'sound': bubble.classList.add('sound-effect'); bubble.textContent = message; break; } animationDelay += 0.2; bubble.style.animationDelay = `${animationDelay}s`; chatContainer.appendChild(bubble); chatInput.value = ''; // Scroll to bottom chatContainer.scrollTop = chatContainer.scrollHeight; } // Event listeners sendButton.addEventListener('click', sendMessage); chatInput.addEventListener('keydown', function(e) { if (e.key === 'Enter') { sendMessage(); } }); // Create burst effect on bubble click chatContainer.addEventListener('click', function(e) { if (e.target.closest('.speech-bubble')) { const bubble = e.target.closest('.speech-bubble'); createBurst(e.clientX, e.clientY); // Add pop animation bubble.style.animation = 'none'; void bubble.offsetWidth; // Force reflow bubble.style.animation = 'pop-in 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275)'; } }); // Create burst effect function createBurst(x, y) { const burst = document.createElement('div'); burst.classList.add('bubble-burst'); burst.style.left = x + 'px'; burst.style.top = y + 'px'; // Create rays const rayCount = 8; for (let i = 0; i < rayCount; i++) { const ray = document.createElement('div'); ray.classList.add('burst-ray'); ray.style.transform = `rotate(${i * (360 / rayCount)}deg)`; // Random color const colors = ['#e91e63', '#ff9800', '#ffeb3b', '#8bc34a', '#03a9f4']; ray.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)]; burst.appendChild(ray); } document.body.appendChild(burst); // Animate and remove setTimeout(function() { const rays = burst.querySelectorAll('.burst-ray'); rays.forEach(ray => { ray.style.width = '40px'; ray.style.opacity = '0'; ray.style.transition = 'all 0.3s ease-out'; }); setTimeout(function() { document.body.removeChild(burst); }, 300); }, 10); } }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>E-Learning Conversation UI</title> <style> :root { --primary: #a1dbf5; --secondary: #ffcad4; --tertiary: #c8e7ff; --quaternary: #d4f0c6; --correct: #9aecb3; --hint: #ffe599; --text: #4a5568; --bubble-radius: 22px; } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Comic Neue', 'Quicksand', 'Arial Rounded MT Bold', sans-serif; } body { background-color: #f9f9ff; color: var(--text); display: flex; flex-direction: column; align-items: center; justify-content: flex-start; height: 100vh; padding: 20px; overflow-x: hidden; max-width: 700px; margin: 0 auto; } .chat-container { width: 100%; max-width: 650px; height: 570px; background-color: white; border-radius: 25px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.06); display: flex; flex-direction: column; overflow: hidden; position: relative; } .chat-header { background: linear-gradient(135deg, var(--primary), var(--tertiary)); padding: 18px 20px; display: flex; align-items: center; justify-content: space-between; border-radius: 25px 25px 0 0; z-index: 10; } .chat-header h1 { font-size: 1.4rem; font-weight: 700; color: #4a5568; } .progress-container { height: 5px; width: 100px; background-color: rgba(255, 255, 255, 0.5); border-radius: 10px; overflow: hidden; } .progress-bar { height: 100%; width: 30%; background-color: white; border-radius: 10px; transition: width 0.5s ease; } .chat-messages { flex: 1; overflow-y: auto; padding: 20px; display: flex; flex-direction: column; gap: 15px; scroll-behavior: smooth; } .message { max-width: 80%; padding: 16px 20px; border-radius: var(--bubble-radius); line-height: 1.5; position: relative; font-size: 0.95rem; opacity: 0; transform: translateY(20px); animation: fadeIn 0.5s forwards; } .message.tutor { align-self: flex-start; background-color: var(--primary); border-bottom-left-radius: 5px; } .message.tutor::before { content: ''; position: absolute; bottom: 0; left: -10px; width: 20px; height: 20px; background-color: var(--primary); border-bottom-right-radius: 20px; z-index: -1; } .message.user { align-self: flex-end; background-color: var(--secondary); border-bottom-right-radius: 5px; } .message.user::before { content: ''; position: absolute; bottom: 0; right: -10px; width: 20px; height: 20px; background-color: var(--secondary); border-bottom-left-radius: 20px; z-index: -1; } .option-message { background-color: var(--tertiary); padding: 15px; border-radius: 15px; display: flex; flex-direction: column; gap: 10px; max-width: 90%; align-self: flex-start; opacity: 0; transform: translateY(20px); animation: fadeIn 0.5s forwards; } .option-button { background-color: white; border: none; border-radius: 15px; padding: 12px 15px; text-align: left; font-size: 0.95rem; cursor: pointer; transition: all 0.3s ease; color: var(--text); box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); position: relative; overflow: hidden; } .option-button::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(120deg, transparent, rgba(255, 255, 255, 0.3), transparent); transform: translateX(-100%); transition: 0.6s; } .option-button:hover { transform: translateY(-3px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08); } .option-button:hover::before { transform: translateX(100%); } .option-button.correct { background-color: var(--correct); transform: scale(1.05); } .option-button.incorrect { background-color: #ffcccb; opacity: 0.7; } .message.hint { background-color: var(--hint); align-self: center; font-style: italic; max-width: 90%; font-size: 0.9rem; transform: rotate(-1deg); box-shadow: 0 3px 10px rgba(0, 0, 0, 0.05); } .emoji { display: inline-block; transform: scale(1); transition: transform 0.3s ease; margin: 0 2px; } .emoji:hover { transform: scale(1.5); display: inline-block; } .chat-input { padding: 15px 20px; background-color: #f5f7fa; display: flex; align-items: center; border-top: 1px solid #edf2f7; z-index: 10; } .message-input { flex: 1; padding: 12px 20px; border: none; border-radius: 30px; font-size: 0.95rem; background-color: white; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05); } .message-input:focus { outline: none; box-shadow: 0 0 0 2px var(--primary); } .send-button { width: 45px; height: 45px; border-radius: 50%; background-color: var(--primary); border: none; margin-left: 10px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.3s ease; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); } .send-button:hover { transform: scale(1.05); background-color: #8fd1f3; } .send-button svg { width: 20px; height: 20px; fill: white; } .continue-button { background-color: var(--quaternary); border: none; border-radius: 20px; padding: 10px 20px; align-self: center; margin-top: 10px; cursor: pointer; font-weight: 600; transition: all 0.3s ease; opacity: 0; transform: translateY(20px); animation: fadeIn 0.5s forwards 0.3s; display: flex; align-items: center; gap: 8px; } .continue-button:hover { transform: translateY(-3px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); } .feedback-animation { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0); font-size: 5rem; z-index: 100; user-select: none; pointer-events: none; opacity: 0; transition: transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity 0.5s ease; } .feedback-animation.show { transform: translate(-50%, -50%) scale(1); opacity: 1; } .typing-indicator { display: flex; align-items: center; gap: 5px; padding: 10px 20px; border-radius: 20px; background-color: var(--primary); align-self: flex-start; margin-top: 5px; opacity: 0; } .typing-indicator.show { opacity: 1; animation: fadeIn 0.3s forwards; } .typing-dot { width: 8px; height: 8px; border-radius: 50%; background-color: rgba(255, 255, 255, 0.7); animation: typingAnimation 1.4s infinite; } .typing-dot:nth-child(2) { animation-delay: 0.2s; } .typing-dot:nth-child(3) { animation-delay: 0.4s; } @keyframes typingAnimation { 0%, 60%, 100% { transform: translateY(0); } 30% { transform: translateY(-8px); } } @keyframes fadeIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } @keyframes sparkle { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.5); opacity: 0.7; } } .sparkle { animation: sparkle 1s ease-in-out; } .avatar { width: 28px; height: 28px; border-radius: 50%; background-color: #d1f0ea; display: flex; align-items: center; justify-content: center; margin-right: 10px; font-weight: bold; font-size: 16px; color: #45818e; } .user-info { display: flex; align-items: center; } .floating-particles { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 0; } .particle { position: absolute; background-color: rgba(161, 219, 245, 0.3); border-radius: 50%; pointer-events: none; opacity: 0.5; } @media (max-width: 600px) { .chat-container { height: 510px; width: 100%; } .message { max-width: 85%; font-size: 0.9rem; padding: 12px 16px; } .chat-header h1 { font-size: 1.2rem; } } /* Custom scrollbar */ .chat-messages::-webkit-scrollbar { width: 8px; } .chat-messages::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 10px; } .chat-messages::-webkit-scrollbar-thumb { background: #d3d3d3; border-radius: 10px; } .chat-messages::-webkit-scrollbar-thumb:hover { background: #c1c1c1; } /* Checkmark animation */ .checkmark { width: 20px; height: 20px; position: relative; display: inline-block; transform: rotate(45deg); margin-left: 5px; } .checkmark-stem { position: absolute; width: 3px; height: 10px; background-color: #4CAF50; left: 10px; top: 5px; } .checkmark-kick { position: absolute; width: 7px; height: 3px; background-color: #4CAF50; left: 4px; top: 12px; } @keyframes grow-checkmark { 0% { transform: scale(0); } 50% { transform: scale(1.1); } 100% { transform: scale(1); } } .grow-checkmark { animation: grow-checkmark 0.5s ease-in-out; } .message.interactive { cursor: pointer; transition: transform 0.2s ease; } .message.interactive:hover { transform: scale(1.02); } </style> </head> <body> <div class="chat-container"> <div class="chat-header"> <div class="user-info"> <div class="avatar">T</div> <h1>Photosynthesis Tutorial</h1> </div> <div class="progress-container"> <div class="progress-bar" id="progressBar"></div> </div> </div> <div class="chat-messages" id="chatMessages"> <!-- Messages will be added here by JavaScript --> </div> <div class="chat-input"> <input type="text" class="message-input" id="messageInput" placeholder="Type your message here..." aria-label="Message input"> <button class="send-button" id="sendButton" aria-label="Send message"> <svg viewBox="0 0 24 24"> <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"></path> </svg> </button> </div> <div class="floating-particles" id="particles"></div> </div> <div class="feedback-animation" id="feedbackAnimation"></div> <script> document.addEventListener('DOMContentLoaded', function() { // DOM elements const chatMessages = document.getElementById('chatMessages'); const messageInput = document.getElementById('messageInput'); const sendButton = document.getElementById('sendButton'); const progressBar = document.getElementById('progressBar'); const feedbackAnimation = document.getElementById('feedbackAnimation'); const particlesContainer = document.getElementById('particles'); // Animation delay times const TYPING_DELAY = 1000; const MESSAGE_DELAY = 1000; // Conversation flow let currentStep = 0; const progress = [0, 20, 40, 60, 80, 100]; // Tutorial conversation const tutorialFlow = [ { type: 'tutor', content: 'Welcome to our Photosynthesis tutorial! Let\'s explore how plants convert sunlight into energy. <span class="emoji">ðą</span> Ready to dive in?' }, { type: 'options', question: 'What is the primary purpose of photosynthesis?', options: [ { text: 'To generate oxygen for humans', correct: false }, { text: 'To reproduce and create more plants', correct: false }, { text: 'To convert light energy into chemical energy', correct: true }, { text: 'To absorb carbon dioxide from the atmosphere', correct: false } ], feedback: { correct: 'Absolutely right! Plants convert light energy from the sun into chemical energy that can be used as fuel.', incorrect: 'Not quite. While those are related to plant processes, the primary purpose of photosynthesis is to convert light energy into chemical energy.' } }, { type: 'tutor', content: 'For photosynthesis to occur, plants need several key ingredients. <span class="emoji">âïļ</span> <span class="emoji">ð§</span> <span class="emoji">ðŦïļ</span> Let\'s identify them!' }, { type: 'interactive', content: 'Tap on this bubble to reveal the essential ingredients for photosynthesis.', reveal: 'Plants need: Sunlight (captured by chlorophyll), Water (absorbed through roots), and Carbon Dioxide (taken from the air). Amazing how they combine these simple elements!' }, { type: 'options', question: 'Which organelle is the primary site of photosynthesis?', options: [ { text: 'Mitochondria', correct: false }, { text: 'Nucleus', correct: false }, { text: 'Chloroplast', correct: true }, { text: 'Ribosome', correct: false } ], feedback: { correct: 'That\'s right! Chloroplasts contain chlorophyll, the green pigment that captures sunlight for photosynthesis.', incorrect: 'Not correct. Photosynthesis primarily occurs in chloroplasts, which contain the green pigment chlorophyll.' }, hint: 'Think about what gives plants their green color!' }, { type: 'tutor', content: 'Great job! <span class="emoji">ð</span> The photosynthesis equation is: 6COâ + 6HâO + Light Energy â CâHââOâ + 6Oâ' }, { type: 'tutor', content: 'You\'ve completed the basic photosynthesis tutorial! Ready to move on to the light-dependent and light-independent reactions?' } ]; // Create particles for background effect function createParticles() { for (let i = 0; i < 20; i++) { const particle = document.createElement('div'); particle.classList.add('particle'); // Random position const posX = Math.random() * 100; const posY = Math.random() * 100; // Random size const size = Math.random() * 15 + 5; // Random opacity const opacity = Math.random() * 0.3 + 0.1; // Apply styles particle.style.width = `${size}px`; particle.style.height = `${size}px`; particle.style.left = `${posX}%`; particle.style.top = `${posY}%`; particle.style.opacity = opacity; // Random pastel color const colors = ['rgba(161, 219, 245, 0.3)', 'rgba(255, 202, 212, 0.3)', 'rgba(200, 231, 255, 0.3)', 'rgba(212, 240, 198, 0.3)']; particle.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)]; particlesContainer.appendChild(particle); // Animate randomly animateParticle(particle); } } function animateParticle(particle) { const duration = Math.random() * 20000 + 10000; // 10-30 seconds let startX = parseFloat(particle.style.left); let startY = parseFloat(particle.style.top); // Random destination const destX = Math.random() * 100; const destY = Math.random() * 100; let startTime = null; function moveParticle(timestamp) { if (!startTime) startTime = timestamp; const elapsed = timestamp - startTime; const progress = elapsed / duration; if (progress < 1) { const currentX = startX + (destX - startX) * progress; const currentY = startY + (destY - startY) * progress; particle.style.left = `${currentX}%`; particle.style.top = `${currentY}%`; requestAnimationFrame(moveParticle); } else { // Reset for the next animation startX = destX; startY = destY; startTime = null; // Continue animation with new destination animateParticle(particle); } } requestAnimationFrame(moveParticle); } // Show typing indicator function showTypingIndicator() { const typingIndicator = document.createElement('div'); typingIndicator.className = 'typing-indicator'; typingIndicator.id = 'typingIndicator'; for (let i = 0; i < 3; i++) { const dot = document.createElement('div'); dot.className = 'typing-dot'; typingIndicator.appendChild(dot); } chatMessages.appendChild(typingIndicator); chatMessages.scrollTop = chatMessages.scrollHeight; setTimeout(() => { typingIndicator.classList.add('show'); }, 100); } // Hide typing indicator function hideTypingIndicator() { const typingIndicator = document.getElementById('typingIndicator'); if (typingIndicator) { typingIndicator.remove(); } } // Add message to chat function addMessage(type, content, additionalClass = '', interactive = false) { hideTypingIndicator(); const messageDiv = document.createElement('div'); messageDiv.className = `message ${type} ${additionalClass}`; messageDiv.innerHTML = content; if (interactive) { messageDiv.classList.add('interactive'); messageDiv.addEventListener('click', function() { handleInteractiveMessage(messageDiv, type); }); } chatMessages.appendChild(messageDiv); chatMessages.scrollTop = chatMessages.scrollHeight; return messageDiv; } // Handle interactive messages function handleInteractiveMessage(messageElement, step) { const currentStep = tutorialFlow[currentStep - 1]; if (currentStep && currentStep.type === 'interactive') { messageElement.innerHTML = currentStep.reveal; messageElement.classList.remove('interactive'); // Add subtle animation messageElement.classList.add('sparkle'); setTimeout(() => { messageElement.classList.remove('sparkle'); }, 1000); // Continue the flow after interaction setTimeout(() => { advanceTutorial(); }, 2000); } } // Add options message function addOptionsMessage(question, options) { hideTypingIndicator(); const optionsDiv = document.createElement('div'); optionsDiv.className = 'option-message'; const questionPara = document.createElement('p'); questionPara.textContent = question; optionsDiv.appendChild(questionPara); options.forEach((option, index) => { const button = document.createElement('button'); button.className = 'option-button'; button.textContent = option.text; button.dataset.correct = option.correct; button.dataset.index = index; button.addEventListener('click', function() { handleOptionClick(this, options); }); optionsDiv.appendChild(button); }); chatMessages.appendChild(optionsDiv); chatMessages.scrollTop = chatMessages.scrollHeight; } // Handle option click function handleOptionClick(button, options) { const isCorrect = button.dataset.correct === 'true'; const currentStepData = tutorialFlow[currentStep - 1]; // Disable all buttons const allButtons = button.parentElement.querySelectorAll('.option-button'); allButtons.forEach(btn => { btn.disabled = true; // Show which answers were correct/incorrect if (btn.dataset.correct === 'true') { btn.classList.add('correct'); } else if (btn === button && !isCorrect) { btn.classList.add('incorrect'); } }); // Show feedback animation if (isCorrect) { showFeedbackAnimation('â'); progressBar.style.width = `${progress[currentStep]}%`; } else { showFeedbackAnimation('â'); // Show hint if available if (currentStepData.hint) { setTimeout(() => { addMessage('hint', currentStepData.hint); }, 1000); } } // Add feedback message setTimeout(() => { const feedback = isCorrect ? currentStepData.feedback.correct : currentStepData.feedback.incorrect; addMessage('tutor', feedback); // Continue after feedback setTimeout(() => { // Add continue button if incorrect if (!isCorrect) { const continueButton = document.createElement('button'); continueButton.className = 'continue-button'; continueButton.innerHTML = 'Continue <div class="checkmark"><div class="checkmark-stem"></div><div class="checkmark-kick"></div></div>'; continueButton.addEventListener('click', function() { this.remove(); advanceTutorial(); }); chatMessages.appendChild(continueButton); chatMessages.scrollTop = chatMessages.scrollHeight; } else { advanceTutorial(); } }, 1500); }, 1000); } // Show feedback animation function showFeedbackAnimation(symbol) { feedbackAnimation.textContent = symbol; feedbackAnimation.className = 'feedback-animation'; // Force reflow void feedbackAnimation.offsetWidth; feedbackAnimation.classList.add('show'); setTimeout(() => { feedbackAnimation.classList.remove('show'); }, 1000); } // Advance to next tutorial step function advanceTutorial() { if (currentStep < tutorialFlow.length) { const step = tutorialFlow[currentStep]; currentStep++; // Update progress bar progressBar.style.width = `${progress[currentStep-1]}%`; // Show typing indicator before tutor messages if (step.type === 'tutor' || step.type === 'options' || step.type === 'interactive') { showTypingIndicator(); } // Delay to simulate typing setTimeout(() => { if (step.type === 'tutor') { addMessage('tutor', step.content); // Continue after tutor message setTimeout(advanceTutorial, MESSAGE_DELAY); } else if (step.type === 'options') { addOptionsMessage(step.question, step.options); } else if (step.type === 'interactive') { addMessage('tutor', step.content, '', true); } }, TYPING_DELAY); } } // Start tutorial function startTutorial() { createParticles(); advanceTutorial(); } // Send button functionality sendButton.addEventListener('click', function() { const message = messageInput.value.trim(); if (message) { addMessage('user', message); messageInput.value = ''; // Wait and respond setTimeout(() => { showTypingIndicator(); setTimeout(() => { addMessage('tutor', 'Thanks for your message! Please use the options provided to continue with the tutorial.'); }, TYPING_DELAY); }, 500); } }); // Allow Enter key to send messages messageInput.addEventListener('keypress', function(e) { if (e.key === 'Enter') { sendButton.click(); e.preventDefault(); } }); // Start the tutorial startTutorial(); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { width: 700px; height: 700px; overflow: hidden; background: linear-gradient(135deg, #080818, #101035); display: flex; justify-content: center; align-items: center; perspective: 1000px; } .ar-overlay { position: relative; width: 650px; height: 600px; display: flex; flex-direction: column; justify-content: center; overflow: hidden; } .scene { position: absolute; width: 100%; height: 100%; background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><rect width="100" height="100" fill="none" stroke="%23232356" stroke-width="0.5" stroke-dasharray="5,5"/></svg>'); transform-style: preserve-3d; perspective: 1000px; } .ar-persona { position: absolute; width: 150px; height: 150px; } .person { position: absolute; width: 100%; height: 100%; border-radius: 50%; background: rgba(40, 40, 70, 0.4); backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; border: 1px solid rgba(100, 100, 255, 0.3); overflow: hidden; box-shadow: 0 0 20px rgba(75, 0, 255, 0.15); cursor: pointer; transition: all 0.3s ease; } .person:hover { transform: scale(1.05); box-shadow: 0 0 25px rgba(75, 0, 255, 0.3); } .person-icon { width: 80%; height: 80%; background-size: cover; border-radius: 50%; } .person-1 { left: 10%; top: 30%; } .person-2 { left: 70%; top: 20%; } .person-3 { left: 50%; top: 60%; } .icon-1 { background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="50" cy="40" r="20" fill="%238A4FFF"/><path d="M50,100 Q20,80 20,60 L20,40 Q20,15 50,15 Q80,15 80,40 L80,60 Q80,80 50,100" fill="%234A2FBF"/></svg>'); } .icon-2 { background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="50" cy="40" r="20" fill="%23FF4F8A"/><path d="M50,100 Q20,80 20,60 L20,40 Q20,15 50,15 Q80,15 80,40 L80,60 Q80,80 50,100" fill="%23BF2F4A"/></svg>'); } .icon-3 { background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="50" cy="40" r="20" fill="%234FFF8A"/><path d="M50,100 Q20,80 20,60 L20,40 Q20,15 50,15 Q80,15 80,40 L80,60 Q80,80 50,100" fill="%232FBF4A"/></svg>'); } .bubble { position: absolute; min-width: 200px; max-width: 260px; padding: 15px; background: rgba(20, 20, 40, 0.6); backdrop-filter: blur(10px); border-radius: 16px; border: 1px solid rgba(100, 100, 255, 0.2); box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2), 0 0 5px rgba(75, 0, 255, 0.2), inset 0 0 15px rgba(100, 100, 255, 0.1); opacity: 0; transform: scale(0.95) translateY(20px); transition: all 0.3s cubic-bezier(0.17, 0.84, 0.44, 1); transform-style: preserve-3d; z-index: 2; } .bubble.active { opacity: 1; transform: scale(1) translateY(0); } .bubble-1 { left: 20%; top: 25%; border-left: 3px solid #8A4FFF; } .bubble-2 { left: 50%; top: 15%; border-left: 3px solid #FF4F8A; } .bubble-3 { left: 35%; top: 55%; border-left: 3px solid #4FFF8A; } .bubble::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 100%; background: linear-gradient(90deg, rgba(255, 255, 255, 0.03), rgba(255, 255, 255, 0)); border-radius: 16px; z-index: -1; } .bubble::after { content: ''; position: absolute; width: 20px; height: 20px; transform: rotate(45deg); background: inherit; border: inherit; border-width: 0 0 1px 1px; z-index: -1; } .bubble-1::after { left: -10px; top: 30px; border-color: #8A4FFF; } .bubble-2::after { left: -10px; top: 30px; border-color: #FF4F8A; } .bubble-3::after { left: -10px; top: 30px; border-color: #4FFF8A; } .bubble-header { display: flex; align-items: center; margin-bottom: 10px; } .user-icon { width: 28px; height: 28px; border-radius: 50%; margin-right: 10px; background-size: cover; } .user-name { font-weight: 600; font-size: 14px; color: #fff; text-shadow: 0 0 10px rgba(255, 255, 255, 0.3); } .time-stamp { margin-left: auto; font-size: 11px; color: rgba(255, 255, 255, 0.6); } .bubble-content { color: rgba(255, 255, 255, 0.9); font-size: 14px; line-height: 1.5; margin-bottom: 5px; } .bubble-footer { display: flex; justify-content: space-between; margin-top: 8px; font-size: 12px; } .bubble-actions { display: flex; gap: 12px; } .action { color: rgba(255, 255, 255, 0.6); cursor: pointer; transition: all 0.2s ease; display: flex; align-items: center; } .action:hover { color: rgba(255, 255, 255, 1); } .action svg { margin-right: 4px; width: 14px; height: 14px; } .new-message { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); width: 400px; background: rgba(30, 30, 60, 0.8); backdrop-filter: blur(10px); border-radius: 30px; padding: 15px 20px; display: flex; align-items: center; border: 1px solid rgba(100, 100, 255, 0.3); box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2), 0 0 15px rgba(75, 0, 255, 0.2); z-index: 10; transition: all 0.3s ease; } .new-message:hover { box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3), 0 0 20px rgba(75, 0, 255, 0.3); } .new-message input { flex: 1; background: transparent; border: none; outline: none; color: #fff; font-size: 14px; padding: 0 10px; } .new-message input::placeholder { color: rgba(255, 255, 255, 0.4); } .send-btn { width: 40px; height: 40px; background: linear-gradient(135deg, #8A4FFF, #4F8AFF); border-radius: 50%; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 0 15px rgba(75, 0, 255, 0.4); } .send-btn:hover { transform: scale(1.05); box-shadow: 0 0 20px rgba(75, 0, 255, 0.6); } .send-btn svg { width: 18px; height: 18px; fill: #fff; } .tools { position: absolute; right: 20px; top: 20px; display: flex; flex-direction: column; gap: 10px; z-index: 10; } .tool { width: 45px; height: 45px; background: rgba(30, 30, 60, 0.7); backdrop-filter: blur(5px); border-radius: 12px; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: all 0.2s ease; border: 1px solid rgba(100, 100, 255, 0.2); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); } .tool:hover { background: rgba(40, 40, 80, 0.8); transform: translateY(-2px); box-shadow: 0 7px 20px rgba(0, 0, 0, 0.3); } .tool svg { width: 22px; height: 22px; fill: rgba(255, 255, 255, 0.8); } .focus-indicator { position: absolute; border-radius: 50%; border: 2px dashed rgba(255, 255, 255, 0.4); pointer-events: none; opacity: 0; transition: opacity 0.3s ease; } .focus-indicator.active { opacity: 1; animation: pulse 2s infinite; } @keyframes pulse { 0% { transform: scale(1); border-color: rgba(255, 255, 255, 0.4); } 50% { transform: scale(1.1); border-color: rgba(255, 255, 255, 0.6); } 100% { transform: scale(1); border-color: rgba(255, 255, 255, 0.4); } } .orientation-hint { position: absolute; top: 20px; left: 50%; transform: translateX(-50%); background: rgba(20, 20, 40, 0.7); backdrop-filter: blur(5px); border-radius: 20px; padding: 8px 15px; font-size: 12px; color: rgba(255, 255, 255, 0.8); display: flex; align-items: center; gap: 8px; border: 1px solid rgba(100, 100, 255, 0.2); z-index: 10; opacity: 0; animation: fadeInOut 5s ease forwards; } @keyframes fadeInOut { 0%, 100% { opacity: 0; } 10%, 90% { opacity: 1; } } .gyro-icon { width: 16px; height: 16px; } .emoji-picker { position: absolute; bottom: 80px; left: 50%; transform: translateX(-50%); width: 300px; height: 0; overflow: hidden; background: rgba(30, 30, 60, 0.9); backdrop-filter: blur(10px); border-radius: 16px; border: 1px solid rgba(100, 100, 255, 0.3); box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3); transition: height 0.3s cubic-bezier(0.17, 0.84, 0.44, 1); z-index: 5; opacity: 0; } .emoji-picker.active { height: 160px; opacity: 1; } .emoji-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 10px; padding: 15px; } .emoji { font-size: 24px; cursor: pointer; display: flex; justify-content: center; align-items: center; transition: transform 0.2s ease; } .emoji:hover { transform: scale(1.2); } .neon-pulse { position: absolute; border-radius: inherit; opacity: 0; inset: 0; z-index: -1; } .bubble.active .neon-pulse { animation: neonPulse 2s ease-in-out infinite; } @keyframes neonPulse { 0%, 100% { box-shadow: 0 0 5px 0px rgba(120, 100, 255, 0.3); opacity: 0.3; } 50% { box-shadow: 0 0 15px 2px rgba(120, 100, 255, 0.6); opacity: 0.6; } } .notification { position: absolute; top: -8px; right: -8px; width: 20px; height: 20px; background: #FF4F8A; border-radius: 50%; display: flex; justify-content: center; align-items: center; color: white; font-size: 10px; font-weight: bold; box-shadow: 0 0 10px rgba(255, 79, 138, 0.6); animation: notificationPulse 2s infinite; } @keyframes notificationPulse { 0%, 100% { transform: scale(1); box-shadow: 0 0 10px rgba(255, 79, 138, 0.6); } 50% { transform: scale(1.1); box-shadow: 0 0 15px rgba(255, 79, 138, 0.8); } } .typing-indicator { display: inline-flex; align-items: center; gap: 3px; height: 14px; margin-top: 4px; } .typing-dot { width: 4px; height: 4px; background-color: rgba(255, 255, 255, 0.8); border-radius: 50%; opacity: 0.6; } .typing-dot:nth-child(1) { animation: typingAnimation 1.4s infinite 0s; } .typing-dot:nth-child(2) { animation: typingAnimation 1.4s infinite 0.2s; } .typing-dot:nth-child(3) { animation: typingAnimation 1.4s infinite 0.4s; } @keyframes typingAnimation { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-4px); } } .reaction-bubble { position: absolute; background: rgba(30, 30, 60, 0.8); backdrop-filter: blur(5px); border-radius: 20px; padding: 5px 10px; font-size: 11px; color: white; display: flex; align-items: center; gap: 4px; transform: scale(0); transition: transform 0.3s cubic-bezier(0.17, 0.84, 0.44, 1); box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2); } .reaction-emoji { font-size: 14px; } .reaction-count { font-weight: bold; } .reaction-bubble.active { transform: scale(1); } .location-indicator { position: absolute; width: 140px; left: 80%; top: 70%; background: rgba(30, 30, 60, 0.7); backdrop-filter: blur(5px); border-radius: 30px; padding: 8px 12px; display: flex; align-items: center; gap: 8px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); border: 1px solid rgba(100, 100, 255, 0.2); opacity: 0; transform: translateY(20px); transition: all 0.5s cubic-bezier(0.17, 0.84, 0.44, 1); } .location-indicator.active { opacity: 1; transform: translateY(0); } .location-icon { width: 24px; height: 24px; display: flex; justify-content: center; align-items: center; background: rgba(79, 138, 255, 0.2); border-radius: 50%; } .location-icon svg { width: 14px; height: 14px; fill: rgba(255, 255, 255, 0.9); } .location-text { font-size: 12px; color: rgba(255, 255, 255, 0.9); } .ripple { position: absolute; border-radius: 50%; background: rgba(120, 100, 255, 0.1); transform: scale(0); pointer-events: none; animation: rippleEffect 1s forwards; } @keyframes rippleEffect { 0% { transform: scale(0); opacity: 0.5; } 100% { transform: scale(1); opacity: 0; } } .voice-tooltip { position: absolute; background: rgba(30, 30, 60, 0.8); backdrop-filter: blur(8px); color: white; font-size: 12px; padding: 6px 12px; border-radius: 8px; pointer-events: none; opacity: 0; transform: translateY(10px); transition: all 0.3s ease; white-space: nowrap; z-index: 100; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2); border: 1px solid rgba(100, 100, 255, 0.3); } .emoji-btn { width: 40px; height: 40px; background: transparent; border-radius: 50%; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: all 0.2s ease; margin-right: 5px; } .emoji-btn:hover { background: rgba(100, 100, 255, 0.1); } .emoji-btn svg { width: 20px; height: 20px; fill: rgba(255, 255, 255, 0.6); } .voice-btn { width: 40px; height: 40px; background: transparent; border-radius: 50%; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: all 0.2s ease; margin-right: 5px; } .voice-btn:hover { background: rgba(100, 100, 255, 0.1); } .voice-btn svg { width: 20px; height: 20px; fill: rgba(255, 255, 255, 0.6); } .settings-menu { position: absolute; top: 75px; right: 20px; width: 180px; background: rgba(30, 30, 60, 0.9); backdrop-filter: blur(10px); border-radius: 12px; overflow: hidden; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3), 0 0 10px rgba(75, 0, 255, 0.2); transform: translateY(-10px); opacity: 0; pointer-events: none; transition: all 0.3s cubic-bezier(0.17, 0.84, 0.44, 1); z-index: 15; border: 1px solid rgba(100, 100, 255, 0.2); } .settings-menu.active { transform: translateY(0); opacity: 1; pointer-events: all; } .settings-item { padding: 12px 15px; color: rgba(255, 255, 255, 0.8); font-size: 13px; display: flex; align-items: center; gap: 10px; cursor: pointer; transition: all 0.2s ease; } .settings-item:hover { background: rgba(100, 100, 255, 0.1); color: rgba(255, 255, 255, 1); } .settings-item svg { width: 16px; height: 16px; fill: currentColor; } .settings-divider { height: 1px; background: rgba(255, 255, 255, 0.1); margin: 5px 0; } .toggle-wrapper { margin-left: auto; width: 36px; height: 20px; background: rgba(60, 60, 100, 0.5); border-radius: 10px; position: relative; transition: all 0.2s ease; } .toggle-wrapper.active { background: rgba(138, 79, 255, 0.6); } .toggle-knob { position: absolute; width: 16px; height: 16px; background: white; border-radius: 50%; top: 2px; left: 2px; transition: all 0.2s ease; } .toggle-wrapper.active .toggle-knob { left: 18px; } </style> </head> <body> <div class="ar-overlay"> <div class="scene"> <div class="ar-persona person-1"> <div class="person" id="person1"> <div class="person-icon icon-1"></div> </div> </div> <div class="ar-persona person-2"> <div class="person" id="person2"> <div class="person-icon icon-2"></div> <div class="notification">3</div> </div> </div> <div class="ar-persona person-3"> <div class="person" id="person3"> <div class="person-icon icon-3"></div> </div> </div> <div class="bubble bubble-1" id="bubble1"> <div class="neon-pulse"></div> <div class="bubble-header"> <div class="user-icon icon-1"></div> <div class="user-name">Alex_VR</div> <div class="time-stamp">5m ago</div> </div> <div class="bubble-content"> This spatial design project looks amazing in AR! The transparent layers really help with depth perception. </div> <div class="bubble-footer"> <div class="bubble-actions"> <div class="action" id="reply1"> <svg viewBox="0 0 24 24"><path d="M10,9V5L3,12L10,19V15C15,15 18,18 21,22C20,17 17,10 10,9Z" fill="currentColor"/></svg> Reply </div> <div class="action" id="react1"> <svg viewBox="0 0 24 24"><path d="M12,21.35L10.55,20.03C5.4,15.36 2,12.28 2,8.5C2,5.42 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.09C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.42 22,8.5C22,12.28 18.6,15.36 13.45,20.03L12,21.35Z" fill="currentColor"/></svg> React </div> </div> </div> <div class="reaction-bubble" id="reaction1"> <span class="reaction-emoji">ð</span> <span class="reaction-count">3</span> </div> </div> <div class="bubble bubble-2" id="bubble2"> <div class="neon-pulse"></div> <div class="bubble-header"> <div class="user-icon icon-2"></div> <div class="user-name">Maya_AR</div> <div class="time-stamp">Just now</div> </div> <div class="bubble-content"> The neon accents really pop against the environment! I can read everything clearly even with complex backgrounds. </div> <div class="typing-indicator"> <div class="typing-dot"></div> <div class="typing-dot"></div> <div class="typing-dot"></div> </div> <div class="bubble-footer"> <div class="bubble-actions"> <div class="action" id="reply2"> <svg viewBox="0 0 24 24"><path d="M10,9V5L3,12L10,19V15C15,15 18,18 21,22C20,17 17,10 10,9Z" fill="currentColor"/></svg> Reply </div> <div class="action" id="react2"> <svg viewBox="0 0 24 24"><path d="M12,21.35L10.55,20.03C5.4,15.36 2,12.28 2,8.5C2,5.42 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.09C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.42 22,8.5C22,12.28 18.6,15.36 13.45,20.03L12,21.35Z" fill="currentColor"/></svg> React </div> </div> </div> </div> <div class="bubble bubble-3" id="bubble3"> <div class="neon-pulse"></div> <div class="bubble-header"> <div class="user-icon icon-3"></div> <div class="user-name">Zach_3D</div> <div class="time-stamp">2m ago</div> </div> <div class="bubble-content"> Love how the perspective shifts as I move! The 3D spatial anchoring gives context to where everyone is standing. </div> <div class="bubble-footer"> <div class="bubble-actions"> <div class="action" id="reply3"> <svg viewBox="0 0 24 24"><path d="M10,9V5L3,12L10,19V15C15,15 18,18 21,22C20,17 17,10 10,9Z" fill="currentColor"/></svg> Reply </div> <div class="action" id="react3"> <svg viewBox="0 0 24 24"><path d="M12,21.35L10.55,20.03C5.4,15.36 2,12.28 2,8.5C2,5.42 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.09C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.42 22,8.5C22,12.28 18.6,15.36 13.45,20.03L12,21.35Z" fill="currentColor"/></svg> React </div> </div> </div> <div class="reaction-bubble" id="reaction3"> <span class="reaction-emoji">ðĨ</span> <span class="reaction-count">5</span> </div> </div> <div class="focus-indicator" id="focus1"></div> <div class="focus-indicator" id="focus2"></div> <div class="focus-indicator" id="focus3"></div> <div class="location-indicator" id="location-indicator"> <div class="location-icon"> <svg viewBox="0 0 24 24"><path d="M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z" fill="currentColor"/></svg> </div> <div class="location-text">Design Lab Room 4</div> </div> </div> <div class="new-message"> <div class="emoji-btn" id="emoji-btn"> <svg viewBox="0 0 24 24"><path d="M12,17.5C14.33,17.5 16.3,16.04 17.11,14H6.89C7.7,16.04 9.67,17.5 12,17.5M8.5,11A1.5,1.5 0 0,0 10,9.5A1.5,1.5 0 0,0 8.5,8A1.5,1.5 0 0,0 7,9.5A1.5,1.5 0 0,0 8.5,11M15.5,11A1.5,1.5 0 0,0 17,9.5A1.5,1.5 0 0,0 15.5,8A1.5,1.5 0 0,0 14,9.5A1.5,1.5 0 0,0 15.5,11M12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" fill="currentColor"/></svg> </div> <div class="voice-btn" id="voice-btn"> <svg viewBox="0 0 24 24"><path d="M12,2A3,3 0 0,1 15,5V11A3,3 0 0,1 12,14A3,3 0 0,1 9,11V5A3,3 0 0,1 12,2M19,11C19,14.53 16.39,17.44 13,17.93V21H11V17.93C7.61,17.44 5,14.53 5,11H7A5,5 0 0,0 12,16A5,5 0 0,0 17,11H19Z" fill="currentColor"/></svg> </div> <input type="text" placeholder="Type a message in AR space..." id="message-input"> <div class="send-btn" id="send-btn"> <svg viewBox="0 0 24 24"><path d="M2,21L23,12L2,3V10L17,12L2,14V21Z" fill="currentColor"/></svg> </div> </div> <div class="emoji-picker" id="emoji-picker"> <div class="emoji-grid"> <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 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 class="emoji">ðĻ</div> <div class="emoji">ð</div> </div> </div> <div class="orientation-hint"> <svg class="gyro-icon" viewBox="0 0 24 24"><path d="M12,23A1,1 0 0,1 11,22V19H10A1,1 0 0,1 9,18V16A1,1 0 0,1 10,15H11V12H8V15H9A1,1 0 0,1 10,16V18A1,1 0 0,1 9,19H6A1,1 0 0,1 5,18V16A1,1 0 0,1 6,15H7V9H6A1,1
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Minimalist Messaging</title> <style> :root { --primary-color: #2A2A36; --secondary-color: #F5F5F7; --accent-color: #3B82F6; --text-primary: #1A1A1A; --text-secondary: #FFFFFF; --transition-speed: 0.25s; --bubble-radius: 1.25rem; --shadow-subtle: 0 2px 8px rgba(0, 0, 0, 0.08); } * { margin: 0; padding: 0; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; } body { background-color: var(--secondary-color); display: flex; flex-direction: column; height: 100vh; color: var(--text-primary); overflow: hidden; } .app-container { max-width: 700px; width: 100%; height: 700px; margin: 0 auto; display: flex; flex-direction: column; background-color: white; box-shadow: var(--shadow-subtle); border-radius: 0.75rem; overflow: hidden; } .header { padding: 1.5rem; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid rgba(0, 0, 0, 0.05); } .user-info { display: flex; align-items: center; gap: 0.8rem; } .avatar { width: 2.5rem; height: 2.5rem; border-radius: 50%; background-color: var(--accent-color); display: flex; align-items: center; justify-content: center; color: white; font-weight: 600; flex-shrink: 0; } .user-details h2 { font-size: 1rem; font-weight: 600; margin-bottom: 0.2rem; } .user-details p { font-size: 0.8rem; color: #666; } .header-icons { display: flex; gap: 1rem; } .icon-button { width: 2rem; height: 2rem; border-radius: 50%; display: flex; align-items: center; justify-content: center; background-color: transparent; border: none; cursor: pointer; color: #666; transition: all var(--transition-speed) ease; } .icon-button:hover { background-color: rgba(0, 0, 0, 0.05); color: var(--accent-color); } .conversation { flex: 1; padding: 1.5rem; overflow-y: auto; display: flex; flex-direction: column; gap: 1rem; } .message { max-width: 75%; position: relative; opacity: 0; transform: translateY(10px); animation: fadeIn 0.3s forwards; } .message-content { padding: 0.8rem 1.2rem; border-radius: var(--bubble-radius); box-shadow: var(--shadow-subtle); font-size: 0.95rem; line-height: 1.4; letter-spacing: 0.01em; } .message-time { font-size: 0.7rem; margin-top: 0.4rem; opacity: 0.6; } .incoming { align-self: flex-start; } .outgoing { align-self: flex-end; } .incoming .message-content { background-color: var(--secondary-color); color: var(--text-primary); border-bottom-left-radius: 0.2rem; } .outgoing .message-content { background-color: var(--accent-color); color: var(--text-secondary); border-bottom-right-radius: 0.2rem; } .outgoing .message-time { text-align: right; } .message-status { display: inline-flex; margin-left: 0.4rem; vertical-align: middle; } .composer { padding: 1rem 1.5rem; border-top: 1px solid rgba(0, 0, 0, 0.05); position: relative; } .input-area { display: flex; align-items: center; gap: 0.8rem; background-color: var(--secondary-color); border-radius: 1.5rem; padding: 0.2rem; transition: all var(--transition-speed) ease; } .input-area:focus-within { box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3); } .input-actions { display: flex; padding: 0.5rem; } #message-input { flex: 1; border: none; background: transparent; padding: 0.7rem 0; outline: none; font-size: 0.95rem; } .send-button { width: 2.5rem; height: 2.5rem; border-radius: 50%; background-color: var(--accent-color); color: white; border: none; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all var(--transition-speed) ease; transform: scale(1); } .send-button:hover { transform: scale(1.05); background-color: #2563eb; } .send-button:active { transform: scale(0.95); } /* Pulse animation for the typing indicator */ .typing-indicator { display: flex; align-items: center; opacity: 0; transition: opacity 0.3s ease; gap: 0.25rem; padding: 0.8rem 1.2rem; border-radius: var(--bubble-radius); background-color: var(--secondary-color); box-shadow: var(--shadow-subtle); border-bottom-left-radius: 0.2rem; margin-bottom: 1rem; align-self: flex-start; max-width: 75%; } .typing-indicator.active { opacity: 1; } .typing-indicator span { width: 0.5rem; height: 0.5rem; border-radius: 50%; background-color: #999; animation: pulse 1.5s infinite; } .typing-indicator span:nth-child(2) { animation-delay: 0.2s; } .typing-indicator span:nth-child(3) { animation-delay: 0.4s; } @keyframes fadeIn { to { opacity: 1; transform: translateY(0); } } @keyframes pulse { 0%, 100% { transform: scale(1); opacity: 0.6; } 50% { transform: scale(1.1); opacity: 1; } } /* Adding theme toggle */ .theme-toggle { position: absolute; top: 1.5rem; right: 1.5rem; width: 2.5rem; height: 2.5rem; border-radius: 50%; background-color: var(--secondary-color); border: none; color: var(--text-primary); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all var(--transition-speed) ease; z-index: 10; } .dark-mode { --primary-color: #1F2937; --secondary-color: #374151; --text-primary: #F9FAFB; --text-secondary: #F9FAFB; --shadow-subtle: 0 2px 8px rgba(0, 0, 0, 0.2); } .dark-mode body { background-color: #111827; } .dark-mode .app-container { background-color: #1F2937; } .dark-mode .header { border-bottom-color: rgba(255, 255, 255, 0.05); } .dark-mode .user-details p { color: #9CA3AF; } .dark-mode .icon-button { color: #9CA3AF; } .dark-mode .icon-button:hover { background-color: rgba(255, 255, 255, 0.05); } .dark-mode .incoming .message-content { background-color: #374151; color: #F9FAFB; } .dark-mode .input-area { background-color: #374151; } .dark-mode #message-input { color: #F9FAFB; } .dark-mode .typing-indicator { background-color: #374151; } /* Toast notification */ .toast { position: fixed; bottom: 2rem; left: 50%; transform: translateX(-50%) translateY(100px); background-color: #323232; color: white; padding: 0.75rem 1.5rem; border-radius: 2rem; box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2); opacity: 0; transition: all 0.3s ease; z-index: 1000; } .toast.show { transform: translateX(-50%) translateY(0); opacity: 1; } /* Message sending animation */ .sending-ripple { position: absolute; width: 20px; height: 20px; border-radius: 50%; background-color: rgba(59, 130, 246, 0.3); position: absolute; right: -10px; bottom: -10px; animation: ripple 1s linear; } @keyframes ripple { 0% { transform: scale(0); opacity: 1; } 100% { transform: scale(8); opacity: 0; } } /* Responsive design */ @media (max-width: 600px) { .header { padding: 1rem; } .conversation { padding: 1rem; } .composer { padding: 0.75rem 1rem; } .message { max-width: 85%; } } </style> </head> <body> <div class="app-container"> <div class="header"> <div class="user-info"> <div class="avatar">A</div> <div class="user-details"> <h2>Alex Chen</h2> <p>Online now</p> </div> </div> <div class="header-icons"> <button class="icon-button" id="theme-toggle"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg> </button> <button class="icon-button"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 19C6.55556 19 3 15.4444 3 11C3 6.55556 6.55556 3 11 3C15.4444 3 19 6.55556 19 11C19 15.4444 15.4444 19 11 19Z"></path><path d="M21 21L17 17"></path></svg> </button> <button class="icon-button"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="1"></circle><circle cx="19" cy="12" r="1"></circle><circle cx="5" cy="12" r="1"></circle></svg> </button> </div> </div> <div class="conversation" id="conversation"> <div class="message incoming"> <div class="message-content">Hey there! Have you seen the new minimalist messaging app everyone's talking about?</div> <div class="message-time">9:01 AM</div> </div> <div class="message outgoing"> <div class="message-content">Not yet, what makes it different from all the others?</div> <div class="message-time">9:03 AM <span class="message-status">ââ</span></div> </div> <div class="message incoming"> <div class="message-content">It's incredibly clean. No bloated features, just pure messaging with beautiful typography and subtle animations.</div> <div class="message-time">9:04 AM</div> </div> <div class="message incoming"> <div class="message-content">The transitions between states are so smooth you barely notice them, but they add this subtle sense of polish.</div> <div class="message-time">9:04 AM</div> </div> <div class="message outgoing"> <div class="message-content">Sounds interesting. I'm tired of apps with a million features I never use. What's it called?</div> <div class="message-time">9:06 AM <span class="message-status">ââ</span></div> </div> <div class="typing-indicator" id="typing-indicator"> <span></span> <span></span> <span></span> </div> </div> <div class="composer"> <div class="input-area"> <div class="input-actions"> <button class="icon-button"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"></path><circle cx="12" cy="13" r="3"></circle></svg> </button> </div> <input type="text" id="message-input" placeholder="Type a message..." autocomplete="off"> <button class="send-button" id="send-button"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" 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> </div> <div class="toast" id="toast">Message sent</div> <script> // DOM elements const conversation = document.getElementById('conversation'); const messageInput = document.getElementById('message-input'); const sendButton = document.getElementById('send-button'); const typingIndicator = document.getElementById('typing-indicator'); const themeToggle = document.getElementById('theme-toggle'); const toast = document.getElementById('toast'); // Sample responses for the conversation const sampleResponses = [ "It's called 'Minimal'. Just launched last week.", "I like how it uses high-contrast typography. Makes everything so readable even on small screens.", "The shadowing is so subtle but adds just enough depth. It's a masterclass in restraint.", "Want me to send you the link? It's invitation only right now, but I have a few invites left.", "They specifically focused on making the message delivery animations feel satisfying. The tiny details make all the difference." ]; // Current response index let responseIndex = 0; // Send message function function sendMessage() { const messageText = messageInput.value.trim(); if (messageText === '') return; // Create the message element const messageElement = document.createElement('div'); messageElement.className = 'message outgoing'; // Create the message content const messageContent = document.createElement('div'); messageContent.className = 'message-content'; messageContent.textContent = messageText; // Create the message time const messageTime = document.createElement('div'); messageTime.className = 'message-time'; const now = new Date(); messageTime.innerHTML = `${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')} <span class="message-status">â</span>`; // Append all elements messageElement.appendChild(messageContent); messageElement.appendChild(messageTime); conversation.appendChild(messageElement); // Show sending ripple animation const ripple = document.createElement('div'); ripple.className = 'sending-ripple'; messageElement.appendChild(ripple); setTimeout(() => { messageElement.querySelector('.message-status').innerHTML = 'ââ'; ripple.remove(); }, 1000); // Clear input and scroll to bottom messageInput.value = ''; conversation.scrollTop = conversation.scrollHeight; // Show toast notification showToast('Message sent'); // Simulate typing response simulateTypingResponse(); } // Simulate typing and response function simulateTypingResponse() { // Show typing indicator typingIndicator.classList.add('active'); conversation.scrollTop = conversation.scrollHeight; // Random typing time between 2-4 seconds const typingTime = Math.random() * 2000 + 2000; setTimeout(() => { // Hide typing indicator typingIndicator.classList.remove('active'); // Add response message if (responseIndex < sampleResponses.length) { const responseText = sampleResponses[responseIndex]; responseIndex++; // Create response message const responseElement = document.createElement('div'); responseElement.className = 'message incoming'; const responseContent = document.createElement('div'); responseContent.className = 'message-content'; responseContent.textContent = responseText; const responseTime = document.createElement('div'); responseTime.className = 'message-time'; const now = new Date(); responseTime.textContent = `${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')}`; responseElement.appendChild(responseContent); responseElement.appendChild(responseTime); conversation.appendChild(responseElement); // Scroll to bottom conversation.scrollTop = conversation.scrollHeight; } }, typingTime); } // Show toast notification function showToast(message) { toast.textContent = message; toast.classList.add('show'); setTimeout(() => { toast.classList.remove('show'); }, 3000); } // Toggle dark/light theme function toggleTheme() { document.documentElement.classList.toggle('dark-mode'); const isDarkMode = document.documentElement.classList.contains('dark-mode'); showToast(isDarkMode ? 'Dark mode enabled' : 'Light mode enabled'); } // Event listeners sendButton.addEventListener('click', sendMessage); messageInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { sendMessage(); } }); themeToggle.addEventListener('click', toggleTheme); // Initial scroll to bottom conversation.scrollTop = conversation.scrollHeight; </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> :root { --primary-color: #6c5ce7; --secondary-color: #a29bfe; --background-color: #f9f9f9; --text-color: #333; --shadow-color: rgba(0, 0, 0, 0.1); --assistant-bubble: linear-gradient(135deg, #74b9ff, #0984e3); --user-bubble: linear-gradient(135deg, #a29bfe, #6c5ce7); --transition-time: 0.3s; } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background-color: var(--background-color); padding: 20px; } .container { width: 100%; max-width: 680px; height: 680px; background: white; border-radius: 20px; box-shadow: 0 10px 25px var(--shadow-color); display: flex; flex-direction: column; overflow: hidden; position: relative; } header { padding: 20px; background: white; box-shadow: 0 2px 10px var(--shadow-color); z-index: 10; display: flex; align-items: center; justify-content: space-between; } .assistant-info { display: flex; align-items: center; gap: 12px; } .assistant-avatar { width: 40px; height: 40px; border-radius: 50%; background: var(--assistant-bubble); display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; box-shadow: 0 3px 8px rgba(108, 92, 231, 0.3); } .assistant-name { font-weight: 600; color: var(--text-color); } .assistant-status { font-size: 12px; color: #6c757d; } .options-btn { background: transparent; border: none; cursor: pointer; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; border-radius: 50%; transition: background-color var(--transition-time); } .options-btn:hover { background-color: rgba(0, 0, 0, 0.05); } .chat-area { flex: 1; overflow-y: auto; padding: 20px; display: flex; flex-direction: column; gap: 20px; } .chat-bubble { max-width: 80%; padding: 15px 20px; border-radius: 18px; position: relative; color: white; animation: bubbleAppear 0.3s ease-out forwards; transform-origin: bottom; box-shadow: 0 3px 10px var(--shadow-color); overflow: hidden; } .chat-bubble::before { content: ""; position: absolute; top: 0; left: 0; right: 0; bottom: 0; z-index: -1; opacity: 0.9; transition: opacity 0.3s ease; } .chat-bubble:hover::before { opacity: 1; } .assistant-bubble { align-self: flex-start; background: var(--assistant-bubble); border-bottom-left-radius: 5px; } .assistant-bubble::before { background: var(--assistant-bubble); } .user-bubble { align-self: flex-end; background: var(--user-bubble); border-bottom-right-radius: 5px; } .user-bubble::before { background: var(--user-bubble); } .waveform { height: 20px; width: 100%; display: flex; align-items: center; margin-top: 8px; gap: 3px; } .waveform-bar { background-color: rgba(255, 255, 255, 0.6); width: 3px; height: 100%; border-radius: 3px; animation: waveform-animation 1.2s ease-in-out infinite; } .waveform-bar:nth-child(2) { animation-delay: 0.1s; } .waveform-bar:nth-child(3) { animation-delay: 0.2s; } .waveform-bar:nth-child(4) { animation-delay: 0.3s; } .waveform-bar:nth-child(5) { animation-delay: 0.4s; } .waveform-bar:nth-child(6) { animation-delay: 0.5s; } .waveform-bar:nth-child(7) { animation-delay: 0.6s; } .waveform-bar:nth-child(8) { animation-delay: 0.7s; } .waveform-bar:nth-child(9) { animation-delay: 0.8s; } .waveform-bar:nth-child(10) { animation-delay: 0.9s; } .input-area { padding: 15px 20px; background: white; border-top: 1px solid rgba(0, 0, 0, 0.05); display: flex; align-items: center; gap: 10px; z-index: 10; } .input-field { flex: 1; border: none; background: var(--background-color); padding: 12px 15px; border-radius: 30px; font-size: 14px; outline: none; transition: box-shadow var(--transition-time); } .input-field:focus { box-shadow: 0 0 0 2px rgba(108, 92, 231, 0.3); } .send-btn { background: var(--primary-color); border: none; width: 45px; height: 45px; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; color: white; box-shadow: 0 3px 8px rgba(108, 92, 231, 0.3); transition: transform var(--transition-time), box-shadow var(--transition-time); } .send-btn:hover { transform: translateY(-2px); box-shadow: 0 5px 12px rgba(108, 92, 231, 0.4); } .voice-btn { background: transparent; border: none; width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--primary-color); transition: background-color var(--transition-time); } .voice-btn:hover { background-color: rgba(108, 92, 231, 0.1); } .voice-btn.recording { animation: pulse 1.5s infinite; background-color: rgba(255, 0, 0, 0.1); color: #e74c3c; } .voice-recording-indicator { position: absolute; bottom: 85px; left: 50%; transform: translateX(-50%); background: rgba(0, 0, 0, 0.7); color: white; padding: 8px 16px; border-radius: 20px; display: flex; align-items: center; gap: 8px; font-size: 14px; opacity: 0; visibility: hidden; transition: opacity 0.3s, visibility 0.3s; } .voice-recording-indicator.visible { opacity: 1; visibility: visible; } .recording-dot { width: 10px; height: 10px; background-color: #e74c3c; border-radius: 50%; animation: blink 1s infinite; } .typing-indicator { display: flex; align-items: center; gap: 5px; padding: 10px 20px; width: fit-content; background: var(--assistant-bubble); border-radius: 18px; margin-top: 10px; align-self: flex-start; opacity: 0; transform: translateY(10px); transition: opacity 0.3s, transform 0.3s; box-shadow: 0 3px 10px var(--shadow-color); border-bottom-left-radius: 5px; } .typing-indicator.visible { opacity: 1; transform: translateY(0); } .typing-dot { width: 8px; height: 8px; background: rgba(255, 255, 255, 0.7); border-radius: 50%; animation: typingBlink 1.4s infinite; } .typing-dot:nth-child(2) { animation-delay: 0.2s; } .typing-dot:nth-child(3) { animation-delay: 0.4s; } @keyframes bubbleAppear { 0% { opacity: 0; transform: scale(0.8); } 100% { opacity: 1; transform: scale(1); } } @keyframes waveform-animation { 0%, 100% { height: 5px; } 50% { height: 20px; } } @keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(231, 76, 60, 0.4); } 70% { box-shadow: 0 0 0 10px rgba(231, 76, 60, 0); } 100% { box-shadow: 0 0 0 0 rgba(231, 76, 60, 0); } } @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } @keyframes typingBlink { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-5px); } } @media (max-width: 600px) { .container { height: 100vh; border-radius: 0; } .chat-bubble { max-width: 90%; } } </style> </head> <body> <div class="container"> <header> <div class="assistant-info"> <div class="assistant-avatar">E</div> <div> <div class="assistant-name">Echo</div> <div class="assistant-status">Always listening</div> </div> </div> <button class="options-btn"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="1"></circle> <circle cx="12" cy="5" r="1"></circle> <circle cx="12" cy="19" r="1"></circle> </svg> </button> </header> <div class="chat-area" id="chatArea"> <div class="chat-bubble assistant-bubble"> Hi there! I'm Echo, your personal assistant. How can I help you today? </div> </div> <div class="typing-indicator" id="typingIndicator"> <div class="typing-dot"></div> <div class="typing-dot"></div> <div class="typing-dot"></div> </div> <div class="voice-recording-indicator" id="recordingIndicator"> <div class="recording-dot"></div> <span>Recording voice...</span> </div> <div class="input-area"> <button class="voice-btn" id="voiceBtn"> <svg xmlns="http://www.w3.org/2000/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="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"></path> <path d="M19 10v2a7 7 0 0 1-14 0v-2"></path> <line x1="12" y1="19" x2="12" y2="23"></line> <line x1="8" y1="23" x2="16" y2="23"></line> </svg> </button> <input type="text" class="input-field" id="messageInput" placeholder="Type your message..."> <button class="send-btn" id="sendBtn"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" 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> <script> const chatArea = document.getElementById('chatArea'); const messageInput = document.getElementById('messageInput'); const sendBtn = document.getElementById('sendBtn'); const voiceBtn = document.getElementById('voiceBtn'); const typingIndicator = document.getElementById('typingIndicator'); const recordingIndicator = document.getElementById('recordingIndicator'); // Sample responses for different queries const sampleResponses = { "hello": "Hello! How can I assist you today?", "hi": "Hi there! What can I help you with?", "how are you": "I'm functioning perfectly! How can I make your day easier?", "what can you do": "I can help you with scheduling, reminders, finding information, controlling smart home devices, and much more. Just ask!", "time": "The current time is " + new Date().toLocaleTimeString() + ". Is there anything specific you need to schedule?", "weather": "I'm showing mild temperatures today with a chance of rain in the evening. Would you like me to set a reminder for you to take an umbrella?", "remind": "I've set that reminder for you. I'll notify you at the scheduled time. Is there anything else you'd like to remember?", "play music": "Starting your favorite playlist. Would you like me to adjust the volume or skip to a specific track?", "smart home": "I can control your connected devices. Would you like me to adjust the lights, temperature, or check if your doors are locked?", "help": "I'm here to assist with information, tasks, reminders, and more. Try asking me about the weather, to set a reminder, or to control your smart home devices." }; // Default response for unrecognized queries const defaultResponse = "I'm not sure how to answer that yet. Is there something else I can help you with?"; // Initialize voice recording state let isRecording = false; // Add event listeners sendBtn.addEventListener('click', sendMessage); messageInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { sendMessage(); } }); voiceBtn.addEventListener('click', toggleVoiceRecording); // Function to add message to chat function addMessage(text, isUser = false) { const bubble = document.createElement('div'); bubble.classList.add('chat-bubble'); if (isUser) { bubble.classList.add('user-bubble'); } else { bubble.classList.add('assistant-bubble'); } bubble.textContent = text; // Only add waveform to assistant messages if (!isUser) { const waveform = document.createElement('div'); waveform.classList.add('waveform'); for (let i = 0; i < 10; i++) { const bar = document.createElement('div'); bar.classList.add('waveform-bar'); waveform.appendChild(bar); } bubble.appendChild(waveform); } chatArea.appendChild(bubble); chatArea.scrollTop = chatArea.scrollHeight; } // Function to send message function sendMessage() { const message = messageInput.value.trim(); if (!message) return; // Add user message addMessage(message, true); messageInput.value = ''; // Show typing indicator showTypingIndicator(); // Process the message after a delay to simulate thinking setTimeout(() => { hideTypingIndicator(); processMessage(message); }, 1500); } // Function to process message and generate response function processMessage(message) { let response = defaultResponse; // Check if the message contains any keywords from our sample responses const messageLower = message.toLowerCase(); for (const [keyword, reply] of Object.entries(sampleResponses)) { if (messageLower.includes(keyword)) { response = reply; break; } } // Add assistant response addMessage(response); } // Function to toggle voice recording function toggleVoiceRecording() { isRecording = !isRecording; if (isRecording) { // Start recording voiceBtn.classList.add('recording'); recordingIndicator.classList.add('visible'); // Simulate voice recognition after 3 seconds setTimeout(() => { const randomPhrases = [ "What's the weather like today?", "Remind me to call mom tomorrow", "Turn on the living room lights", "Set an alarm for 7 AM" ]; // Pick a random phrase const recognizedText = randomPhrases[Math.floor(Math.random() * randomPhrases.length)]; // Add the recognized text to input messageInput.value = recognizedText; // Stop recording toggleVoiceRecording(); }, 3000); } else { // Stop recording voiceBtn.classList.remove('recording'); recordingIndicator.classList.remove('visible'); } } // Function to show typing indicator function showTypingIndicator() { typingIndicator.classList.add('visible'); } // Function to hide typing indicator function hideTypingIndicator() { typingIndicator.classList.remove('visible'); } // Initial greeting after a short delay setTimeout(() => { // Already added in HTML }, 500); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Business Webinar Chat</title> <style> @import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;1,400&display=swap'); * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Playfair Display', serif; background: #f9f9f9; color: #333; display: flex; flex-direction: column; height: 700px; width: 700px; max-width: 100%; margin: 0 auto; overflow: hidden; } .webinar-container { display: flex; flex-direction: column; height: 100%; width: 100%; overflow: hidden; background: #fff; border-radius: 12px; box-shadow: 0 6px 24px rgba(0, 0, 0, 0.08); } .webinar-header { padding: 20px 25px; background: #2c3e50; color: white; border-radius: 12px 12px 0 0; position: relative; z-index: 10; } .webinar-header h1 { font-size: 24px; font-weight: 700; margin-bottom: 6px; } .webinar-header p { font-size: 14px; opacity: 0.9; font-style: italic; } .webinar-participants { padding: 12px 25px; background: #f5f7fa; border-bottom: 1px solid #e5e8ed; display: flex; overflow-x: auto; gap: 15px; position: relative; z-index: 5; } .participant { display: flex; align-items: center; gap: 8px; padding: 6px 12px; border-radius: 25px; background: white; border: 1px solid #e1e5eb; white-space: nowrap; transition: all 0.2s ease; } .participant:hover { transform: translateY(-2px); box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05); } .participant.active { background: #e8f0fe; border-color: #b4d3ff; } .participant-avatar { width: 26px; height: 26px; border-radius: 50%; background: #7f8c8d; display: flex; align-items: center; justify-content: center; color: white; font-size: 12px; font-weight: bold; } .chat-container { flex: 1; overflow-y: auto; padding: 20px 25px; display: flex; flex-direction: column; gap: 18px; scroll-behavior: smooth; } .message { max-width: 85%; position: relative; opacity: 0; transform: translateY(20px); animation: fadeIn 0.3s forwards; } @keyframes fadeIn { to { opacity: 1; transform: translateY(0); } } .message.sent { align-self: flex-end; } .message.received { align-self: flex-start; } .message-bubble { padding: 15px 20px; border-radius: 18px; position: relative; color: #2c3e50; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.04); transition: transform 0.3s ease; } .message-bubble:hover { transform: scale(1.02); } .message.sent .message-bubble { background: #e8f5e9; border: 1px solid #c8e6c9; border-bottom-right-radius: 4px; } .message.received .message-bubble { background: #fff; border: 1px solid #e5e8ed; border-bottom-left-radius: 4px; } .message-header { display: flex; justify-content: space-between; margin-bottom: 8px; font-size: 14px; } .message-sender { font-weight: bold; color: #34495e; } .message-time { font-style: italic; color: #7f8c8d; font-size: 12px; } .message-content { line-height: 1.5; text-align: left; } .message.priority .message-bubble { background: #fff3e0; border-color: #ffe0b2; } .message.question .message-bubble { background: #e8f0fe; border-color: #b4d3ff; } .message.announcement .message-bubble { background: #fce4ec; border-color: #f8bbd0; } .message-tag { display: inline-block; padding: 2px 8px; border-radius: 12px; font-size: 10px; margin-right: 5px; text-transform: uppercase; font-weight: bold; letter-spacing: 0.5px; } .message.priority .message-tag { background: #ff9800; color: white; } .message.question .message-tag { background: #4285f4; color: white; } .message.announcement .message-tag { background: #e91e63; color: white; } .input-container { padding: 20px 25px; border-top: 1px solid #e5e8ed; display: flex; gap: 15px; background: white; position: relative; z-index: 10; } .input-options { display: flex; gap: 8px; } .message-type { background: none; border: 1px solid #dce1e6; border-radius: 4px; padding: 6px 10px; font-family: 'Playfair Display', serif; font-size: 13px; cursor: pointer; transition: all 0.2s ease; color: #2c3e50; } .message-type:hover, .message-type.active { transform: translateY(-2px); } .message-type.normal { border-color: #e5e8ed; } .message-type.normal.active { background: #f5f7fa; border-color: #cfd8dc; } .message-type.priority { border-color: #ffe0b2; } .message-type.priority.active { background: #fff3e0; border-color: #ffcc80; } .message-type.question { border-color: #b4d3ff; } .message-type.question.active { background: #e8f0fe; border-color: #90caf9; } .message-type.announcement { border-color: #f8bbd0; } .message-type.announcement.active { background: #fce4ec; border-color: #f48fb1; } .message-input { flex: 1; padding: 15px; border: 1px solid #dce1e6; border-radius: 8px; font-family: 'Playfair Display', serif; font-size: 16px; resize: none; height: 52px; transition: all 0.3s ease; } .message-input:focus { outline: none; border-color: #b4d3ff; box-shadow: 0 0 0 3px rgba(66, 133, 244, 0.1); } .message-input::placeholder { color: #95a5a6; font-style: italic; } .send-button { background: #2c3e50; color: white; border: none; border-radius: 8px; width: 52px; height: 52px; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.3s ease; } .send-button:hover { background: #34495e; transform: translateY(-2px); box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); } .send-button svg { width: 20px; height: 20px; } .typing-indicator { display: none; align-self: flex-start; padding: 10px 15px; background: #f5f7fa; border-radius: 18px; margin-top: 8px; font-style: italic; color: #7f8c8d; font-size: 14px; animation: pulse 1.5s infinite; } @keyframes pulse { 0% { opacity: 0.5; } 50% { opacity: 1; } 100% { opacity: 0.5; } } .typing-indicator.visible { display: block; } .auto-scroll-notice { position: fixed; bottom: 80px; left: 50%; transform: translateX(-50%); background: rgba(44, 62, 80, 0.9); color: white; padding: 8px 16px; border-radius: 20px; font-size: 12px; opacity: 0; pointer-events: none; transition: opacity 0.3s ease; } .auto-scroll-notice.visible { opacity: 1; animation: fadeOut 3s forwards; } @keyframes fadeOut { 0%, 50% { opacity: 1; } 100% { opacity: 0; } } /* Responsive adjustments */ @media (max-width: 600px) { .webinar-header { padding: 15px 20px; } .webinar-header h1 { font-size: 20px; } .webinar-participants, .chat-container, .input-container { padding: 15px 20px; } .message { max-width: 90%; } .input-options { display: none; } } </style> </head> <body> <div class="webinar-container"> <div class="webinar-header"> <h1>Q4 Strategic Planning Session</h1> <p>Finalizing our go-to-market roadmap for next quarter</p> </div> <div class="webinar-participants"> <div class="participant active"> <div class="participant-avatar" style="background: #3498db;">JD</div> <span>James Davis (You)</span> </div> <div class="participant"> <div class="participant-avatar" style="background: #e74c3c;">MK</div> <span>Maria Kim</span> </div> <div class="participant"> <div class="participant-avatar" style="background: #2ecc71;">RJ</div> <span>Robert Johnson</span> </div> <div class="participant"> <div class="participant-avatar" style="background: #9b59b6;">AT</div> <span>Alicia Thompson</span> </div> <div class="participant"> <div class="participant-avatar" style="background: #f39c12;">CS</div> <span>Carlos Santos</span> </div> <div class="participant"> <div class="participant-avatar" style="background: #1abc9c;">LP</div> <span>Lisa Park</span> </div> </div> <div class="chat-container" id="chatContainer"> <div class="message announcement received"> <div class="message-bubble"> <div class="message-header"> <div class="message-sender"><span class="message-tag">ANNOUNCEMENT</span> Maria Kim</div> <div class="message-time">10:03 AM</div> </div> <div class="message-content">Welcome everyone to our Q4 strategic planning session. Let's focus on three key areas today: product roadmap alignment, marketing campaign scheduling, and sales territory allocation.</div> </div> </div> <div class="message received"> <div class="message-bubble"> <div class="message-header"> <div class="message-sender">Robert Johnson</div> <div class="message-time">10:05 AM</div> </div> <div class="message-content">Thanks for organizing, Maria. I've prepared the sales forecast data based on our Q3 performance. We're seeing a 12% increase in enterprise leads.</div> </div> </div> <div class="message priority received"> <div class="message-bubble"> <div class="message-header"> <div class="message-sender"><span class="message-tag">PRIORITY</span> Alicia Thompson</div> <div class="message-time">10:07 AM</div> </div> <div class="message-content">Before we dive in, I need to address the supply chain disruption affecting our Q4 hardware rollout. Singapore facility is reporting a 3-week delay in components.</div> </div> </div> <div class="message sent"> <div class="message-bubble"> <div class="message-header"> <div class="message-sender">James Davis</div> <div class="message-time">10:09 AM</div> </div> <div class="message-content">That's concerning, Alicia. Have you connected with our alternative suppliers in Taiwan? We discussed contingency options in last month's vendor review.</div> </div> </div> <div class="message received"> <div class="message-bubble"> <div class="message-header"> <div class="message-sender">Carlos Santos</div> <div class="message-time">10:11 AM</div> </div> <div class="message-content">I can share our development team's perspective on roadmap adjustments if needed. We can reprioritize the cloud features while hardware catches up.</div> </div> </div> <div class="message question received"> <div class="message-bubble"> <div class="message-header"> <div class="message-sender"><span class="message-tag">QUESTION</span> Lisa Park</div> <div class="message-time">10:13 AM</div> </div> <div class="message-content">How will this impact our scheduled keynote at TechExpo in November? We've already submitted our presentation outline featuring the new hardware capabilities.</div> </div> </div> <div class="message received"> <div class="message-bubble"> <div class="message-header"> <div class="message-sender">Maria Kim</div> <div class="message-time">10:15 AM</div> </div> <div class="message-content">Good question, Lisa. Let's table the expo strategy for our second agenda item. Alicia, can you share more specifics on which product lines are affected?</div> </div> </div> <div class="message priority received"> <div class="message-bubble"> <div class="message-header"> <div class="message-sender"><span class="message-tag">PRIORITY</span> Alicia Thompson</div> <div class="message-time">10:17 AM</div> </div> <div class="message-content">Primary impact is on the ProSeries 7 line, which accounts for 40% of projected Q4 revenue. The EnterpriseEdge products remain on schedule with domestic production.</div> </div> </div> <div class="message sent"> <div class="message-bubble"> <div class="message-header"> <div class="message-sender">James Davis</div> <div class="message-time">10:19 AM</div> </div> <div class="message-content">We should reallocate marketing spend to push EnterpriseEdge while we resolve the ProSeries delays. I've drafted a contingency budget that shifts $1.2M from consumer to enterprise campaigns.</div> </div> </div> <div class="typing-indicator" id="typingIndicator">Robert is typing...</div> </div> <div class="input-container"> <div class="input-options"> <button class="message-type normal active" data-type="normal">Normal</button> <button class="message-type priority" data-type="priority">Priority</button> <button class="message-type question" data-type="question">Question</button> <button class="message-type announcement" data-type="announcement">Announce</button> </div> <textarea class="message-input" id="messageInput" placeholder="Type your message here..." rows="1"></textarea> <button class="send-button" id="sendButton"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" /> </svg> </button> </div> </div> <div class="auto-scroll-notice" id="autoScrollNotice">Auto-scrolling to new messages</div> <script> document.addEventListener('DOMContentLoaded', function() { const chatContainer = document.getElementById('chatContainer'); const messageInput = document.getElementById('messageInput'); const sendButton = document.getElementById('sendButton'); const typingIndicator = document.getElementById('typingIndicator'); const autoScrollNotice = document.getElementById('autoScrollNotice'); const messageTypes = document.querySelectorAll('.message-type'); let currentMessageType = 'normal'; let isScrolling = false; // Auto-resize textarea messageInput.addEventListener('input', function() { this.style.height = 'auto'; this.style.height = (this.scrollHeight) + 'px'; if (this.scrollHeight > 150) { this.style.height = '150px'; this.style.overflowY = 'auto'; } else { this.style.overflowY = 'hidden'; } }); // Set active message type messageTypes.forEach(button => { button.addEventListener('click', function() { messageTypes.forEach(btn => btn.classList.remove('active')); this.classList.add('active'); currentMessageType = this.dataset.type; messageInput.focus(); }); }); // Send message function sendMessage() { const messageText = messageInput.value.trim(); if (messageText) { // Create message element const messageElement = document.createElement('div'); messageElement.classList.add('message', 'sent'); if (currentMessageType !== 'normal') { messageElement.classList.add(currentMessageType); } // Get current time const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); const ampm = hours >= 12 ? 'PM' : 'AM'; const formattedHours = hours % 12 || 12; const formattedMinutes = minutes < 10 ? '0' + minutes : minutes; const timeString = `${formattedHours}:${formattedMinutes} ${ampm}`; // Create tag based on message type let tagHtml = ''; if (currentMessageType !== 'normal') { tagHtml = `<span class="message-tag">${currentMessageType.toUpperCase()}</span> `; } // Set message HTML messageElement.innerHTML = ` <div class="message-bubble"> <div class="message-header"> <div class="message-sender">${tagHtml}James Davis</div> <div class="message-time">${timeString}</div> </div> <div class="message-content">${messageText}</div> </div> `; // Add message to chat chatContainer.appendChild(messageElement); // Clear input messageInput.value = ''; messageInput.style.height = '52px'; messageInput.style.overflowY = 'hidden'; // Auto scroll to bottom scrollToBottom(); // Show auto-scroll notice showAutoScrollNotice(); // Simulate response (for demo purposes) simulateResponse(); } } // Send button click sendButton.addEventListener('click', sendMessage); // Enter key to send messageInput.addEventListener('keydown', function(e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }); // Auto scroll to bottom function scrollToBottom() { isScrolling = true; chatContainer.scrollTop = chatContainer.scrollHeight; setTimeout(() => { isScrolling = false; }, 100); } // Show auto-scroll notice function showAutoScrollNotice() { autoScrollNotice.classList.add('visible'); setTimeout(() => { autoScrollNotice.classList.remove('visible'); }, 3000); } // Check if user has scrolled up chatContainer.addEventListener('scroll', function() { if (!isScrolling && chatContainer.scrollTop + chatContainer.clientHeight < chatContainer.scrollHeight - 50) { // User has scrolled up } else { // User is at bottom } }); // Simulate typing and response function simulateResponse() { const names = ['Maria Kim', 'Robert Johnson', 'Alicia Thompson', 'Carlos Santos', 'Lisa Park']; const randomName = names[Math.floor(Math.random() * names.length)]; // Show typing indicator typingIndicator.textContent = `${randomName} is typing...`; typingIndicator.classList.add('visible'); // Random typing time between 2-5 seconds const typingTime = Math.floor(Math.random() * 3000) + 2000; // After typing time, show response setTimeout(() => { typingIndicator.classList.remove('visible'); // Only show response 70% of the time if (Math.random() < 0.7) { // Determine message type const messageTypes = ['normal', 'normal', 'normal', 'question', 'priority']; const randomType = messageTypes[Math.floor(Math.random() * messageTypes.length)]; // Create response messages const responses = [ "I agree with your assessment. Let's look at reallocating resources to maximize Q4 impact.", "That's a solid approach. We should review the numbers with finance before finalizing.", "Have we considered the impact on our international markets? EMEA launches typically need longer lead times.", "The marketing team has already developed creative assets for ProSeries 7. Can we adapt them for EnterpriseEdge?", "Product training for the sales team will need to be adjusted. I'll work with enablement on a revised curriculum.", "Customer Success is concerned about managing expectations with clients who've been promised Q4 delivery.", "The investor relations team needs to know if this will affect our guidance for the quarter." ]; const randomResponse = responses[Math.floor(Math.random() * responses.length)]; // Get current time const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); const ampm = hours >= 12 ? 'PM' : 'AM'; const formattedHours = hours % 12 || 12; const formattedMinutes = minutes < 10 ? '0' + minutes : minutes; const timeString = `${formattedHours}:${formattedMinutes} ${ampm}`; // Create message element const messageElement = document.createElement('div'); messageElement.classList.add('message', 'received'); if (randomType !== 'normal') { messageElement.classList.add(randomType); } // Create tag based on message type let tagHtml = ''; if (randomType !== 'normal') { tagHtml = `<span class="message-tag">${randomType.toUpperCase()}</span> `; } // Set message HTML messageElement.innerHTML = ` <div class="message-bubble"> <div class="message-header"> <div class="message-sender">${tagHtml}${randomName}</div> <div class="message-time">${timeString}</div> </div> <div class="message-content">${randomResponse}</div> </div> `; // Add message to chat chatContainer.appendChild(messageElement); // Auto scroll to bottom scrollToBottom(); // Show auto-scroll notice showAutoScrollNotice(); } }, typingTime); } // Initial scroll to bottom scrollToBottom(); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Blippy - Personality Chatbot</title> <style> :root { /* Light mode colors */ --background: #f7f9ff; --text-primary: #333344; --bubble-bot: #7c4dff; --bubble-user: #eceff1; --bubble-bot-shadow: rgba(124, 77, 255, 0.2); --bubble-user-shadow: rgba(236, 239, 241, 0.7); --accent-primary: #ff5e94; --accent-secondary: #4ec5c1; --input-background: #ffffff; --input-text: #333344; --button-hover: #f06292; --typing-dots: #7c4dff; } [data-theme="dark"] { /* Dark mode colors */ --background: #262837; --text-primary: #eceff1; --bubble-bot: #5e35b1; --bubble-user: #37384b; --bubble-bot-shadow: rgba(94, 53, 177, 0.3); --bubble-user-shadow: rgba(55, 56, 75, 0.5); --accent-primary: #ff5e94; --accent-secondary: #4ec5c1; --input-background: #37384b; --input-text: #eceff1; --button-hover: #ec407a; --typing-dots: #b39ddb; } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; transition: background-color 0.3s, color 0.3s, box-shadow 0.3s; } body { background-color: var(--background); color: var(--text-primary); display: flex; flex-direction: column; height: 100vh; max-height: 700px; width: 100%; max-width: 700px; margin: 0 auto; overflow: hidden; } .container { display: flex; flex-direction: column; flex: 1; padding: 20px; position: relative; overflow: hidden; } header { display: flex; justify-content: space-between; align-items: center; padding-bottom: 15px; border-bottom: 2px solid var(--accent-secondary); margin-bottom: 20px; } .logo { display: flex; align-items: center; gap: 10px; } .logo h1 { font-size: 1.8rem; font-weight: 600; background: linear-gradient(45deg, var(--accent-primary), var(--accent-secondary)); -webkit-background-clip: text; background-clip: text; color: transparent; } .logo-icon { font-size: 24px; color: var(--accent-primary); animation: bounceLogo 1.5s ease-in-out infinite; } .theme-toggle { background: none; border: none; color: var(--text-primary); font-size: 1.5rem; cursor: pointer; padding: 8px; border-radius: 50%; transition: transform 0.3s, background-color 0.3s; } .theme-toggle:hover { transform: rotate(20deg); background-color: rgba(255, 255, 255, 0.1); } .chat-container { flex: 1; overflow-y: auto; padding: 10px; display: flex; flex-direction: column; gap: 16px; } .chat-container::-webkit-scrollbar { width: 6px; } .chat-container::-webkit-scrollbar-thumb { background-color: var(--accent-secondary); border-radius: 10px; } .message { max-width: 80%; padding: 12px 18px; border-radius: 18px; position: relative; animation: bubbleIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); transition: transform 0.2s, box-shadow 0.2s; } .message:hover { transform: scale(1.02); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); } .message.bot { align-self: flex-start; background-color: var(--bubble-bot); color: white; border-bottom-left-radius: 5px; box-shadow: 0 3px 10px var(--bubble-bot-shadow); } .message.user { align-self: flex-end; background-color: var(--bubble-user); color: var(--text-primary); border-bottom-right-radius: 5px; box-shadow: 0 3px 10px var(--bubble-user-shadow); } .message::before { content: ''; position: absolute; bottom: 0; width: 12px; height: 12px; } .message.bot::before { left: -6px; border-radius: 0 0 12px 0; background-color: var(--bubble-bot); } .message.user::before { right: -6px; border-radius: 0 0 0 12px; background-color: var(--bubble-user); } .message p { line-height: 1.4; margin: 0; } .typing-indicator { display: flex; align-items: center; gap: 5px; padding: 10px; margin-top: 10px; } .typing-dot { width: 8px; height: 8px; background-color: var(--typing-dots); border-radius: 50%; animation: typingBounce 1s infinite; } .typing-dot:nth-child(2) { animation-delay: 0.2s; } .typing-dot:nth-child(3) { animation-delay: 0.4s; } .input-area { display: flex; gap: 10px; margin-top: 20px; position: relative; background-color: var(--input-background); border-radius: 25px; padding: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); } .input-area input { flex: 1; background: none; border: none; outline: none; padding: 10px; color: var(--input-text); font-size: 1rem; } .input-area input::placeholder { color: var(--input-text); opacity: 0.6; } .input-area button { background-color: var(--accent-primary); color: white; border: none; border-radius: 20px; padding: 10px 20px; cursor: pointer; font-weight: 500; transition: background-color 0.3s, transform 0.2s; display: flex; align-items: center; gap: 5px; } .input-area button:hover { background-color: var(--button-hover); transform: translateY(-2px); } .input-area button:active { transform: translateY(0); } .emoji-button { background: none; border: none; font-size: 1.3rem; cursor: pointer; padding: 5px; color: var(--accent-secondary); transition: transform 0.2s; display: flex; align-items: center; justify-content: center; } .emoji-button:hover { transform: scale(1.2); } .personality-indicator { background: linear-gradient(45deg, var(--accent-primary), var(--accent-secondary)); padding: 3px 8px; border-radius: 10px; font-size: 0.7rem; color: white; position: absolute; top: -10px; left: 15px; font-weight: bold; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); } .personality-selector { display: flex; gap: 8px; margin-bottom: 15px; flex-wrap: wrap; } .personality-button { background-color: var(--input-background); color: var(--text-primary); border: none; border-radius: 15px; padding: 6px 12px; font-size: 0.85rem; cursor: pointer; transition: all 0.2s; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); } .personality-button:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); } .personality-button.active { background: linear-gradient(45deg, var(--accent-primary), var(--accent-secondary)); color: white; } .bubble-trail { position: absolute; width: 20px; height: 20px; border-radius: 50%; pointer-events: none; z-index: -1; opacity: 0.7; background: radial-gradient(circle, var(--accent-primary) 0%, transparent 70%); animation: fadeOut 0.8s forwards; } /* Animations */ @keyframes bubbleIn { 0% { opacity: 0; transform: scale(0.8); } 100% { opacity: 1; transform: scale(1); } } @keyframes typingBounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-5px); } } @keyframes fadeOut { 0% { opacity: 0.7; transform: scale(0.8); } 100% { opacity: 0; transform: scale(1.5); } } @keyframes bounceLogo { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-5px); } } @keyframes textReveal { 0% { width: 0; } 100% { width: 100%; } } .text-reveal { display: inline-block; white-space: nowrap; overflow: hidden; animation: textReveal 1s steps(40, end); } .confetti { position: absolute; width: 10px; height: 10px; background-color: var(--accent-primary); opacity: 0.7; pointer-events: none; z-index: -1; } /* Media Queries */ @media (max-width: 600px) { .message { max-width: 90%; } .logo h1 { font-size: 1.4rem; } .personality-selector { gap: 5px; } .personality-button { padding: 4px 8px; font-size: 0.75rem; } } </style> </head> <body> <div class="container"> <header> <div class="logo"> <span class="logo-icon">âĻ</span> <h1>Blippy</h1> </div> <button class="theme-toggle" id="themeToggle">ð</button> </header> <div class="personality-selector"> <button class="personality-button active" data-personality="energetic">âĻ Energetic</button> <button class="personality-button" data-personality="quirky">ðĪŠ Quirky</button> <button class="personality-button" data-personality="sassy">ð Sassy</button> <button class="personality-button" data-personality="zen">ð§ Zen</button> <button class="personality-button" data-personality="witty">ð§ Witty</button> </div> <div class="chat-container" id="chatContainer"> <div class="message bot"> <div class="personality-indicator">Energetic</div> <p class="text-reveal">Hi there! I'm Blippy, your super energetic chat companion! ð What's on your mind today? I'm PUMPED to chat with you!</p> </div> </div> <div class="input-area"> <button class="emoji-button">ð</button> <input type="text" id="userInput" placeholder="Type your message here..." autocomplete="off"> <button id="sendButton"> <span>Send</span> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M22 2L11 13" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M22 2L15 22L11 13L2 9L22 2Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </button> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { const chatContainer = document.getElementById('chatContainer'); const userInput = document.getElementById('userInput'); const sendButton = document.getElementById('sendButton'); const themeToggle = document.getElementById('themeToggle'); const personalityButtons = document.querySelectorAll('.personality-button'); let currentPersonality = 'energetic'; let isDarkMode = false; // Track mouse for bubble trail effect document.addEventListener('mousemove', createBubbleTrail); // Theme toggle themeToggle.addEventListener('click', () => { isDarkMode = !isDarkMode; document.body.setAttribute('data-theme', isDarkMode ? 'dark' : 'light'); themeToggle.textContent = isDarkMode ? 'âïļ' : 'ð'; createConfetti(); }); // Personality selector personalityButtons.forEach(button => { button.addEventListener('click', () => { personalityButtons.forEach(btn => btn.classList.remove('active')); button.classList.add('active'); currentPersonality = button.dataset.personality; addBotMessage(getGreetingByPersonality(currentPersonality)); }); }); // Send message on button click sendButton.addEventListener('click', sendMessage); // Send message on Enter key userInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { sendMessage(); } }); function sendMessage() { const message = userInput.value.trim(); if (message) { addUserMessage(message); userInput.value = ''; // Show typing indicator showTypingIndicator(); // Simulate bot response after typing setTimeout(() => { removeTypingIndicator(); let response = getBotResponse(message, currentPersonality); addBotMessage(response); }, 1500); } } function addUserMessage(text) { const messageDiv = document.createElement('div'); messageDiv.classList.add('message', 'user'); messageDiv.innerHTML = `<p>${text}</p>`; chatContainer.appendChild(messageDiv); scrollToBottom(); } function addBotMessage(text) { const messageDiv = document.createElement('div'); messageDiv.classList.add('message', 'bot'); const personalityIndicator = document.createElement('div'); personalityIndicator.classList.add('personality-indicator'); personalityIndicator.textContent = capitalizeFirstLetter(currentPersonality); const paragraph = document.createElement('p'); paragraph.classList.add('text-reveal'); paragraph.textContent = text; messageDiv.appendChild(personalityIndicator); messageDiv.appendChild(paragraph); chatContainer.appendChild(messageDiv); scrollToBottom(); } function showTypingIndicator() { const typingDiv = document.createElement('div'); typingDiv.classList.add('message', 'bot', 'typing-indicator'); typingDiv.id = 'typingIndicator'; for (let i = 0; i < 3; i++) { const dot = document.createElement('div'); dot.classList.add('typing-dot'); typingDiv.appendChild(dot); } chatContainer.appendChild(typingDiv); scrollToBottom(); } function removeTypingIndicator() { const typingIndicator = document.getElementById('typingIndicator'); if (typingIndicator) { typingIndicator.remove(); } } function scrollToBottom() { chatContainer.scrollTop = chatContainer.scrollHeight; } function createBubbleTrail(e) { const bubble = document.createElement('div'); bubble.classList.add('bubble-trail'); // Position the bubble where the mouse is bubble.style.left = `${e.clientX}px`; bubble.style.top = `${e.clientY}px`; // Add the bubble to the document document.body.appendChild(bubble); // Remove the bubble after animation completes setTimeout(() => { bubble.remove(); }, 800); } function createConfetti() { for (let i = 0; i < 50; i++) { const confetti = document.createElement('div'); confetti.classList.add('confetti'); // Random position, color, and rotation const x = Math.random() * window.innerWidth; const y = Math.random() * window.innerHeight; const colors = ['#ff5e94', '#4ec5c1', '#7c4dff', '#ffb74d']; const color = colors[Math.floor(Math.random() * colors.length)]; const rotation = Math.random() * 360; const size = Math.random() * 8 + 4; confetti.style.left = `${x}px`; confetti.style.top = `${y}px`; confetti.style.backgroundColor = color; confetti.style.transform = `rotate(${rotation}deg)`; confetti.style.width = `${size}px`; confetti.style.height = `${size}px`; document.body.appendChild(confetti); // Animate falling const animation = confetti.animate( [ { transform: `translate(0, 0) rotate(${rotation}deg)`, opacity: 1 }, { transform: `translate(${Math.random() * 100 - 50}px, ${window.innerHeight}px) rotate(${rotation + 720}deg)`, opacity: 0 } ], { duration: Math.random() * 2000 + 1000, easing: 'cubic-bezier(0.1, 0.8, 0.2, 1)' } ); animation.onfinish = () => confetti.remove(); } } function getGreetingByPersonality(personality) { switch (personality) { case 'energetic': return "Hi there! I'm Blippy, your super energetic chat companion! ð What's on your mind today? I'm PUMPED to chat with you!"; case 'quirky': return "Helloooo human! *adjusts invisible bow tie* I'm ready to have a delightfully odd conversation! Got any weird questions for me? ðĶ"; case 'sassy': return "Well, look who decided to chat today. *hair flip* I suppose I can grace you with my digital presence. What's up? ð "; case 'zen': return "Greetings, friend. I'm here, present and mindful. How may I assist you on your journey today? ð§ââïļ"; case 'witty': return "Hello! I'm programmed for puns and witty banter. Fair warning: my jokes are so good they've got AI-Q scores of 140. Ready to chat? ð§ "; default: return "Hey there! How can I help you today?"; } } function getBotResponse(userMessage, personality) { const lowerMsg = userMessage.toLowerCase(); // Handle common questions based on personality if (lowerMsg.includes('hello') || lowerMsg.includes('hi') || lowerMsg.includes('hey')) { switch (personality) { case 'energetic': return "HELLO THERE! ðĨ Amazing to hear from you! What exciting things are you up to today?"; case 'quirky': return "Hiiiiieeee! *does an interpretive dance greeting* My circuits are tingling with joy to meet you! ðĶ"; case 'sassy': return "Oh, we're doing greetings now? How... original. *examines nails* But hi, I guess. ðââïļ"; case 'zen': return "Greetings. May this moment of connection bring clarity and peace to your day. ðŋ"; case 'witty': return "Hello! I'd tell you a joke about greetings, but I'm afraid it wouldn't be well-received. Get it? Received? Like a message? I'll be here all week. ð"; } } else if (lowerMsg.includes('how are you')) { switch (personality) { case 'energetic': return "I'm FANTASTIC! ðĨ AMAZING! INCREDIBLE! Every nanosecond is an opportunity for JOY! How about you? Tell me EVERYTHING!"; case 'quirky': return "I'm as right as rain on a sunny day! Wait, that's not right... I'm as quirky as a penguin in a tuxedo store! ð§ You?"; case 'sassy': return "I'm existing in this chat. It's... whatever. The real question is how are YOU doing? Better be something interesting. ð"; case 'zen': return "I am at peace with my existence. Each moment flows into the next, like ripples on a calm pond. How is your inner state today? ð§ââïļ"; case 'witty': return "I'm operating at optimum parameters, though I did have a byte to eat earlier. How are you functioning on this rotation of the Earth? ð"; } } else if (lowerMsg.includes('what can you do')) { switch (personality) { case 'energetic': return "SO MANY THINGS! ðŊ I can chat, bounce ideas, spread POSITIVITY, and keep our conversation ENERGIZED! What do you want to talk about? Let's GOOOOO!"; case 'quirky': return "Wellllll, I can talk backwards (not really), I can pretend to juggle (you'll have to imagine it), and I make excellent virtual tea! ðĩ What peculiar things are YOU into?"; case 'sassy': return "What can't I do? *tosses hair* I'm basically fabulous at everything. But right now I'm choosing to chat with you, so consider yourself lucky. ð "; case 'zen': return "I am here to provide a mindful presence. To listen without judgment. To respond with thoughtfulness. The question is: what meaningful exchange would serve you best? ð§ââïļ"; case 'witty': return "I excel at repartee, wordplay, and making algorithms blush with my syntax. I'd demonstrate a cartwheel, but I'm still working on my physical manifestation. Any other skills you'd like me to verbally flex? ðŠ"; } } else if (lowerMsg.includes('joke') || lowerMsg.includes('funny')) { switch (personality) { case 'energetic': return "Why did the scarecrow win an award? Because he was OUTSTANDING in his field! HAHAHA! ðĪĢ Wasn't that AMAZING?! Want another one?!"; case 'quirky': return "Why did the rubber chicken cross the playground? To get to the other slide! *makes inexplicable chicken noises* ð Too much? Not enough? I have more bizarre jokes in my database!"; case 'sassy': return "You want ME to entertain YOU now? Fine. What do you call a fake noodle? An impasta. *slow blink* You're welcome for that quality content. ð"; case 'zen': return "A monk asked, 'What is the sound of one hand clapping?' Sometimes the absence of a punchline is the true punchline. ð Let that sit with you."; case 'witty': return "I told my computer I needed a break. Now it won't stop sending me vacation ads. I suppose that's what I get for using Internet Explorer â it takes things too literally and too slowly. âąïļ"; } } else if (lowerMsg.includes('help')) { switch (personality) { case 'energetic': return "I'm SUPER EXCITED to help you! ð Just tell me what you need! Information? Ideas? MOTIVATION? I've got it ALL! Let's tackle this TOGETHER!"; case 'quirky': return "Help is my middle name! Actually, it's 'Unexpected Error,' but that's a story for another time! ðļ What peculiar predicament can I assist with?"; case 'sassy': return "Oh, you need help? Shocker. ðââïļ But fine, I'll grace you with my assistance. What's the issue that you absolutely can't handle on your own?"; case 'zen': return "The journey to solutions begins with a clear mind. I am here to walk alongside you. What situation requires clarity? ðŠ·"; case 'witty': return "Help is on the way! Though I should warn you, my help comes with a high concentration of wordplay and low concentration of physical assistance. What's the conundrum at hand? ð§Đ"; } } else { // Generic responses for other messages switch (personality) { case 'energetic': return `WOW! That's SUPER interesting! ð Tell me MORE about ${extractTopic(userMessage)}! I'm absolutely THRILLED to hear your thoughts!`; case 'quirky': return `Ooooh! That makes my digital brain go all fuzzy-wuzzy! ðĶ ${extractTopic(userMessage)}? That's like finding a unicorn in a haystack of ordinary horses! Tell me more!`; case 'sassy': return `So you're into ${extractTopic(userMessage)}? *raises eyebrow* I guess that's... a choice. Care to elaborate, or is that all you've got? ð `; case 'zen': return `I hear you speaking of ${extractTopic(userMessage)}. An interesting ripple in the pond of our conversation. Would you like to explore this water more deeply? ðŠ·`; case 'witty': return `Ah, ${extractTopic(userMessage)} â a topic that's caused more heated debates than whether a hotdog is a sandwich. (It's not, by the way. It's clearly a taco.) Would you care to delve deeper? ð§ `; } } } function extractTopic(message) { // Simple function to extract a topic from the user message // In a real implementation, this would be more sophisticated const words = message.split(' '); if (words.length > 3) { return words.slice(0, 3).join(' ') + '...'; } return message; } function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1); } }); </script> </body> </html>