Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Minimal Dark Glass Chat</title> | |
| <!-- Import Google Fonts: Nunito for that airy, soft feel --> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Nunito:wght@300;400;500;600&display=swap" rel="stylesheet"> | |
| <!-- Import FontAwesome for Icons --> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| :root { | |
| /* Palette: Muted Dark Gradients & Dark Glass */ | |
| --bg-gradient-start: #1f2229; | |
| --bg-gradient-end: #2c303a; | |
| /* Muted Dark Blob Colors */ | |
| --blob-1: #2b3244; | |
| --blob-2: #1e2536; | |
| /* Dark Glass Variables */ | |
| --glass-bg: rgba(30, 35, 45, 0.4); | |
| --glass-border: rgba(255, 255, 255, 0.08); | |
| --glass-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.2); | |
| --glass-blur: blur(12px); | |
| --sidebar-bg: rgba(0, 0, 0, 0.2); | |
| /* Typography */ | |
| --font-main: 'Nunito', sans-serif; | |
| --text-primary: #e0e0e0; /* Soft white/gray */ | |
| --text-secondary: #9aa0a6; /* Muted gray */ | |
| /* Message Bubbles - Brightness differentiation, no color change */ | |
| --msg-sent-bg: rgba(255, 255, 255, 0.12); /* Lighter glass */ | |
| --msg-received-bg: rgba(0, 0, 0, 0.2); /* Darker glass */ | |
| --msg-text: #f0f0f0; | |
| /* Spacing */ | |
| --spacing-sm: 8px; | |
| --spacing-md: 16px; | |
| --spacing-lg: 24px; | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: var(--font-main); | |
| background: linear-gradient(135deg, var(--bg-gradient-start) 0%, var(--bg-gradient-end) 100%); | |
| height: 100vh; | |
| width: 100vw; | |
| overflow: hidden; | |
| color: var(--text-primary); | |
| position: relative; | |
| } | |
| /* | |
| Background: Muted dark gradients | |
| Removed animations, static only | |
| */ | |
| .ambient-bg { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| z-index: -1; | |
| /* Deep, dark, static gradient blobs */ | |
| background: radial-gradient(circle at 10% 20%, rgba(43, 50, 68, 0.4) 0%, transparent 50%), | |
| radial-gradient(circle at 90% 80%, rgba(30, 37, 54, 0.4) 0%, transparent 50%); | |
| pointer-events: none; | |
| } | |
| /* Layout Container */ | |
| .app-container { | |
| display: flex; | |
| width: 95%; | |
| height: 92%; | |
| max-width: 1400px; | |
| margin: 4vh auto; | |
| background: var(--glass-bg); | |
| backdrop-filter: var(--glass-blur); | |
| -webkit-backdrop-filter: var(--glass-blur); | |
| border: 1px solid var(--glass-border); | |
| border-radius: 24px; | |
| box-shadow: var(--glass-shadow); | |
| overflow: hidden; | |
| position: relative; | |
| } | |
| /* --- Sidebar --- */ | |
| .sidebar { | |
| width: 280px; | |
| min-width: 280px; | |
| border-right: 1px solid var(--glass-border); | |
| display: flex; | |
| flex-direction: column; | |
| padding: var(--spacing-md); | |
| background: var(--sidebar-bg); | |
| transition: transform 0.3s ease; | |
| } | |
| .contact-list { | |
| list-style: none; | |
| margin-top: var(--spacing-md); | |
| overflow-y: auto; | |
| flex-grow: 1; | |
| } | |
| /* Dark Scrollbar */ | |
| .contact-list::-webkit-scrollbar { | |
| width: 4px; | |
| } | |
| .contact-list::-webkit-scrollbar-thumb { | |
| background: rgba(255, 255, 255, 0.1); | |
| border-radius: 4px; | |
| } | |
| .contact-item { | |
| display: flex; | |
| align-items: center; | |
| padding: 10px; | |
| margin-bottom: 4px; | |
| border-radius: 12px; | |
| cursor: pointer; | |
| transition: background 0.2s ease; | |
| position: relative; | |
| } | |
| /* Subtle hover states - no strong contrast */ | |
| .contact-item:hover { | |
| background: rgba(255, 255, 255, 0.05); | |
| } | |
| .contact-item.active { | |
| background: rgba(255, 255, 255, 0.08); | |
| } | |
| /* Smaller avatars */ | |
| .avatar { | |
| width: 36px; | |
| height: 36px; | |
| border-radius: 50%; | |
| object-fit: cover; | |
| margin-right: 12px; | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| opacity: 0.9; | |
| } | |
| .contact-info { | |
| display: flex; | |
| flex-direction: column; | |
| overflow: hidden; | |
| } | |
| .contact-name { | |
| font-size: 0.95rem; | |
| font-weight: 500; | |
| color: var(--text-primary); | |
| } | |
| .last-msg { | |
| font-size: 0.8rem; | |
| color: var(--text-secondary); | |
| white-space: nowrap; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| font-weight: 400; /* Reduced variation */ | |
| } | |
| /* --- Main Chat Area --- */ | |
| .chat-area { | |
| flex: 1; | |
| display: flex; | |
| flex-direction: column; | |
| position: relative; | |
| } | |
| /* --- Header --- */ | |
| header { | |
| height: 56px; /* Reduced height */ | |
| min-height: 56px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: 0 var(--spacing-lg); | |
| border-bottom: 1px solid var(--glass-border); | |
| background: rgba(0, 0, 0, 0.15); /* Darker translucent background */ | |
| } | |
| .chat-title h2 { | |
| font-size: 1.1rem; | |
| font-weight: 600; | |
| color: var(--text-primary); | |
| } | |
| .chat-title span { | |
| font-size: 0.8rem; | |
| color: var(--text-secondary); | |
| font-weight: 400; | |
| margin-left: 8px; | |
| } | |
| /* Branding - Subtle */ | |
| .branding { | |
| font-size: 0.75rem; | |
| font-weight: 400; | |
| opacity: 0.7; | |
| } | |
| .branding a { | |
| text-decoration: none; | |
| color: var(--text-secondary); | |
| transition: color 0.2s; | |
| } | |
| .branding a:hover { | |
| color: var(--text-primary); | |
| } | |
| /* --- Messages Area --- */ | |
| .messages-container { | |
| flex: 1; | |
| padding: var(--spacing-lg); | |
| overflow-y: auto; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 12px; | |
| } | |
| .message { | |
| max-width: 70%; | |
| padding: 10px 16px; | |
| font-size: 0.95rem; | |
| line-height: 1.5; | |
| position: relative; | |
| animation: slideUpFade 0.4s cubic-bezier(0.2, 0.8, 0.2, 1) forwards; | |
| opacity: 0; | |
| transform: translateY(10px); | |
| /* No borders */ | |
| } | |
| @keyframes slideUpFade { | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| .message.received { | |
| align-self: flex-start; | |
| background: var(--msg-received-bg); | |
| color: var(--msg-text); | |
| border-radius: 18px 18px 18px 4px; | |
| } | |
| .message.sent { | |
| align-self: flex-end; | |
| background: var(--msg-sent-bg); | |
| color: var(--msg-text); | |
| border-radius: 18px 18px 4px 18px; | |
| } | |
| .message-time { | |
| display: block; | |
| font-size: 0.65rem; | |
| margin-top: 4px; | |
| text-align: right; | |
| opacity: 0.5; | |
| } | |
| /* --- Input Area --- */ | |
| .input-area { | |
| padding: var(--spacing-md) var(--spacing-lg); | |
| background: rgba(0, 0, 0, 0.2); | |
| border-top: 1px solid var(--glass-border); | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| } | |
| .input-wrapper { | |
| flex: 1; | |
| position: relative; | |
| } | |
| input[type="text"] { | |
| width: 100%; | |
| padding: 14px 20px; | |
| border-radius: 30px; | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| background: rgba(0, 0, 0, 0.3); | |
| font-family: var(--font-main); | |
| font-size: 0.95rem; | |
| color: var(--text-primary); | |
| outline: none; | |
| transition: all 0.2s ease; | |
| } | |
| input[type="text"]:focus { | |
| background: rgba(0, 0, 0, 0.4); | |
| border-color: rgba(255, 255, 255, 0.2); | |
| box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.05); | |
| } | |
| input[type="text"]::placeholder { | |
| color: rgba(255, 255, 255, 0.3); | |
| } | |
| .send-btn { | |
| width: 46px; | |
| height: 46px; | |
| border-radius: 50%; | |
| border: none; | |
| background: #4a5568; /* Muted dark blue-grey */ | |
| color: white; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| transition: background 0.2s; | |
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); | |
| /* No hover scaling */ | |
| } | |
| .send-btn:hover { | |
| background: #5a6578; | |
| } | |
| .send-btn:active { | |
| transform: scale(0.95); | |
| } | |
| /* Responsive Design */ | |
| @media (max-width: 768px) { | |
| .app-container { | |
| width: 100%; | |
| height: 100%; | |
| border-radius: 0; | |
| border: none; | |
| margin: 0; | |
| } | |
| .sidebar { | |
| position: absolute; | |
| left: 0; | |
| top: 0; | |
| bottom: 0; | |
| z-index: 10; | |
| background: rgba(20, 24, 30, 0.95); | |
| backdrop-filter: blur(20px); | |
| transform: translateX(-100%); | |
| box-shadow: 2px 0 10px rgba(0, 0, 0, 0.3); | |
| } | |
| .sidebar.open { | |
| transform: translateX(0); | |
| } | |
| header { | |
| padding: 0 var(--spacing-md); | |
| } | |
| .mobile-menu-toggle { | |
| display: block; | |
| margin-right: 12px; | |
| color: var(--text-primary); | |
| cursor: pointer; | |
| font-size: 1.2rem; | |
| } | |
| } | |
| @media (min-width: 769px) { | |
| .mobile-menu-toggle { | |
| display: none; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="ambient-bg"></div> | |
| <div class="app-container"> | |
| <!-- Sidebar --> | |
| <aside class="sidebar" id="sidebar"> | |
| <!-- Contacts --> | |
| <ul class="contact-list" id="contactList"> | |
| <!-- Contacts generated by JS --> | |
| </ul> | |
| </aside> | |
| <!-- Main Chat --> | |
| <main class="chat-area"> | |
| <!-- Header --> | |
| <header> | |
| <div style="display: flex; align-items: center;"> | |
| <i class="fas fa-bars mobile-menu-toggle" id="menuToggle"></i> | |
| <div class="chat-title"> | |
| <h2 id="activeChatName">Select a chat</h2> | |
| <span id="activeChatStatus"></span> | |
| </div> | |
| </div> | |
| <div class="branding"> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">Built with anycoder</a> | |
| </div> | |
| </header> | |
| <!-- Messages --> | |
| <div class="messages-container" id="messagesContainer"> | |
| <!-- Messages generated by JS --> | |
| <div class="message received"> | |
| Hello! Select a friend to start chatting with the minimal dark glass interface. | |
| </div> | |
| </div> | |
| <!-- Input --> | |
| <div class="input-area"> | |
| <div class="input-wrapper"> | |
| <input type="text" id="messageInput" placeholder="Type a message..." autocomplete="off"> | |
| </div> | |
| <button class="send-btn" id="sendBtn"> | |
| <i class="fas fa-paper-plane"></i> | |
| </button> | |
| </div> | |
| </main> | |
| </div> | |
| <script> | |
| // Data Mockup | |
| const contacts = [ | |
| { id: 1, name: "Alice Moore", avatar: "https://picsum.photos/seed/alice/100/100", lastMsg: "See you tomorrow!", status: "Online" }, | |
| { id: 2, name: "Bob Smith", avatar: "https://picsum.photos/seed/bob/100/100", lastMsg: "Is the project ready?", status: "Away" }, | |
| { id: 3, name: "Clara Oswald", avatar: "https://picsum.photos/seed/clara/100/100", lastMsg: "That looks amazing.", status: "Online" }, | |
| { id: 4, name: "David Tennant", avatar: "https://picsum.photos/seed/david/100/100", lastMsg: "Fantastic!", status: "Offline" }, | |
| { id: 5, name: "Emma Watson", avatar: "https://picsum.photos/seed/emma/100/100", lastMsg: "Book club is on Friday.", status: "Online" } | |
| ]; | |
| const chatHistory = { | |
| 1: [ | |
| { type: 'received', text: "Hey! How are you doing?", time: "10:00 AM" }, | |
| { type: 'sent', text: "I'm doing great, thanks! Just redesigning this UI.", time: "10:02 AM" }, | |
| { type: 'received', text: "Oh nice, is it the glassmorphism one?", time: "10:03 AM" }, | |
| { type: 'sent', text: "Yeah, making it much darker and quieter.", time: "10:05 AM" }, | |
| { type: 'received', text: "See you tomorrow!", time: "10:06 AM" } | |
| ], | |
| 2: [ | |
| { type: 'received', text: "Is the project ready?", time: "09:30 AM" } | |
| ], | |
| 3: [ | |
| { type: 'sent', text: "I sent you the new mockups.", time: "Yesterday" }, | |
| { type: 'received', text: "That looks amazing.", time: "Yesterday" } | |
| ] | |
| }; | |
| let activeContactId = 1; | |
| // DOM Elements | |
| const contactListEl = document.getElementById('contactList'); | |
| const messagesContainerEl = document.getElementById('messagesContainer'); | |
| const activeChatNameEl = document.getElementById('activeChatName'); | |
| const activeChatStatusEl = document.getElementById('activeChatStatus'); | |
| const messageInput = document.getElementById('messageInput'); | |
| const sendBtn = document.getElementById('sendBtn'); | |
| const sidebar = document.getElementById('sidebar'); | |
| const menuToggle = document.getElementById('menuToggle'); | |
| // Initialize | |
| function init() { | |
| renderContacts(); | |
| loadChat(activeContactId); | |
| // Mobile Menu Toggle | |
| menuToggle.addEventListener('click', () => { | |
| sidebar.classList.toggle('open'); | |
| }); | |
| // Close sidebar when clicking outside on mobile | |
| document.addEventListener('click', (e) => { | |
| if (window.innerWidth <= 768) { | |
| if (!sidebar.contains(e.target) && !menuToggle.contains(e.target)) { | |
| sidebar.classList.remove('open'); | |
| } | |
| } | |
| }); | |
| } | |
| // Render Sidebar | |
| function renderContacts() { | |
| contactListEl.innerHTML = ''; | |
| contacts.forEach(contact => { | |
| const li = document.createElement('li'); | |
| li.className = `contact-item ${contact.id === activeContactId ? 'active' : ''}`; | |
| li.onclick = () => { | |
| activeContactId = contact.id; | |
| renderContacts(); // Re-render to update active state | |
| loadChat(contact.id); | |
| if (window.innerWidth <= 768) sidebar.classList.remove('open'); | |
| }; | |
| li.innerHTML = ` | |
| <img src="${contact.avatar}" alt="${contact.name}" class="avatar"> | |
| <div class="contact-info"> | |
| <span class="contact-name">${contact.name}</span> | |
| <span class="last-msg">${contact.lastMsg}</span> | |
| </div> | |
| `; | |
| contactListEl.appendChild(li); | |
| }); | |
| } | |
| // Load Chat Messages | |
| function loadChat(id) { | |
| const contact = contacts.find(c => c.id === id); | |
| activeChatNameEl.textContent = contact.name; | |
| activeChatStatusEl.textContent = contact.status; | |
| messagesContainerEl.innerHTML = ''; | |
| const messages = chatHistory[id] || []; | |
| if (messages.length === 0) { | |
| const emptyState = document.createElement('div'); | |
| emptyState.className = 'message received'; | |
| emptyState.style.textAlign = 'center'; | |
| emptyState.style.alignSelf = 'center'; | |
| emptyState.style.background = 'transparent'; | |
| emptyState.style.color = 'rgba(255, 255, 255, 0.3)'; | |
| emptyState.textContent = "Start a conversation with " + contact.name; | |
| messagesContainerEl.appendChild(emptyState); | |
| } else { | |
| messages.forEach(msg => { | |
| appendMessage(msg.text, msg.type, msg.time, false); | |
| }); | |
| } | |
| scrollToBottom(); | |
| } | |
| // Append Message to DOM | |
| function appendMessage(text, type, time = null, animate = true) { | |
| const div = document.createElement('div'); | |
| div.className = `message ${type}`; | |
| // Format time if not provided | |
| const now = new Date(); | |
| const timeString = time || now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); | |
| div.innerHTML = ` | |
| ${text} | |
| <span class="message-time">${timeString}</span> | |
| `; | |
| // Disable animation if loading history | |
| if (!animate) { | |
| div.style.animation = 'none'; | |
| div.style.opacity = '1'; | |
| div.style.transform = 'translateY(0)'; | |
| } | |
| messagesContainerEl.appendChild(div); | |
| scrollToBottom(); | |
| } | |
| // Scroll Helper | |
| function scrollToBottom() { | |
| messagesContainerEl.scrollTop = messagesContainerEl.scrollHeight; | |
| } | |
| // Send Message Logic | |
| function handleSend() { | |
| const text = messageInput.value.trim(); | |
| if (!text) return; | |
| // Add user message | |
| appendMessage(text, 'sent'); | |
| // Update history (mock) | |
| if (!chatHistory[activeContactId]) chatHistory[activeContactId] = []; | |
| chatHistory[activeContactId].push({ type: 'sent', text: text, time: new Date().toLocaleTimeString() }); | |
| // Update sidebar preview | |
| const contact = contacts.find(c => c.id === activeContactId); | |
| contact.lastMsg = "You: " + text; | |
| renderContacts(); | |
| messageInput.value = ''; | |
| messageInput.focus(); | |
| // Mock Reply | |
| setTimeout(() => { | |
| const replies = [ | |
| "That sounds interesting!", | |
| "I'll have to check that out.", | |
| "Cool.", | |
| "Can you tell me more?", | |
| "Okay, noted." | |
| ]; | |
| const randomReply = replies[Math.floor(Math.random() * replies.length)]; | |
| appendMessage(randomReply, 'received'); | |
| chatHistory[activeContactId].push({ type: 'received', text: randomReply, time: new Date().toLocaleTimeString() }); | |
| contact.lastMsg = randomReply; | |
| renderContacts(); | |
| }, 1500 + Math.random() * 1000); | |
| } | |
| // Event Listeners | |
| sendBtn.addEventListener('click', handleSend); | |
| messageInput.addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter') handleSend(); | |
| }); | |
| // Start App | |
| init(); | |
| </script> | |
| </body> | |
| </html> |