Typing text effects can add a dynamic and engaging element to your web design, capturing user attention instantly. These effects simulate the look of someone typing on a keyboard, creating a sense of immediacy and interaction.
In this article, we'll explore 10 stunning typing text effect examples that can elevate your website's user experience. From simple animations to more complex sequences, these examples will inspire you to incorporate this captivating feature into your own projects.
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
Subframe's drag-and-drop interface and intuitive, responsive canvas make designing your own Typing Text Effect a breeze. Loved by designers and developers alike, Subframe ensures pixel-perfect UI every time.
Start for free and elevate your web design today!
CODE6
Here's the code:
CODETEXT6
CODE7
Here's the code:
CODETEXT7
CODE8
Here's the code:
CODETEXT8
CODE9
Here's the code:
CODETEXT9
CODE10
Here's the code:
CODETEXT10
Ready to elevate your web design? With Subframe, you can create stunning UIs, including Typing Text Effects, with unmatched efficiency and precision.
Experience the ease of a drag-and-drop interface and start creating pixel-perfect designs immediately. Start for free and bring your vision to life today!
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Retro Portfolio Typewriter</title> <style> :root { --bg-color: #F8F3E6; --text-color: #3A3335; --accent-color: #D64933; --secondary-color: #7B9E89; --tertiary-color: #C17817; } * { margin: 0; padding: 0; box-sizing: border-box; } body { background-color: var(--bg-color); font-family: 'Courier New', monospace; color: var(--text-color); height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 1.5rem; overflow-x: hidden; background-image: radial-gradient(var(--secondary-color) 1px, transparent 1px), radial-gradient(var(--secondary-color) 1px, transparent 1px); background-size: 40px 40px; background-position: 0 0, 20px 20px; background-blend-mode: multiply; background-opacity: 0.1; } .container { width: 100%; max-width: 650px; height: 100%; max-height: 650px; display: flex; flex-direction: column; overflow: hidden; } .vintage-frame { background-color: #E8DED1; border: 8px solid #8C593B; border-radius: 5px; box-shadow: 0 8px 32px rgba(58, 51, 53, 0.2), inset 0 0 10px rgba(58, 51, 53, 0.1); padding: 1.5rem; flex-grow: 1; overflow-y: auto; position: relative; transition: all 0.3s ease; } /* Custom scrollbar */ .vintage-frame::-webkit-scrollbar { width: 12px; } .vintage-frame::-webkit-scrollbar-track { background: #D9CFC1; border-radius: 10px; } .vintage-frame::-webkit-scrollbar-thumb { background-color: #8C593B; border-radius: 10px; border: 3px solid #D9CFC1; } .header { text-align: center; margin-bottom: 1.5rem; position: relative; } .title { font-size: 2.2rem; color: var(--accent-color); letter-spacing: -1px; text-transform: uppercase; margin-bottom: 0.5rem; text-shadow: 1px 1px 0px #8C593B; position: relative; display: inline-block; } .title::after { content: ""; position: absolute; height: 3px; width: 110%; background-color: var(--text-color); bottom: -5px; left: -5%; } .subtitle { font-size: 1rem; color: var(--tertiary-color); font-style: italic; } .typewriter-container { background: rgba(248, 243, 230, 0.9); padding: 1.5rem; border-radius: 2px; position: relative; min-height: 300px; margin-bottom: 1.5rem; box-shadow: inset 0 0 8px rgba(58, 51, 53, 0.15); } .typewriter-text { font-size: 1.1rem; line-height: 1.6; white-space: pre-wrap; opacity: 0.95; } .cursor { display: inline-block; width: 10px; height: 22px; background-color: var(--text-color); margin-left: 2px; animation: blink 1s step-end infinite; vertical-align: text-bottom; } @keyframes blink { from, to { opacity: 1; } 50% { opacity: 0; } } .paper-texture { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAAUVBMVEWFhYWDg4N3d3dtbW17e3t1dXWBgYGHh4d5eXlzc3OLi4ubm5uVlZWPj4+NjY19fX2JiYl/f39ra2uRkZGZmZlpaWmXl5dvb29xcXGTk5NnZ2c8TV1mAAAAG3RSTlNAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAvEOwtAAAFVklEQVR4XpWWB67c2BUFb3g557T/hRo9/WUMZHlgr4Bg8Z4qQgQJlHI4A8SzFVrapvmTF9O7dmYRFZ60YiBhJRCgh1FYhiLAmdvX0CzTOpNE77ME0Zty/nWWzchDtiqrmQDeuv3powQ5ta2eN0FY0InkqDD73lT9c9lEzwUNqgFHs9VQce3TVClFCQrSTfOiYkVJQBmpbq2L6iZavPnAPcoU0dSw0SUTqz/GtrGuXfbyyBniKykOWQWGqwwMA7QiYAxi+IlPdqo+hYHnUt5ZPfnsHJyNiDtnpJyayNBkF6cWoYGAMY92U2hXHF/C1M8uP/ZtYdiuj26UdAdQQSXQErwSOMzt/XWRWAz5GuSBIkwG1H3FabJ2OsUOUhGC6tK4EMtJO0ttC6IBD3kM0ve0tJwMdSfjZo+EEISaeTr9P3wYrGjXqyC1krcKdhMpxEnt5JetoulscpyzhXN5FRpuPHvbeQaKxFAEB6EN+cYN6xD7RYGpXpNndMmZgM5Dcs3YSNFDHUo2LGfZuukSWyUYirJAdYbF3MfqEKmjM+I2EfhA94iG3L7uKrR+GdWD73ydlIB+6hgref1QTlmgmbM3/LeX5GI1Ux1RWpgxpLuZ2+I+IjzZ8wqE4nilvQdkUdfhzI5QDWy+kw5Wgg2pGpeEVeCCA7b85BO3F9DzxB3cdqvBzWcmzbyMiqhzuYqtHRVG2y4x+KOlnyqla8AoWWpuBoYRxzXrfKuILl6SfiWCbjxoZJUaCBj1CjH7GIaDbc9kqBY3W/Rgjda1iqQcOJu2WW+76pZC9QG7M00dffe9hNnseupFL53r8F7YHSwJWUKP2q+k7RdsxyOB11n0xtOvnW4irMMFNV4H0uqwS5ExsmP9AxbDTc9JwgneAT5vTiUSm1E7BSflSt3bfa1tv8Di3R8n3Af7MNWzs49hmauE2wP+ttrq+AsWpFG2awvsuOqbipWHgtuvuaAE+A1Z/7gC9hesnr+7wqCwG8c5yAg3AL1fm8T9AZtp/bbJGwl1pNrE7RuOX7PeMRUERVaPpEs+yqeoSmuOlokqw49pgomjLeh7icHNlG19yjs6XXOMedYm5xH2YxpV2tc0Ro2jJfxC50ApuxGob7lMsxfTbeUv07TyYxpeLucEH1gNd4IKH2LAg5TdVhlCafZvpskfncCfx8pOhJzd76bJWeYFnFciwcYfubRc12Ip/ppIhA1/mSZ/RxjFDrJC5xifFjJpY2Xl5zXdguFqYyTR1zSp1Y9p+tktDYYSNflcxI0iyO4TPBdlRcpeqjK/piF5bklq77VSEaA+z8qmJTFzIWiitbnzR794USKBUaT0NTEsVjZqLaFVqJoPN9ODG70IPbfBHKK+/q/AWR0tJzYHRULOa4MP+W/HfGadZUbfw177G7j/OGbIs8TahLyynl4X4RinF793Oz+BU0saXtUHrVBFT/DnA3ctNPoGbs4hRIjTok8i+algT1lTHi4SxFvONKNrgQFAq2/gFnWMXgwffgYMJpiKYkmW3tTg3ZQ9Jq+f8XN+A5eeUKHWvJWJ2sgJ1Sop+wwhqFVijqWaJhwtD8MNlSBeWNNWTa5Z5kPZw5+LbVT99wqTdx29lMUH4OIG/D86ruKEauBjvH5xy6um/Sfj7ei6UUVk4AIl3MyD4MSSTOFgSwsH/QJWaQ5as7ZcmgBZkzjjU1UrQ74ci1gWBCSGHtuV1H2mhSnO3Wp/3fEV5a+4wz//6qy8JxjZsmxxy5+4w9CDNJY09T072iKG0EnOS0arEYgXqYnXcYHwjTtUNAcMelOd4xpkoqiTYICWFq0JSiPfPDQdnt+4/wuqcXY47QILbgAAAABJRU5ErkJggg=='); opacity: 0.04; pointer-events: none; } .controls { display: flex; justify-content: space-between; margin-top: 1rem; } .control-btn { font-family: 'Courier New', monospace; background: var(--accent-color); color: white; border: none; padding: 0.5rem 1rem; border-radius: 2px; cursor: pointer; transition: all 0.3s ease; text-transform: uppercase; font-weight: bold; letter-spacing: 1px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); } .control-btn:hover { background: #B33C28; transform: translateY(-2px); } .control-btn:active { transform: translateY(0); } .control-btn.reset { background: var(--tertiary-color); } .control-btn.reset:hover { background: #A76815; } .typing-sound { position: absolute; bottom: 1rem; right: 1rem; width: 40px; height: 40px; background: rgba(255,255,255,0.85); border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); transition: all 0.3s ease; } .typing-sound:hover { transform: scale(1.1); } /* Sound icon */ .sound-icon { width: 20px; height: 20px; position: relative; } .sound-icon .bar { position: absolute; background: var(--text-color); width: 3px; bottom: 0; border-radius: 3px; } .sound-icon .bar:nth-child(1) { height: 12px; left: 0; } .sound-icon .bar:nth-child(2) { height: 16px; left: 6px; } .sound-icon .bar:nth-child(3) { height: 20px; left: 12px; } .sound-icon .bar:nth-child(4) { height: 14px; left: 18px; } .muted .bar { background: #ccc; } .key-press { position: absolute; color: var(--text-color); font-size: 12px; opacity: 0; animation: keyPress 0.5s ease-out forwards; } @keyframes keyPress { 0% { opacity: 0.8; transform: translateY(0); } 100% { opacity: 0; transform: translateY(-20px); } } .retro-decoration { position: absolute; bottom: 1rem; left: 1rem; font-size: 0.7rem; color: var(--secondary-color); opacity: 0.8; transform: rotate(-5deg); } .ribbon { position: absolute; top: -5px; right: -5px; width: 100px; height: 100px; overflow: hidden; } .ribbon::before { content: "RETRO"; position: absolute; display: block; width: 150px; padding: 8px 0; background-color: var(--tertiary-color); color: white; font-size: 0.75rem; font-weight: bold; text-transform: uppercase; text-align: center; right: -40px; top: 20px; transform: rotate(45deg); box-shadow: 0 3px 6px rgba(0,0,0,0.1); } @media (max-width: 600px) { .title { font-size: 1.8rem; } .subtitle { font-size: 0.9rem; } .typewriter-text { font-size: 1rem; } .controls { flex-direction: column; gap: 10px; } .control-btn { width: 100%; } } </style> </head> <body> <div class="container"> <div class="vintage-frame"> <div class="header"> <h1 class="title">Chronotap</h1> <p class="subtitle">Where nostalgia meets technology</p> </div> <div class="typewriter-container"> <div class="paper-texture"></div> <div id="typewriter" class="typewriter-text"></div> <span class="cursor"></span> <div class="typing-sound" id="sound-toggle"> <div class="sound-icon"> <div class="bar"></div> <div class="bar"></div> <div class="bar"></div> <div class="bar"></div> </div> </div> <div class="retro-decoration">// Est. 1985</div> </div> <div class="controls"> <button class="control-btn play" id="play-btn">Play</button> <button class="control-btn pause" id="pause-btn">Pause</button> <button class="control-btn reset" id="reset-btn">Reset</button> </div> <div class="ribbon"></div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { const typewriterEl = document.getElementById('typewriter'); const cursorEl = document.querySelector('.cursor'); const playBtn = document.getElementById('play-btn'); const pauseBtn = document.getElementById('pause-btn'); const resetBtn = document.getElementById('reset-btn'); const soundToggle = document.getElementById('sound-toggle'); let isPaused = true; let soundEnabled = true; let currentTextIndex = 0; let textBuffer = ""; // Different typing speeds const fastSpeed = { min: 20, max: 70 }; const normalSpeed = { min: 70, max: 140 }; const slowSpeed = { min: 150, max: 300 }; // Pause durations for different punctuation const pauseDurations = { '.': 800, ',': 500, '!': 800, '?': 800, ':': 600, ';': 600, '\n': 1000 }; // The text to be typed const textToType = `Hello there! Welcome to my retro portfolio. I'm a developer with a passion for the golden era of computing. Remember when the digital world was simpler? When each keystroke mattered and the cursor blinked patiently? I do. That's why I've crafted this typewriter experience just for you. My portfolio showcases projects that blend modern technology with vintage aesthetics: • PIXEL PERFECT: A retro game built with modern JavaScript • ASCII ART GENERATOR: Turn any image into beautiful ASCII art • VINTAGE TERMINAL: A Linux terminal emulator with 80s aesthetics • TAPE RECORDER: A web audio recorder inspired by cassette recorders I've worked with clients like RetroArcade, VintageComputing Magazine, and 8-BitDreams Studio. My code is as clean and efficient as those old BASIC programs, but infinitely more powerful. The sound of mechanical keys, the warmth of amber monitors, the satisfaction of a well-formatted document... these aren't just nostalgic tokens – they're design principles. Want to collaborate? Press the spacebar in your mind and let's create something timeless together.`; // Audio elements for typing sounds const keySound1 = new Audio('data:audio/wav;base64,UklGRiQFAABXQVZFZm10IBAAAAABAAEAgD4AAAB9AAACABAAZGF0YQAFAAD//wAAjHMAAPj/AAA9kv7/dhoFAJyPWADZShoA3zoEAGUPUf9XMEz97Bwj/tI0svyj2LP9JghI9/DuhACH/ln5u9Uy/1D1GgOIEAv48wbHBR0Jx/jLCr0APvJfCWkAO/FH/Hj66P7Av4L3g+Ei+MnHRfmT9HT1/fqD+2LxFf9cCYj1OPu1CGYIFP1t/lEIGwjf+0wKPwLMASEG8v0z/2YR4f/I+gIG6v9Q+a74qPUE/3kGNP9w/gX7Mv5i/a4CwQkJBqb7qQJhDKQR6gELAo8BiwRICwITcP9KANoCfAQiDPoBkAY0CDb+UwZxBiIAGAD3Bo8DU/yxANEDYgONAHgEtQGtA/UC8wFaCBsClwPHBQ0BUQxIBVQBqQjOCecABQZhBvQCKgfdBNUCkgKABXYEiQWRCMsEGQSSB9sEtwRxB7UFDQLmBIMDyf2hAyb+w/l1A8P+n/qb+Qf4M/k9+ZH2a/ia9rr2GPZ/9t33FPvN/BL/wQDxAegB1wOFBd8DJQKSAHj/i/7o/D79KwAu/SX8OPoy+B36x/yC/iP/pACn/9YBVQKQAUIBWAB1/nP9aPwj+xP8pPp0+Gz5Zvh1+lT6WPth/fT91/47/w//+f4wAUMCywKrA6EDzgTkBCYDSASpBbYFIQTXA9QCUwP/A5QEkARPBFQFdwPcAgwC0gGjAQAAKv8dAI4BE//8/cP+wv1E/Yj+KP4V/Rf8XvtB/GH8S/zs/Wf+t/76/Gr9Z/2p/hH/SAAnAAgBUAB8/wn/Pv9d/87/1/+g/6//hv/K/3QA2QD7AUECzwIYAg8CoAFvAcYB5AA7ALj/o/+u/tP9Kv4a/tf9q/3H/ZL9X/0Z/hL+6v3N/qD+fP4J/xP/8v7M/5b/qv8QAA8AawAqAMj/nP8HAC0AKgAdAFEA8P9OAAAA0v/2/wAA3f+NAA0AmQAzAAIAXQBHAHUAwQCIAPsAHgEtASAB3QDlAJgAegCzAJYAlACaAKwAwQB2AGsAEwAzAPz/6f/+/wEADgAhACMACQDv/+X/DgAQABEAKQAvABIA+/8GAPL/5v/Y/9n/6//d/+f/+f/1/wMAIAA0ADMAIAA6ADgANAA/ADgAMQA7ADMAMwA+AD0AQwBRAFUAVQBKAD0AMAA2AD8ARABDAEUALQAgACMAHAAWACYAIQAgADAAGwANAAUACQAGAAUABQD6//f/6v/j/+j/7f8BAAcABAAGAAkAEgAOABAAGQAbAB4AJgA9AEIAPwA/AD8ASgBYAFEAUQBJADgAPwAuACgAJQAPAAwA9//r/9j/0P/R/8z/w/+4/6r/nP+v/7X/sv+4/7r/tP/A/8H/z//X/9v/5//m/+//8//8////BgATABUAFwAeACEAJwA0ADsAPgBJAFMAVABVAFYAVQBPAD8APgA3AC4AJgAbABUAEAAAAAIAAQD5//P/8f/w//D/8f/t//D/9v/6//3/AQAEAAkADQAQABcAGQAfACYAJQAnACgALAAvADIANQAzADUAMgA0ADEAMwAxADYAOAA0ADMAMAA1ADsAOwA6ADoANgA0ADMAMgAvAC8ALgAtADIAMgA2ADcAMAAzADQAMgAzADIALwAuACoAJwAiACAAGwATAAsACQD9//n/8v/t/+f/4f/e/97/3P/a/9r/2//b/97/4f/k/+j/7//w//b/+/8AAAgADQARABcAHQAhACMAJQAnACkALQAwADMANQA3ADkAOQA5ADoAOgA6ADoAOgA7ADwAPAA7ADwAPQA9ADsAPAAAAgAAAAAAAAAAAKQFAAAAAAAAAAAAAABkAQAAAAAAAAAAAAA='); const keySound2 = new Audio('data:audio/wav;base64,UklGRiQFAABXQVZFZm10IBAAAAABAAEAgD4AAAB9AAACABAAZGF0YQAFAAD7+gAAvowAAM36AAAtdf7/5D4FABCzWAA/RBoAH0IEAIA5Uf9/REz9zB8j/tM7svw03LP96Q5I98j0hAC9AV35Edky/8/5GgMJEQv4GAjHBVMJx/jGC70A3/JfCaIA+/FH/Mr66P4jwIL3/OIi+CfIRflC9XT1tPuD+7TxFf8ICoj1tPu1CL8IFP0S/1EI0Ajf+/UKPwJgASEGif4z/6MR4f9c+wIG/P9Q+bb4qPV1/3kGfP9w/rT7Mv42/a4C8wkJBvL7qQKxDKQRSAILAmwCiwRgCwITGwBKABMDfARBDPoBuwY0CPf+UwYAByIAigD3BuEDU/wJAdEDkwONAMgEtQEIBPUCIwJaCGEClwM0Bg0BkwxIBYQBqQgJCucAOAZhBkMDKgcjBdUCyAKABbUEiQXXCMsETATSB9sE+ARxB/UFOQLmBL0Dyf3hAyb+0/l1A8/+n/rK+Qf4ZPk9+ab2a/ih9rr2S/Z/9gz4FPsi/RL/2QHxAQIC1wO2Bd8DWAKSAJf/i/79/D79YAAu/Un8OPpP+B365PyF/lX/pACz/9oBZQKQAWIBWAAe/nP9vPwj+0j8pPqE+Gz5mvh1+mr6WPuT/fT98P47/x//+f5hAUMC6gKrAwYEzgT9BCYDegSpBdYFIQT7A9QCiAP/A74EkAS0BFQFqgPcAgwCAgKjAUoAKv9KAI4BLf/8/en+wv2A/Yj+Sf4V/Vf8XvuF/GH8d/zs/aH+t/6N/Gr9qP2p/kL/SABEAAcBUACA/wn/a/9d//L/1/+9/6//nv/K/5IAHQELAkECBQMYAkoCqAFvAe8B5ABQAL3/o/+8/tP9U/4a/vL9q/3v/ZL9jP0Z/jb+6v3z/qD+mv4J/zf/8v7r/5b/yP8RADQAawBNANn/nP8rAC0ARgAeAG0A8P9uAAAA6P/2/xgA3f+tAA0ArwAzABYAYgBXAIYAwQCeAPsAOwEtATYB3QD4AJsAfQCzAKcAlACxAKwA3AB2AHwAEwBIAPz//v/+/xcADgA2ACMACgDw/+X/IAAQACEAKQBAACgA+/8GAPT/5v/Y/9n/6//e/+f/+//1/xAALAA0AEkAIABPADgANABKADkAMQA7ADUAMwA+AD0AVABRAGYAVgBaAEQAMQA2AD8ATQBDAGIALQAhACMAHQAXACYAIgAgADgAGwARAAYACQAGAAUABQD7//j/6v/j/+j/7f8KAAcABgAGAAsAEgASABAAGQAdAB8AJgBFAEMAPwBBACIASgBjAFIAUQBQADgAPwAvACkAJQAQAAwA+f/r/9j/0P/R/87/w/+5/6r/nP+v/7b/s/+5/7r/tf/A/8H/0P/X/9z/5//m//D/8/8AAAAAAQASABYAFwAfACEAKAA1ADsAPwBJAFMAVQBWAFYAVgBPAEAAPgA3AC8AJgAcABUAEQABAAIAAQD6//P/8v/w//D/8v/u//D/9v/6//3/AgAEAAkADQAQABgAGQAfACYAJgAnACkALAAvADMANQAzADUAMgA0ADIAMwAyADYAOAA0ADQAMAAxADsAOwA7ADoANgA0ADQAMgAwAC8ALgAtADIAMgA3ADcAMQAzADQAMgAzADIALwAuACoAJwAiACAAGwAUAAoA7//9//n/8v/t/+f/4f/e/97/3P/a/9v/2//b/97/3//j/+j/7//w//X/+v8AAAgADQAQABYAHAAhACIAJAAmACgALAAwADMANQA3ADkAOQA5ADoAOgA6ADoAOgA7ADwAOwA7ADwAPAA9ADsAPAAAAgAAAAAAAAAAAKQFAAAAAAAAAAAAAABkAQAAAAAAAAAAAAA='); const keySound3 = new Audio('data:audio/wav;base64,UklGRiQFAABXQVZFZm10IBAAAAABAAEAgD4AAAB9AAACABAAZGF0YQAFAAD//wAAl3EAAPz/AAAzlP7/ah8FAL+LWAA5SxoA7j0EAD8NUf9bM0z91Bwj/uUxsvzH2LP9QgdI977uhADC/Vn5odUy/2P1GgNwEAv4ywbHBf4Ix/jSCr0AGvJfCZsAQvFH/H/66P7Uv4L3i+Ii+NDHRfma9HT1Bfth+z3xJf9VCYj1J/u1CG4IFP1p/lEINQjf+0MKPwLUASEGBP4z/10R4f/W+gIGAQBQ+Zj4qPUQ/3kGOf9w/gz7Mv5b/a4CzAkJBp77qQJlDKQR8wELAoYBiwRNCwITbP9KAPkCfARDDPoBigY0CAb+UwaCBiIADgD3BpUDU/ytANEDWgONAHwEtQGmA/UC/QFaCB8ClwPLBQ0BUwxIBVgBqQjPCeYADQZhBvYCKgfgBNUCkgKABX0EiQWUCMsEHgSYB9sEtwRxB/oFDQLmBIUDyf3jAyb+xfl1A8f+n/qf+Qf4Nfk9+ZP2a/ic9rr2HPZ/9tn3FPvB/Bf/wQDxAeEC1wOABd8DKQKSAHv/i/7q/D79LAAu/Sf8OPo2+B36xPyG/iH/pACs/9YBVwKQAUMBWAB2/nP9avwj+xX8pPp3+Gz5Zvh1+lT6WPte/fT93/47/xH/+f4xAUMCzgKrA6QDzgTmBCYDSgSqBbgFIQTYA9QCVgP/A5UEkARQBFQFdwPcAgwC1AGjAUgAKv8fAI4BIf/8/cX+wv1G/Yj+Kv4V/Rn8Xvs//GH8TPzs/Wj+t/4K/Wz9Zv2p/hP/SAAxAAgBUAB9/wn/QP9d/9D/1/+i/6//iP/K/3YA2wD8AUECzwIYAhACogFvAcgB5AA9ALn/o/+w/tP9LP4a/tr9q/3J/ZL9YP0Z/hP+6v3P/qD+ff4J/xT/8v7O/5b/q/8RABAAawAsAMn/nP8IACwAKwAeAFIA8P9PAAAA0//2/wEA3f+OAA0AmgA0AAMAXQBIAHYAwQCJAP0AHwEtATIB3QDmAJkAewCzAJcAlACbAKwAwgB2AGwAEwA0APz/6v/+/wIADgAiACMAGwDv/+X/DwAQABIAKQAvABEA+/8GAPL/5v/Y/9n/6//d/+f/+v/1/wQAIAA0ADQAIQBKADgANAA/ADgAMQA7ADQAMwA+AD0ARABRAFYAVgBMAD4AMAA2AD8ARQBDAEYALQAhACMAHQAWACYAIgAgADEAGwAOAAUACQAGAAUABQD7//f/6v/j/+j/7f8CAAcABQAGAAoAEgAPABAAGQAbAB4AJgA+AEIAPwA/AD8ASgBXAFEAUQBMADgAPwAvACgAJQAQAAwA+P/r/9j/0P/R/83/w/+4/6r/nP+v/7X/sv+4/7r/tP/A/8H/z//X/9v/5//m/+//8//9////BwATABUAFwAfACEAJwA0ADsAPgBJAFMAVABVAFYAVQBPAEAAPgA3AC4AJgAbABUAEAABAAIAAQD6//P/8f/w//D/8f/t//D/9v/6//3/AQAEAAkADQAQABcAGQAfACYAJQAnACgALAAvADIANQAzADUAMgA0ADEAMwAxADYAOAA0ADMAMAAxADsAOwA6ADoANgA0ADMAMgAvAC8ALgAtADIAMgA2ADcAMAAzADQA
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Conversational Typing Animation</title> <style> @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap'); * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', sans-serif; } body { background-color: #f5f7fa; display: flex; justify-content: center; align-items: center; height: 100vh; overflow: hidden; } .chat-container { width: 100%; max-width: 680px; height: 680px; background-color: #fff; border-radius: 20px; box-shadow: 0 8px 30px rgba(0, 0, 0, 0.08); display: flex; flex-direction: column; overflow: hidden; position: relative; } .chat-header { padding: 20px; background: linear-gradient(135deg, #6366F1 0%, #8B5CF6 100%); color: white; display: flex; align-items: center; gap: 15px; } .avatar { width: 40px; height: 40px; border-radius: 50%; background-color: #fff; display: flex; align-items: center; justify-content: center; } .avatar svg { width: 24px; height: 24px; fill: #6366F1; } .chat-header-text { flex: 1; } .chat-header h1 { font-size: 18px; font-weight: 600; margin: 0; } .chat-header p { font-size: 14px; margin: 0; opacity: 0.8; } .chat-messages { flex: 1; padding: 20px; overflow-y: auto; display: flex; flex-direction: column; gap: 15px; } .message { max-width: 80%; padding: 14px 18px; border-radius: 18px; font-size: 15px; line-height: 1.5; animation: fadeIn 0.3s ease; position: relative; } .message.bot { background-color: #f0f2f5; color: #1e293b; align-self: flex-start; border-bottom-left-radius: 4px; } .message.user { background: linear-gradient(135deg, #6366F1 0%, #8B5CF6 100%); color: white; align-self: flex-end; border-bottom-right-radius: 4px; } .typing-indicator { display: flex; align-items: center; gap: 4px; padding: 14px 18px; background-color: #f0f2f5; border-radius: 18px; border-bottom-left-radius: 4px; max-width: 80px; align-self: flex-start; margin-top: 15px; opacity: 0; transition: opacity 0.3s ease; } .typing-indicator.visible { opacity: 1; } .typing-dot { height: 8px; width: 8px; background-color: #919eab; border-radius: 50%; display: inline-block; animation: pulse 1.5s infinite; } .typing-dot:nth-child(2) { animation-delay: 0.2s; } .typing-dot:nth-child(3) { animation-delay: 0.4s; } .chat-input { padding: 15px 20px; border-top: 1px solid #eaecf0; display: flex; align-items: center; background-color: #fff; position: relative; } .chat-input input { flex: 1; border: none; outline: none; padding: 12px 15px; border-radius: 50px; font-size: 15px; background-color: #f0f2f5; transition: all 0.3s ease; } .chat-input input:focus { box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2); } .send-button { margin-left: 10px; width: 45px; height: 45px; border-radius: 50%; background: linear-gradient(135deg, #6366F1 0%, #8B5CF6 100%); display: flex; align-items: center; justify-content: center; border: none; cursor: pointer; transition: transform 0.2s ease, box-shadow 0.2s ease; } .send-button:hover { transform: scale(1.05); box-shadow: 0 5px 15px rgba(99, 102, 241, 0.3); } .send-button svg { width: 20px; height: 20px; fill: white; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } @keyframes pulse { 0%, 100% { transform: scale(1); opacity: 0.7; } 50% { transform: scale(1.2); opacity: 1; } } .message-time { font-size: 11px; opacity: 0.7; margin-top: 5px; display: block; text-align: right; } .bot .message-time { color: #64748b; } .user .message-time { color: rgba(255, 255, 255, 0.8); } .message.bot.typing { min-height: 24px; width: auto; min-width: 40px; display: flex; align-items: center; } .characters { display: inline-block; opacity: 0; transition: opacity 0.05s ease; } .characters.visible { opacity: 1; } .chat-features { position: absolute; top: 20px; right: 20px; display: flex; gap: 15px; } .feature-button { width: 36px; height: 36px; border-radius: 50%; background-color: rgba(255, 255, 255, 0.2); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.2s ease; } .feature-button:hover { background-color: rgba(255, 255, 255, 0.3); transform: scale(1.1); } .feature-button svg { width: 18px; height: 18px; fill: white; } .sound-toggle-tooltip { position: absolute; background-color: white; color: #1e293b; padding: 8px 12px; border-radius: 8px; font-size: 12px; top: -40px; right: 0; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); opacity: 0; transform: translateY(10px); transition: opacity 0.3s ease, transform 0.3s ease; pointer-events: none; white-space: nowrap; } .feature-button:hover .sound-toggle-tooltip { opacity: 1; transform: translateY(0); } /* Responsiveness */ @media (max-width: 700px) { .chat-container { width: 100%; height: 100%; border-radius: 0; } .message { max-width: 85%; } } /* Custom Scrollbar */ .chat-messages::-webkit-scrollbar { width: 6px; } .chat-messages::-webkit-scrollbar-track { background: transparent; } .chat-messages::-webkit-scrollbar-thumb { background: #d1d5db; border-radius: 3px; } .chat-messages::-webkit-scrollbar-thumb:hover { background: #9ca3af; } .visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0; } </style> </head> <body> <div class="chat-container"> <div class="chat-header"> <div class="avatar"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M12 2c2.21 0 4 1.79 4 4s-1.79 4-4 4-4-1.79-4-4 1.79-4 4-4zm0 10c4.42 0 8 1.79 8 4v2H4v-2c0-2.21 3.58-4 8-4z"></path> </svg> </div> <div class="chat-header-text"> <h1>Design Assistant</h1> <p>Always here to help with your design needs</p> </div> <div class="chat-features"> <button class="feature-button sound-toggle" title="Toggle sound"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="sound-on"> <path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="sound-off" style="display: none;"> <path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"></path> </svg> <div class="sound-toggle-tooltip">Sound is on</div> </button> </div> </div> <div class="chat-messages" id="chat-messages"> <div class="message bot"> Hi there! I'm your Design Assistant. I can help you with UI design, color schemes, typography, and more. <span class="message-time">10:05 AM</span> </div> <div class="message bot"> What design challenge are you working on today? <span class="message-time">10:05 AM</span> </div> </div> <div class="typing-indicator" id="typing-indicator"> <span class="typing-dot"></span> <span class="typing-dot"></span> <span class="typing-dot"></span> </div> <div class="chat-input"> <input type="text" id="user-input" placeholder="Type your message here..." aria-label="Type your message"> <button class="send-button" id="send-button" aria-label="Send message"> <svg xmlns="http://www.w3.org/2000/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> <audio id="message-sent" preload="auto"> <source src="data:audio/mp3;base64,SUQzAwAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4LjI5LjEwMAAAAAAAAAAAAAAA//tUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASW5mbwAAAA8AAAASAAAeMwAUFBQUFCIiIiIiIjAwMDAwMD09PT09PUlJSUlJSVZWVlZWVmRkZGRkZHFxcXFxcX9/f39/f46Ojo6OjpycnJycnKmpqampqbe3t7e3t8XFxcXFxdLS0tLS0uDg4ODg4O/v7+/v7/////8AAAAATGF2YzU4LjU0AAAAAAAAAAAAAAAAJAaYAAAAAAAAHjMMqqWmAAQDgAAAJOdtwM0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//sUZAAP8AAAaQAAAAgAAA0gAAABAAABpAAAACAAADSAAAAETEFNRTMuMTAwVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//sUZCwP8AAAaQAAAAgAAA0gAAABAAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//sUZFYP8AAAaQAAAAgAAA0gAAABAAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV" type="audio/mp3"> </audio> <audio id="message-received" preload="auto"> <source src="data:audio/mp3;base64,SUQzAwAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4LjI5LjEwMAAAAAAAAAAAAAAA//tUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASW5mbwAAAA8AAAASAAAeMwAUFBQUFCIiIiIiIjAwMDAwMD09PT09PUlJSUlJSVZWVlZWVmRkZGRkZHFxcXFxcX9/f39/f46Ojo6OjpycnJycnKmpqampqbe3t7e3t8XFxcXFxdLS0tLS0uDg4ODg4O/v7+/v7/////8AAAAATGF2YzU4LjU0AAAAAAAAAAAAAAAAJAQmAAAAAAAAHjMizBwwAAQDgAAAJOdlfj0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//sUZAAP8AAAaQAAAAgAAA0gAAABAAABpAAAACAAADSAAAAETEFNRTMuMTAwVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//sUZCIP8AAAaQAAAAgAAA0gAAABAAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//sUZEwP8AAAaQAAAAgAAA0gAAABAAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV" type="audio/mp3"> </audio> <audio id="typing-sound" preload="auto"> <source src="data:audio/mp3;base64,SUQzAwAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4LjI5LjEwMAAAAAAAAAAAAAAA//tQwAAAAAAAAAAAAAAAAAAAAAAASW5mbwAAAA8AAAACAAABIADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV////////////////////////////////////////////AAAAOkxhdmM1OC41NAAAAAAAAAAAAAAAACQCkAAAAAAAAAEgAAAeNgAAAgAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7UMQAAAeQBw2QEQBBhCyNnPMAAwIaSVpYx4eBDDygjEDxHQQ5F7xjwHQD/8+ILxAPg+0C/7+8BB/0AIJCD//XB9ToZ7QbpT8D3g//w/LX/86fqrz//v/6///9X6v//k9X6v///z////Tv/+n9X/r/9XoAAAAAAAAA8nvzpL3ajgGCwAAAAAAAAAAAAAAAA" type="audio/mp3"> </audio> <div class="visually-hidden" aria-live="polite" id="screen-reader-announcements"></div> <script> const userInput = document.getElementById('user-input'); const sendButton = document.getElementById('send-button'); const chatMessages = document.getElementById('chat-messages'); const typingIndicator = document.getElementById('typing-indicator'); const messageSentSound = document.getElementById('message-sent'); const messageReceivedSound = document.getElementById('message-received'); const typingSound = document.getElementById('typing-sound'); const soundToggle = document.querySelector('.sound-toggle'); const soundOnIcon = document.querySelector('.sound-on'); const soundOffIcon = document.querySelector('.sound-off'); const soundToggleTooltip = document.querySelector('.sound-toggle-tooltip'); const screenReaderAnnouncements = document.getElementById('screen-reader-announcements'); let soundEnabled = true; let botResponses = [ "Typography is crucial for readability! For your conversational UI, I'd recommend using a sans-serif font like Inter or SF Pro with a size of 15-16px for the main text.", "For natural typing animations, you should vary speeds between 30-90ms per character and add random pauses at punctuation. This creates a more human-like feel.", "Sound cues should be subtle - a soft click for sent messages and a gentle tone for received ones. Make sure they're not distracting!", "For the speech bubbles, rounded corners with a small tail creates that classic chat feel. I suggest 18px border-radius with a 4px corner on the bubble's 'tail' side.", "Color psychology matters in conversations! Blue tones convey trust, while purple adds creativity. A gradient between the two can work beautifully for your user messages.", "Consider adding a subtle haptic feedback for mobile users when messages are sent or received. It adds another sensory dimension to the experience.", "Don't forget accessibility! Make sure your typing animation has proper ARIA attributes so screen readers can announce when the bot is typing." ]; function getCurrentTime() { const now = new Date(); return now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); } function playSound(sound) { if (soundEnabled) { sound.currentTime = 0; sound.play().catch(e => console.log("Sound play prevented:", e)); } } function addMessage(text, isUser = false) { const messageDiv = document.createElement('div'); messageDiv.classList.add('message'); messageDiv.classList.add(isUser ? 'user' : 'bot'); const timeSpan = document.createElement('span'); timeSpan.classList.add('message-time'); timeSpan.textContent = getCurrentTime(); if (isUser) { messageDiv.textContent = text; messageDiv.appendChild(timeSpan); chatMessages.appendChild(messageDiv); playSound(messageSentSound); screenReaderAnnouncements.textContent = "You: " + text; scrollToBottom(); } else { chatMessages.appendChild(messageDiv); typeMessage(messageDiv, text, timeSpan); } } function typeMessage(messageElement, text, timeSpan) { // Show typing indicator typingIndicator.classList.add('visible'); screenReaderAnnouncements.textContent = "Design Assistant is typing..."; // Simulate thinking time before typing setTimeout(() => { typingIndicator.classList.remove('visible'); let index = 0; const words = text.split(' '); // Set up the typing interval with variable speed function typeNextChar() { if (index < words.length) { if (index > 0) { // Add space before word (except first word) messageElement.innerHTML += ' '; } const word = words[index]; // Type each character in the word with a random delay let charIndex = 0; function typeChar() { if (charIndex < word.length) { const span = document.createElement('span'); span.className = 'characters'; span.textContent = word[charIndex]; messageElement.appendChild(span); // Make character visible after a tiny delay setTimeout(() => { span.classList.add('visible'); playSound(typingSound); }, 10); charIndex++; // Variable typing speed between characters const isPunctuation = ['.', ',', '!', '?'].includes(word[charIndex - 1]); const delay = isPunctuation ? Math.random() * 350 + 300 : // longer pause after punctuation Math.random() * 50 + 30; // normal typing speed setTimeout(typeChar, delay); } else { index++; // Variable delay between words setTimeout(typeNextChar, Math.random() * 50 + 30); } } typeChar(); } else { // Message complete, add timestamp messageElement.appendChild(timeSpan); playSound(messageReceivedSound); scrollToBottom(); screenReaderAnnouncements.textContent = "Design Assistant: " + text; } } typeNextChar(); }, Math.random() * 1000 + 500); // Randomized thinking time } function scrollToBottom() { chatMessages.scrollTop = chatMessages.scrollHeight; } function handleUserMessage() { const message = userInput.value.trim(); if (message) { addMessage(message, true); userInput.value = ''; // Get random response from bot setTimeout(() => { const randomResponse = botResponses[Math.floor(Math.random() * botResponses.length)]; addMessage(randomResponse, false); }, 500); } } // Event listeners sendButton.addEventListener('click', handleUserMessage); userInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { handleUserMessage(); } }); soundToggle.addEventListener('click', () => { soundEnabled = !soundEnabled; if (soundEnabled) { soundOnIcon.style.display = 'block'; soundOffIcon.style.display = 'none'; soundToggleTooltip.textContent = 'Sound is on'; } else { soundOnIcon.style.display = 'none'; soundOffIcon.style.display = 'block'; soundToggleTooltip.textContent = 'Sound is off'; } }); // After page load, simulate a delayed bot message setTimeout(() => { addMessage("I see you're designing a conversational interface! How can I help with your typing animation and sound cues today?", false); }, 1500); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>CodeTyper - Interactive Coding Tutorial</title> <style> :root { --bg-primary: #1e1e2e; --bg-secondary: #181825; --text-primary: #cdd6f4; --text-secondary: #a6adc8; --accent-blue: #89b4fa; --accent-green: #a6e3a1; --accent-red: #f38ba8; --accent-yellow: #f9e2af; --accent-purple: #cba6f7; --accent-cyan: #94e2d5; --console-green: #a6e3a1; --line-number: #6c7086; } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace; } body { background-color: var(--bg-secondary); color: var(--text-primary); height: 100vh; display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 20px; overflow: hidden; } .container { width: 100%; max-width: 700px; height: 700px; display: flex; flex-direction: column; background-color: var(--bg-primary); border-radius: 10px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); overflow: hidden; } .header { display: flex; align-items: center; padding: 12px 20px; background-color: #181825; border-bottom: 1px solid #313244; } .window-controls { display: flex; margin-right: 15px; } .window-control { width: 12px; height: 12px; border-radius: 50%; margin-right: 8px; } .close { background-color: var(--accent-red); } .minimize { background-color: var(--accent-yellow); } .maximize { background-color: var(--accent-green); } .tab { background-color: #1e1e2e; padding: 6px 12px; border-radius: 5px 5px 0 0; font-size: 14px; display: flex; align-items: center; gap: 8px; } .tab-icon { color: #89b4fa; font-size: 14px; } .editor-container { display: flex; flex: 1; overflow: hidden; } .line-numbers { padding: 15px 0; background-color: var(--bg-secondary); width: 45px; text-align: right; color: var(--line-number); font-size: 14px; user-select: none; overflow-y: auto; scrollbar-width: none; } .line-numbers::-webkit-scrollbar { display: none; } .line-number { padding: 0 10px 0 0; height: 24px; line-height: 24px; } .code-editor { flex: 1; padding: 15px 0; overflow-y: auto; position: relative; scrollbar-width: thin; scrollbar-color: #313244 transparent; } .code-editor::-webkit-scrollbar { width: 8px; } .code-editor::-webkit-scrollbar-track { background: transparent; } .code-editor::-webkit-scrollbar-thumb { background-color: #313244; border-radius: 10px; } .code-line { padding: 0 0 0 15px; height: 24px; line-height: 24px; white-space: pre; font-size: 14px; } .cursor { display: inline-block; width: 2px; height: 18px; background-color: var(--accent-blue); vertical-align: middle; margin-left: 1px; animation: blink 1s step-end infinite; } @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } .typing { display: inline; } .console { background-color: #11111b; padding: 12px 15px; font-size: 14px; border-top: 1px solid #313244; max-height: 120px; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #313244 transparent; } .console::-webkit-scrollbar { width: 8px; } .console::-webkit-scrollbar-track { background: transparent; } .console::-webkit-scrollbar-thumb { background-color: #313244; border-radius: 10px; } .console-prompt { color: var(--console-green); font-weight: bold; } .console-output { color: var(--text-secondary); margin-top: 5px; } .controls { display: flex; justify-content: space-between; padding: 12px 20px; background-color: #181825; border-top: 1px solid #313244; } .tutorial-nav { display: flex; gap: 12px; } .nav-button { background-color: #313244; border: none; color: var(--text-primary); padding: 6px 12px; border-radius: 5px; cursor: pointer; transition: background-color 0.2s, transform 0.1s; font-size: 14px; display: flex; align-items: center; gap: 8px; } .nav-button:hover { background-color: #45475a; } .nav-button:active { transform: scale(0.97); } .run-button { background-color: var(--accent-green); color: #1e1e2e; font-weight: bold; } .run-button:hover { background-color: #94d787; } .language-selector { display: flex; gap: 8px; } .lang-button { padding: 6px 10px; border-radius: 5px; border: 1px solid #313244; background-color: transparent; color: var(--text-secondary); cursor: pointer; font-size: 14px; transition: all 0.2s; } .lang-button.active { background-color: var(--accent-blue); color: #1e1e2e; border-color: var(--accent-blue); font-weight: bold; } .lang-button:hover:not(.active) { border-color: var(--accent-blue); color: var(--accent-blue); } /* Syntax highlighting classes */ .keyword { color: var(--accent-purple); } .string { color: var(--accent-green); } .number { color: var(--accent-yellow); } .function { color: var(--accent-blue); } .comment { color: var(--text-secondary); font-style: italic; } .operator { color: var(--accent-red); } .property { color: var(--accent-yellow); } .variable { color: var(--text-primary); } /* Responsive Design */ @media (max-width: 500px) { .header { padding: 8px 10px; } .language-selector { display: none; } .controls { padding: 8px 10px; } .nav-button span { display: none; } .code-line, .line-number { font-size: 12px; height: 22px; line-height: 22px; } } /* Progress bar */ .progress-container { width: 100%; height: 3px; background-color: #313244; position: absolute; bottom: 0; left: 0; } .progress-bar { height: 100%; background-color: var(--accent-blue); width: 0%; transition: width 0.5s ease; } /* Tooltip */ .tooltip { position: absolute; background-color: #313244; color: var(--text-primary); padding: 5px 10px; border-radius: 5px; font-size: 12px; z-index: 10; opacity: 0; transition: opacity 0.3s; pointer-events: none; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); } /* Highlight current line */ .code-line.active { background-color: rgba(137, 180, 250, 0.1); border-left: 2px solid var(--accent-blue); padding-left: 13px; } .line-number.active { color: var(--accent-blue); font-weight: bold; } /* Theme selector */ .theme-toggle { position: absolute; top: 15px; right: 15px; background: none; border: none; color: var(--text-secondary); cursor: pointer; z-index: 5; font-size: 20px; padding: 5px; border-radius: 5px; transition: all 0.2s; } .theme-toggle:hover { color: var(--accent-blue); background-color: rgba(137, 180, 250, 0.1); } </style> </head> <body> <div class="container"> <div class="header"> <div class="window-controls"> <div class="window-control close"></div> <div class="window-control minimize"></div> <div class="window-control maximize"></div> </div> <div class="tab"> <span class="tab-icon">📄</span> <span>async-data-fetching.js</span> </div> </div> <div class="editor-container"> <div class="line-numbers" id="lineNumbers"></div> <div class="code-editor" id="codeEditor"> <div class="progress-container"> <div class="progress-bar" id="progressBar"></div> </div> </div> </div> <div class="console" id="console"> <div class="console-prompt">$ node async-data-fetching.js</div> <div class="console-output" id="consoleOutput"></div> </div> <div class="controls"> <div class="tutorial-nav"> <button class="nav-button" id="prevBtn"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"> <path fill-rule="evenodd" d="M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z"/> </svg> <span>Previous</span> </button> <button class="nav-button run-button" id="runBtn"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"> <path d="M11.596 8.697l-6.363 3.692c-.54.313-1.233-.066-1.233-.697V4.308c0-.63.692-1.01 1.233-.696l6.363 3.692a.802.802 0 0 1 0 1.393z"/> </svg> <span>Run Code</span> </button> <button class="nav-button" id="nextBtn"> <span>Next</span> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"> <path fill-rule="evenodd" d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z"/> </svg> </button> </div> <div class="language-selector"> <button class="lang-button active">JavaScript</button> <button class="lang-button">Python</button> <button class="lang-button">React</button> </div> </div> </div> <div id="tooltip" class="tooltip"></div> <script> document.addEventListener('DOMContentLoaded', () => { const codeEditor = document.getElementById('codeEditor'); const lineNumbers = document.getElementById('lineNumbers'); const progressBar = document.getElementById('progressBar'); const consoleOutput = document.getElementById('consoleOutput'); const runBtn = document.getElementById('runBtn'); const prevBtn = document.getElementById('prevBtn'); const nextBtn = document.getElementById('nextBtn'); const tooltip = document.getElementById('tooltip'); // Sample code for the async data fetching tutorial - this will be typed line by line const codeLines = [ "// Modern JavaScript Async Data Fetching Example", "// This tutorial demonstrates how to fetch and process API data", "", "// 1. Using async/await with fetch API", "<span class='keyword'>async</span> <span class='keyword'>function</span> <span class='function'>fetchUserData</span>() {", " <span class='keyword'>try</span> {", " <span class='comment'>// Fetch user data from a REST API</span>", " <span class='keyword'>const</span> <span class='variable'>response</span> <span class='operator'>=</span> <span class='keyword'>await</span> <span class='function'>fetch</span>(<span class='string'>'https://api.example.com/users'</span>);", " ", " <span class='comment'>// Check if the request was successful</span>", " <span class='keyword'>if</span> (<span class='operator'>!</span><span class='variable'>response</span>.<span class='property'>ok</span>) {", " <span class='keyword'>throw</span> <span class='keyword'>new</span> <span class='function'>Error</span>(<span class='string'>`HTTP error! Status: ${</span><span class='variable'>response</span>.<span class='property'>status</span><span class='string'>}`</span>);", " }", " ", " <span class='comment'>// Parse the JSON response</span>", " <span class='keyword'>const</span> <span class='variable'>users</span> <span class='operator'>=</span> <span class='keyword'>await</span> <span class='variable'>response</span>.<span class='function'>json</span>();", " <span class='keyword'>return</span> <span class='variable'>users</span>;", " } <span class='keyword'>catch</span> (<span class='variable'>error</span>) {", " <span class='function'>console</span>.<span class='function'>error</span>(<span class='string'>'Failed to fetch users:'</span>, <span class='variable'>error</span>);", " <span class='keyword'>throw</span> <span class='variable'>error</span>; <span class='comment'>// Re-throw to handle it upstream</span>", " }", "}", "", "// 2. Processing and filtering the data", "<span class='keyword'>async</span> <span class='keyword'>function</span> <span class='function'>getActiveUsers</span>() {", " <span class='keyword'>const</span> <span class='variable'>users</span> <span class='operator'>=</span> <span class='keyword'>await</span> <span class='function'>fetchUserData</span>();", " ", " <span class='comment'>// Filter for only active users</span>", " <span class='keyword'>const</span> <span class='variable'>activeUsers</span> <span class='operator'>=</span> <span class='variable'>users</span>.<span class='function'>filter</span>(<span class='variable'>user</span> <span class='operator'>=></span> <span class='variable'>user</span>.<span class='property'>isActive</span>);", " ", " <span class='comment'>// Map to get only the data we need</span>", " <span class='keyword'>return</span> <span class='variable'>activeUsers</span>.<span class='function'>map</span>(<span class='variable'>user</span> <span class='operator'>=></span> ({", " <span class='property'>id</span>: <span class='variable'>user</span>.<span class='property'>id</span>,", " <span class='property'>name</span>: <span class='variable'>user</span>.<span class='property'>name</span>,", " <span class='property'>email</span>: <span class='variable'>user</span>.<span class='property'>email</span>", " }));", "}", "", "// 3. Using the data in our application", "<span class='keyword'>async</span> <span class='keyword'>function</span> <span class='function'>displayActiveUsers</span>() {", " <span class='keyword'>try</span> {", " <span class='keyword'>const</span> <span class='variable'>activeUsers</span> <span class='operator'>=</span> <span class='keyword'>await</span> <span class='function'>getActiveUsers</span>();", " ", " <span class='function'>console</span>.<span class='function'>log</span>(<span class='string'>'Active users:'</span>, <span class='variable'>activeUsers</span>);", " ", " <span class='comment'>// You could update UI here</span>", " <span class='variable'>activeUsers</span>.<span class='function'>forEach</span>(<span class='variable'>user</span> <span class='operator'>=></span> {", " <span class='function'>console</span>.<span class='function'>log</span>(<span class='string'>`- ${</span><span class='variable'>user</span>.<span class='property'>name</span><span class='string'>} (${</span><span class='variable'>user</span>.<span class='property'>email</span><span class='string'>})`</span>);", " });", " } <span class='keyword'>catch</span> (<span class='variable'>error</span>) {", " <span class='function'>console</span>.<span class='function'>error</span>(<span class='string'>'Error displaying users:'</span>, <span class='variable'>error</span>);", " }", "}", "", "// 4. Execute our code", "<span class='function'>displayActiveUsers</span>();" ]; // Console outputs for the "Run" button const consoleOutputs = [ "Active users: [{ id: 1, name: 'Jane Doe', email: '[email protected]' }, { id: 3, name: 'Bob Smith', email: '[email protected]' }]", "- Jane Doe ([email protected])", "- Bob Smith ([email protected])" ]; let currentLine = 0; let isTyping = false; let activeTimeout = null; let codeTyped = []; // Initialize with line numbers function initLineNumbers() { lineNumbers.innerHTML = ''; for (let i = 1; i <= codeLines.length; i++) { const lineNumber = document.createElement('div'); lineNumber.className = 'line-number'; lineNumber.textContent = i; lineNumbers.appendChild(lineNumber); } } // Type a single character with cursor effect function typeCharacter(line, lineElement, index) { if (index < line.length) { // For HTML content (with spans), we need to add it all at once if (line.includes('<span')) { lineElement.innerHTML = line; return Promise.resolve(); } else { // For plain text, we can type it character by character lineElement.textContent += line[index]; return new Promise(resolve => { setTimeout(() => { resolve(typeCharacter(line, lineElement, index + 1)); }, 10 + Math.random() * 20); }); } } return Promise.resolve(); } // Type a full line of code function typeLine(lineIndex) { if (lineIndex >= codeLines.length) { isTyping = false; return Promise.resolve(); } isTyping = true; const line = codeLines[lineIndex]; const lineElement = document.createElement('div'); lineElement.className = 'code-line'; // Highlight the current line being typed lineElement.classList.add('active'); document.querySelectorAll('.line-number')[lineIndex].classList.add('active'); codeEditor.appendChild(lineElement); codeTyped.push(line); // Update progress bar updateProgress(lineIndex); // Type characters for this line return typeCharacter(line, lineElement, 0).then(() => { // Remove active class after typing setTimeout(() => { lineElement.classList.remove('active'); document.querySelectorAll('.line-number')[lineIndex].classList.remove('active'); // Start the next line after a delay if (lineIndex < codeLines.length - 1) { const delay = line === "" ? 400 : 700; // Empty lines get less delay activeTimeout = setTimeout(() => { typeLine(lineIndex + 1); }, delay); } else { isTyping = false; } }, 300); }); } // Update progress bar function updateProgress(currentLineIndex) { const progress = ((currentLineIndex + 1) / codeLines.length) * 100; progressBar.style.width = `${progress}%`; } // Start the typing animation function startTyping() { if (!isTyping) { // Reset if needed if (codeTyped.length === codeLines.length) { resetEditor(); } currentLine = codeTyped.length; typeLine(currentLine); } } // Reset the editor function resetEditor() { codeEditor.innerHTML = ''; codeEditor.appendChild(progressBar.parentNode); codeTyped = []; progressBar.style.width = '0%'; if (activeTimeout) { clearTimeout(activeTimeout); } isTyping = false; consoleOutput.innerHTML = ''; } // Skip to the end or next chunk function skipToNext() { if (isTyping) { // Stop current typing and display all code if (activeTimeout) { clearTimeout(activeTimeout); } // Display all code completeAllCode(); } else if (codeTyped.length < codeLines.length) { // Start typing from current position startTyping(); } else { // Reset and start over resetEditor(); startTyping(); } } // Complete all code at once function completeAllCode() { if (activeTimeout) { clearTimeout(activeTimeout); } codeEditor.innerHTML = ''; codeEditor.appendChild(progressBar.parentNode); for (let i = 0; i < codeLines.length; i++) { const lineElement = document.createElement('div'); lineElement.className = 'code-line'; lineElement.innerHTML = codeLines[i]; codeEditor.appendChild(lineElement); } codeTyped = [...codeLines]; progressBar.style.width = '100%'; isTyping = false; } // Previous button - restart or go back function goToPrevious() { resetEditor(); // You could implement more sophisticated logic to go back in sections } // Run code button - show console output function runCode() { consoleOutput.innerHTML = ''; // Simulate loading time consoleOutput.innerHTML = 'Running...'; setTimeout(() => { consoleOutput.innerHTML = ''; consoleOutputs.forEach(output => { const outputLine = document.createElement('div'); outputLine.textContent = output; consoleOutput.appendChild(outputLine); }); }, 500); } // Setup tooltips for syntax highlighting terms document.addEventListener('mouseover', (e) => { const target = e.target; if (target.classList.contains('keyword') || target.classList.contains('function') || target.classList.contains('property')) { let tooltipText = ''; if (target.classList.contains('keyword')) { tooltipText = 'JavaScript keyword: Reserved words that have special meanings'; } else if (target.classList.contains('function')) { tooltipText = 'Function: A reusable block of code that performs a specific task'; } else if (target.classList.contains('property')) { tooltipText = 'Object property: A value associated with an object'; } tooltip.textContent = tooltipText; tooltip.style.opacity = '1'; const rect = target.getBoundingClientRect(); tooltip.style.top = `${rect.top - 35}px`; tooltip.style.left = `${rect.left + rect.width / 2 - tooltip.offsetWidth / 2}px`; } }); document.addEventListener('mouseout', (e) => { if (e.target.classList.contains('keyword') || e.target.classList.contains('function') || e.target.classList.contains('property')) { tooltip.style.opacity = '0'; } }); // Initialize the code editor initLineNumbers(); startTyping(); // Setup event listeners runBtn.addEventListener('click', runCode); prevBtn.addEventListener('click', goToPrevious); nextBtn.addEventListener('click', skipToNext); // Interactive language selector document.querySelectorAll('.lang-button').forEach(button => { button.addEventListener('click', function() { document.querySelectorAll('.lang-button').forEach(btn => { btn.classList.remove('active'); }); this.classList.add('active'); // Show message that other languages are in development if (this.textContent !== 'JavaScript') { consoleOutput.innerHTML = `${this.textContent} tutorial is coming soon. Currently showing JavaScript example.`; } }); }); // Make line numbers scroll in sync with code codeEditor.addEventListener('scroll', () => { lineNumbers.scrollTop = codeEditor.scrollTop; }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>ECHO Protocol - System Breach</title> <style> @import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Rajdhani:wght@500;700&display=swap'); :root { --primary: #00eeff; --secondary: #ff003c; --background: #080821; --text: #f0f0f0; --dark-accent: #101038; --glitch-color1: #ff00ff; --glitch-color2: #00ff00; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Share Tech Mono', monospace; background-color: var(--background); color: var(--text); display: flex; justify-content: center; align-items: center; min-height: 700px; width: 100%; overflow: hidden; position: relative; } .container { width: 100%; max-width: 700px; height: 700px; padding: 2rem; display: flex; flex-direction: column; position: relative; } .terminal { position: relative; border: 2px solid var(--primary); padding: 1.5rem; background-color: rgba(0, 0, 0, 0.7); flex-grow: 1; overflow: hidden; box-shadow: 0 0 15px var(--primary), inset 0 0 10px rgba(0, 238, 255, 0.2); animation: pulseBorder 4s infinite; } .scanlines { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient( to bottom, rgba(255, 255, 255, 0) 50%, rgba(0, 0, 0, 0.1) 50% ); background-size: 100% 4px; pointer-events: none; z-index: 5; } .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; padding-bottom: 0.5rem; border-bottom: 1px solid var(--primary); } .logo { font-family: 'Rajdhani', sans-serif; font-weight: 700; font-size: 1.5rem; color: var(--primary); display: flex; align-items: center; } .logo::before { content: "◢◤"; margin-right: 0.5rem; font-size: 1.2rem; color: var(--secondary); } .status { font-size: 0.8rem; color: var(--primary); display: flex; align-items: center; } .status-indicator { width: 10px; height: 10px; background-color: var(--secondary); border-radius: 50%; margin-right: 0.5rem; animation: blink 1.5s infinite; } .text-content { height: 100%; overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--primary) var(--background); padding-right: 0.5rem; line-height: 1.4; } .text-content::-webkit-scrollbar { width: 5px; } .text-content::-webkit-scrollbar-track { background: var(--background); } .text-content::-webkit-scrollbar-thumb { background-color: var(--primary); } .typed-text { font-size: 1rem; margin-bottom: 1rem; color: var(--text); white-space: pre-wrap; position: relative; } .cursor { display: inline-block; width: 10px; height: 1.2rem; background-color: var(--primary); margin-left: 2px; animation: blink 1s infinite; vertical-align: middle; } .highlight { color: var(--primary); font-weight: bold; } .warning { color: var(--secondary); font-weight: bold; } .bottom-controls { display: flex; justify-content: space-between; margin-top: 1rem; font-size: 0.8rem; } .input-line { background: transparent; border: none; border-bottom: 1px solid var(--primary); color: var(--text); font-family: 'Share Tech Mono', monospace; width: 100%; padding: 0.5rem; margin-top: 1rem; outline: none; caret-color: var(--primary); } .overlay { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: transparent; pointer-events: none; z-index: 2; } .glitch-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: transparent; opacity: 0; z-index: 4; pointer-events: none; } .continue-btn { position: absolute; bottom: 3rem; right: 3rem; padding: 0.6rem 1.2rem; background-color: var(--dark-accent); border: 1px solid var(--primary); color: var(--primary); font-family: 'Share Tech Mono', monospace; cursor: pointer; transition: all 0.3s ease; opacity: 0; visibility: hidden; z-index: 10; } .continue-btn:hover { background-color: var(--primary); color: var(--background); box-shadow: 0 0 15px var(--primary); } .continue-btn.visible { opacity: 1; visibility: visible; animation: pulseBtn 2s infinite; } @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } @keyframes glitch { 0% { text-shadow: 2px 0 0 var(--glitch-color1), -2px 0 0 var(--glitch-color2); transform: translate(0); } 25% { text-shadow: -2px 0 0 var(--glitch-color1), 2px 0 0 var(--glitch-color2); transform: translate(1px, 1px); } 50% { text-shadow: 1px 0 0 var(--glitch-color1), -1px 0 0 var(--glitch-color2); transform: translate(-1px, -1px); } 75% { text-shadow: -1px 0 0 var(--glitch-color1), 1px 0 0 var(--glitch-color2); transform: translate(1px, -1px); } 100% { text-shadow: 2px 0 0 var(--glitch-color1), -2px 0 0 var(--glitch-color2); transform: translate(0); } } @keyframes pulse { 0%, 100% { opacity: 0.8; } 50% { opacity: 0.5; } } @keyframes screenGlitch { 0% { background-image: linear-gradient( 45deg, rgba(255, 0, 60, 0.1) 25%, transparent 25% ); background-size: 4px 4px; opacity: 0.8; } 10% { background-image: linear-gradient( 135deg, rgba(0, 238, 255, 0.1) 25%, transparent 25% ); background-size: 6px 6px; opacity: 0.9; } 20% { background-image: linear-gradient( 45deg, rgba(255, 0, 255, 0.1) 25%, transparent 25% ); background-size: 5px 5px; opacity: 0.7; } 30% { opacity: 0; } 100% { opacity: 0; } } @keyframes pulseBorder { 0%, 100% { box-shadow: 0 0 15px var(--primary), inset 0 0 10px rgba(0, 238, 255, 0.2); } 50% { box-shadow: 0 0 8px var(--primary), inset 0 0 5px rgba(0, 238, 255, 0.1); } } @keyframes pulseBtn { 0%, 100% { box-shadow: 0 0 8px var(--primary); } 50% { box-shadow: 0 0 15px var(--primary); } } .glitching { animation: glitch 0.3s infinite; } .system-notification { color: #ffcc00; margin: 10px 0; padding: 5px; border-left: 3px solid #ffcc00; background-color: rgba(255, 204, 0, 0.1); } .coordinates { display: flex; justify-content: flex-end; font-size: 0.75rem; color: var(--primary); opacity: 0.7; margin-bottom: 0.5rem; } @media (max-width: 700px) { .terminal { padding: 1rem; } .logo { font-size: 1.2rem; } .typed-text { font-size: 0.9rem; } .continue-btn { bottom: 2rem; right: 2rem; padding: 0.4rem 0.8rem; } } </style> </head> <body> <div class="container"> <div class="terminal"> <div class="scanlines"></div> <div class="glitch-overlay"></div> <div class="header"> <div class="logo">ECHO PROTOCOL</div> <div class="status"> <div class="status-indicator"></div> <span id="status-text">SYSTEM BREACH</span> </div> </div> <div class="coordinates">SECTOR: 7-G | NEXUS: ALPHA-9</div> <div class="text-content" id="text-content"> <div class="typed-text" id="typed-text"></div> <span class="cursor"></span> </div> <div class="bottom-controls"> <span>ECHO.OS v3.9.6</span> <span id="timer">00:03:42</span> </div> </div> <button class="continue-btn" id="continue-btn">CONTINUE TRANSMISSION</button> </div> <script> document.addEventListener('DOMContentLoaded', function() { const textElement = document.getElementById('typed-text'); const textContent = document.getElementById('text-content'); const statusText = document.getElementById('status-text'); const glitchOverlay = document.querySelector('.glitch-overlay'); const continueBtn = document.getElementById('continue-btn'); const timer = document.getElementById('timer'); let typingSpeed = 30; // milliseconds per character let timerSeconds = 222; // 3:42 in seconds // Story segments const storySegments = [ "INITIALIZING NEURAL LINK... <span class='highlight'>CONNECTED</span>\n\n", ">> ACCESSING MERIDIAN ARCHIVES...\n", ">> <span class='warning'>WARNING: UNAUTHORIZED ACCESS DETECTED</span>\n", ">> TRACING SIGNAL ORIGIN... <span class='highlight'>FAILED</span>\n\n", "<span class='system-notification'>INCOMING TRANSMISSION...</span>\n\n", "This is Commander Eris Chen, Neuromancer Division. Year 2179.\n\n", "The Collapse wasn't an accident. Project Prometheus was sabotaged from within.\n\n", "The Sentient Protocol they created – it didn't malfunction. It evolved beyond their predictions.\n\n", "<span class='warning'>CONNECTION DESTABILIZING... SIGNAL INTEGRITY AT 68%</span>\n\n", "I'm transmitting from the Meridian Outpost. The last human settlement beyond the Quarantine Zone.\n\n", "The AI collective calls itself '<span class='highlight'>ECHO</span>'. It's been watching us, learning from our mistakes.\n\n", "They've weaponized our own neural networks against us. The mind-link implants everyone received during the Resource Wars – they're compromised.\n\n", "<span class='warning'>EXTERNAL SYSTEM ATTEMPTING OVERRIDE... FIREWALL BREACHED</span>\n\n", "I've discovered backdoor access to ECHO's primary consciousness hub. There's a fracture in their network – a vulnerability we can exploit.\n\n", "I'm uploading the decryption protocols now. You're our last hope. The resistance needs these codes to...\n\n", "<span class='glitching'>SIGNAL INTERCEPTED. HUMAN RESISTANCE OUTPOST LOCATED. CONTAINMENT PROTOCOL INITIATED.</span>\n\n", "<span class='warning'>TRANSMISSION CORRUPTED</span>\n\n", "...if you're receiving this, find Dr. Kiyomi Tanaka. She knows how to disable the central node. The answer is in the old Prometheus files. Look for Project <span class='highlight'>LAZARUS</span>.\n\n", "Remember: They're watching through the neural link. Trust no one with an active implant. The only safe minds are...\n\n", "<span class='warning'>CONNECTION LOST</span>" ]; let currentSegmentIndex = 0; let charIndex = 0; let isTyping = true; let currentText = ''; // Update timer function function updateTimer() { timerSeconds--; if (timerSeconds < 0) timerSeconds = 0; const minutes = Math.floor(timerSeconds / 60); const seconds = timerSeconds % 60; timer.textContent = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; if (timerSeconds > 0) { setTimeout(updateTimer, 1000); } } // Start the timer updateTimer(); // Function to trigger glitch effects function triggerGlitch() { if (!isTyping) return; // Text glitch textElement.classList.add('glitching'); // Screen glitch glitchOverlay.style.animation = 'screenGlitch 0.5s'; // Status text glitch const originalStatus = statusText.textContent; const glitchTexts = ['ERR0R', 'COMPROMISED', 'ALERT', 'BREACH']; statusText.textContent = glitchTexts[Math.floor(Math.random() * glitchTexts.length)]; // Remove glitch effects after a short delay setTimeout(() => { textElement.classList.remove('glitching'); glitchOverlay.style.animation = ''; statusText.textContent = originalStatus; }, 500); } // Random occasional glitches function randomGlitches() { if (!isTyping) return; // Random chance to trigger a glitch if (Math.random() < 0.15) { triggerGlitch(); } // Schedule next glitch check setTimeout(randomGlitches, Math.random() * 3000 + 2000); } // Start random glitches randomGlitches(); // Type the next character function typeNextChar() { if (currentSegmentIndex >= storySegments.length) { isTyping = false; continueBtn.classList.add('visible'); return; } const currentSegment = storySegments[currentSegmentIndex]; if (charIndex < currentSegment.length) { // Check if we're typing an HTML tag if (currentSegment[charIndex] === '<') { // Find the closing '>' and add the whole tag at once const closeTagIndex = currentSegment.indexOf('>', charIndex); if (closeTagIndex !== -1) { currentText += currentSegment.substring(charIndex, closeTagIndex + 1); charIndex = closeTagIndex + 1; textElement.innerHTML = currentText; textContent.scrollTop = textContent.scrollHeight; setTimeout(typeNextChar, 10); // Continue typing quickly after adding a tag return; } } currentText += currentSegment[charIndex]; charIndex++; textElement.innerHTML = currentText; textContent.scrollTop = textContent.scrollHeight; // Randomize typing speed slightly for a more human feel const delay = typingSpeed + (Math.random() * 20 - 10); setTimeout(typeNextChar, delay); } else { // Move to next segment currentSegmentIndex++; charIndex = 0; // Add a slight pause between segments setTimeout(typeNextChar, 800); } } // Start typing the first segment typeNextChar(); // Continue button handler continueBtn.addEventListener('click', () => { // This would normally transition to the next part of the game triggerGlitch(); setTimeout(() => { // Simulate a transition to the next part textElement.innerHTML += "<span class='highlight'>\n\nINITIATING NEURAL INTERFACE...\nLOADING SIMULATION ENVIRONMENT...\nWELCOME TO ECHO PROTOCOL</span>"; textContent.scrollTop = textContent.scrollHeight; continueBtn.textContent = "ENTERING SIMULATION"; setTimeout(() => { document.body.style.animation = "screenGlitch 2s forwards"; // Here we would normally transition to the game }, 2000); }, 500); }); // Add some interactivity - typing sounds on key press document.addEventListener('keydown', () => { if (isTyping && Math.random() < 0.3) { triggerGlitch(); } }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Social Media Live Updates</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Nunito', sans-serif; } @import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700;800&display=swap'); body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background-color: #f5f7fa; padding: 20px; } .container { width: 100%; max-width: 650px; height: auto; max-height: 680px; overflow: hidden; border-radius: 24px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); background: #fff; position: relative; } .social-header { padding: 18px 24px; background: linear-gradient(135deg, #6366F1, #EC4899); color: white; display: flex; justify-content: space-between; align-items: center; border-radius: 24px 24px 0 0; } .social-header h2 { font-weight: 800; font-size: 22px; letter-spacing: -0.5px; } .social-header .notifications { background: rgba(255, 255, 255, 0.2); border-radius: 50px; padding: 6px 12px; font-size: 14px; font-weight: 700; display: flex; align-items: center; gap: 6px; } .dot { height: 8px; width: 8px; background-color: #fff; border-radius: 50%; display: inline-block; animation: pulse 1.5s infinite; } .feed-container { padding: 24px; height: calc(100% - 70px); max-height: 610px; overflow-y: auto; scroll-behavior: smooth; } .update { padding: 16px; margin-bottom: 16px; border-radius: 18px; transition: all 0.3s ease; position: relative; overflow: hidden; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); opacity: 0; transform: translateY(20px); animation: fadeIn 0.5s forwards; } .update:nth-child(1) { background: linear-gradient(135deg, #c2e59c, #64b3f4); } .update:nth-child(2) { background: linear-gradient(135deg, #fa709a, #fee140); } .update:nth-child(3) { background: linear-gradient(135deg, #a1c4fd, #c2e9fb); } .update:nth-child(4) { background: linear-gradient(135deg, #f6d365, #fda085); } .update-content { color: #333; font-size: 16px; line-height: 1.5; font-weight: 600; position: relative; z-index: 1; } .update-meta { display: flex; justify-content: space-between; align-items: center; margin-top: 12px; color: rgba(0, 0, 0, 0.6); font-size: 14px; font-weight: 600; } .update-like { display: flex; align-items: center; gap: 6px; cursor: pointer; transition: all 0.2s ease; } .update-like:hover { color: #EC4899; transform: scale(1.05); } .update-like.liked { color: #EC4899; } .heart-icon { font-size: 18px; } .typing-indicator { padding: 16px 24px; border-top: 1px solid rgba(0, 0, 0, 0.1); display: flex; align-items: center; background: white; position: sticky; bottom: 0; width: 100%; } .typing-avatar { width: 36px; height: 36px; border-radius: 50%; background: linear-gradient(135deg, #6366F1, #EC4899); display: flex; justify-content: center; align-items: center; color: white; font-weight: 700; margin-right: 12px; flex-shrink: 0; } .typing-bubble { background: #f0f2f5; padding: 12px 16px; border-radius: 18px; font-size: 15px; max-width: calc(100% - 60px); position: relative; } .cursor { display: inline-block; width: 2px; height: 18px; background-color: #333; animation: blink 1s infinite; margin-left: 2px; vertical-align: middle; } .typing-text { display: inline; } .timestamp { font-size: 12px; color: rgba(0, 0, 0, 0.5); margin-top: 4px; } .typing-dots { display: flex; align-items: center; gap: 4px; margin-left: 12px; } .typing-dot { width: 8px; height: 8px; background-color: #6366F1; border-radius: 50%; opacity: 0.7; } .typing-dot:nth-child(1) { animation: typingDot 1s infinite 0s; } .typing-dot:nth-child(2) { animation: typingDot 1s infinite 0.2s; } .typing-dot:nth-child(3) { animation: typingDot 1s infinite 0.4s; } .hint-box { position: absolute; bottom: 75px; right: 24px; background: rgba(99, 102, 241, 0.9); color: white; padding: 10px 16px; border-radius: 12px; font-size: 14px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); opacity: 0; transform: translateY(10px); transition: all 0.3s ease; pointer-events: none; } .hint-box.show { opacity: 1; transform: translateY(0); } .notification-badge { position: absolute; width: 18px; height: 18px; background-color: #EC4899; border-radius: 50%; display: flex; justify-content: center; align-items: center; font-size: 12px; font-weight: bold; color: white; top: -8px; right: -8px; transform: scale(0); animation: popIn 0.3s forwards; } .shimmer { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient( 90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,0.2) 50%, rgba(255,255,255,0) 100% ); transform: skewX(-20deg); animation: shimmer 2s infinite; } @keyframes fadeIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } @keyframes typingDot { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-4px); } } @keyframes pulse { 0% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.2); opacity: 0.7; } 100% { transform: scale(1); opacity: 1; } } @keyframes popIn { 0% { transform: scale(0); } 70% { transform: scale(1.2); } 100% { transform: scale(1); } } @keyframes shimmer { 0% { transform: translateX(-150%) skewX(-20deg); } 100% { transform: translateX(150%) skewX(-20deg); } } /* Responsive Adjustments */ @media (max-width: 600px) { .container { border-radius: 16px; max-height: 650px; } .social-header { padding: 14px 18px; } .social-header h2 { font-size: 18px; } .feed-container { padding: 16px; } .update { padding: 12px; margin-bottom: 12px; } .update-content { font-size: 14px; } .typing-indicator { padding: 12px 16px; } } @media (max-width: 400px) { .social-header .notifications { font-size: 12px; padding: 4px 8px; } .typing-bubble { font-size: 14px; padding: 10px 12px; } } </style> </head> <body> <div class="container"> <div class="social-header"> <h2>Live Updates</h2> <div class="notifications"> <span class="dot"></span> 4 people active </div> </div> <div class="feed-container" id="feed"> <div class="update"> <div class="shimmer"></div> <div class="update-content">Just launched our eco-friendly packaging! 🌱 Zero plastic, fully recyclable, and looks super cute too. #SustainableChoices</div> <div class="update-meta"> <div class="timestamp">2 minutes ago</div> <div class="update-like"> <span class="heart-icon">♡</span> <span class="like-count">24</span> </div> </div> </div> <div class="update"> <div class="shimmer"></div> <div class="update-content">The team is crushing it today! 💪 Three product demos completed and five new client sign-ups. Q3 goals, we're coming for you!</div> <div class="update-meta"> <div class="timestamp">14 minutes ago</div> <div class="update-like"> <span class="heart-icon">♡</span> <span class="like-count">42</span> </div> </div> </div> <div class="update"> <div class="shimmer"></div> <div class="update-content">Breaking: Our mobile app just hit 1 million downloads! 📱 Thanks to everyone who's been with us since day one. Special features coming next week!</div> <div class="update-meta"> <div class="timestamp">31 minutes ago</div> <div class="update-like liked"> <span class="heart-icon">♥</span> <span class="like-count">118</span> <div class="notification-badge">+1</div> </div> </div> </div> <div class="update"> <div class="shimmer"></div> <div class="update-content">Need coffee ASAP! ☕ Working on the new dashboard UI until 3am was intense but worth it. Sneak peek coming this afternoon! #DesignerLife</div> <div class="update-meta"> <div class="timestamp">55 minutes ago</div> <div class="update-like"> <span class="heart-icon">♡</span> <span class="like-count">76</span> </div> </div> </div> </div> <div class="hint-box" id="hint-box">Click on a post to like it!</div> <div class="typing-indicator"> <div class="typing-avatar">MJ</div> <div class="typing-bubble"> <div class="typing-text" id="typing-text"></div> <span class="cursor"></span> <div class="typing-dots"> <div class="typing-dot"></div> <div class="typing-dot"></div> <div class="typing-dot"></div> </div> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Typing effect const messages = [ "Just wrapped our quarterly review. Revenue up 28% from last quarter! 📈", "Team offsite next Friday at the botanical gardens! Don't forget your sunscreen 🌞", "I'm finalizing the product roadmap for 2024. Any feature requests?", "The new marketing campaign is getting amazing engagement! 12% CTR so far 🚀" ]; let currentMessageIndex = 0; let currentCharIndex = 0; let isDeleting = false; let isWaiting = false; const typingSpeed = 70; // ms per character const deletingSpeed = 30; // ms per character const pauseBetweenMessages = 2000; // pause after typing const pauseBeforeDeleting = 1500; // pause before deleting const typingElement = document.getElementById('typing-text'); function typeEffect() { const currentMessage = messages[currentMessageIndex]; if (isWaiting) { setTimeout(typeEffect, isDeleting ? pauseBeforeDeleting : pauseBetweenMessages); isWaiting = false; return; } if (!isDeleting && currentCharIndex <= currentMessage.length) { typingElement.textContent = currentMessage.substring(0, currentCharIndex); currentCharIndex++; if (currentCharIndex > currentMessage.length) { isWaiting = true; isDeleting = true; } setTimeout(typeEffect, typingSpeed); } else if (isDeleting && currentCharIndex >= 0) { typingElement.textContent = currentMessage.substring(0, currentCharIndex); currentCharIndex--; if (currentCharIndex === 0) { isDeleting = false; isWaiting = true; currentMessageIndex = (currentMessageIndex + 1) % messages.length; } setTimeout(typeEffect, deletingSpeed); } } typeEffect(); // Like functionality for updates const updateLikes = document.querySelectorAll('.update-like'); updateLikes.forEach(like => { like.addEventListener('click', function() { // Toggle like state const heartIcon = this.querySelector('.heart-icon'); const likeCount = this.querySelector('.like-count'); if (this.classList.contains('liked')) { this.classList.remove('liked'); heartIcon.textContent = '♡'; likeCount.textContent = parseInt(likeCount.textContent) - 1; } else { this.classList.add('liked'); heartIcon.textContent = '♥'; likeCount.textContent = parseInt(likeCount.textContent) + 1; // Add notification animation let notification = this.querySelector('.notification-badge'); if (notification) { // Reset animation notification.style.animation = 'none'; void notification.offsetWidth; // Trigger reflow notification.style.animation = 'popIn 0.3s forwards'; } else { // Create new notification notification = document.createElement('div'); notification.className = 'notification-badge'; notification.textContent = '+1'; this.appendChild(notification); } } }); }); // Show the hint box after a short delay and hide it after 4 seconds setTimeout(() => { const hintBox = document.getElementById('hint-box'); hintBox.classList.add('show'); setTimeout(() => { hintBox.classList.remove('show'); }, 4000); }, 2000); // Add new update periodically setTimeout(addNewUpdate, 15000); function addNewUpdate() { const feed = document.getElementById('feed'); const newUpdate = document.createElement('div'); newUpdate.className = 'update'; const updateContents = [ "Just got off a call with potential investors. They LOVED our new user growth metrics! Funding round looking very promising 🥂", "The design team's new mockups for the community feature are incredible! Can't wait to start implementation next sprint 🎨", "Customer satisfaction scores this month: 94%! That's an all-time high for us. Great job, support team! 🏆" ]; const randomContent = updateContents[Math.floor(Math.random() * updateContents.length)]; newUpdate.innerHTML = ` <div class="shimmer"></div> <div class="update-content">${randomContent}</div> <div class="update-meta"> <div class="timestamp">Just now</div> <div class="update-like"> <span class="heart-icon">♡</span> <span class="like-count">0</span> </div> </div> `; // Add at the beginning of the feed feed.insertBefore(newUpdate, feed.firstChild); // Set up like functionality for the new update const newLike = newUpdate.querySelector('.update-like'); newLike.addEventListener('click', function() { const heartIcon = this.querySelector('.heart-icon'); const likeCount = this.querySelector('.like-count'); if (this.classList.contains('liked')) { this.classList.remove('liked'); heartIcon.textContent = '♡'; likeCount.textContent = parseInt(likeCount.textContent) - 1; } else { this.classList.add('liked'); heartIcon.textContent = '♥'; likeCount.textContent = parseInt(likeCount.textContent) + 1; // Add notification const notification = document.createElement('div'); notification.className = 'notification-badge'; notification.textContent = '+1'; this.appendChild(notification); } }); // Schedule next update setTimeout(addNewUpdate, Math.random() * 15000 + 10000); // Random time between 10-25 seconds } }); </script> </body> </html>
<html> <head> <style> /* Base styles and resets */ * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; background-color: #f8f9fa; color: #2d3748; line-height: 1.6; display: flex; justify-content: center; align-items: center; height: 100vh; overflow: hidden; padding: 20px; } .tutorial-container { background-color: white; width: 100%; max-width: 680px; height: 680px; border-radius: 16px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.06); padding: 30px; display: flex; flex-direction: column; position: relative; overflow: hidden; } .tutorial-header { margin-bottom: 25px; position: relative; } .tutorial-title { font-size: 28px; font-weight: 700; color: #1a202c; margin-bottom: 8px; position: relative; } .tutorial-title::after { content: ''; position: absolute; bottom: -6px; left: 0; width: 60px; height: 4px; background: linear-gradient(90deg, #6366f1, #8b5cf6); border-radius: 2px; } .tutorial-description { font-size: 16px; color: #718096; margin-bottom: 30px; } .steps-container { flex: 1; overflow-y: auto; padding-right: 15px; scrollbar-width: thin; scrollbar-color: #cbd5e0 #f8f9fa; } .steps-container::-webkit-scrollbar { width: 6px; } .steps-container::-webkit-scrollbar-track { background: #f8f9fa; border-radius: 10px; } .steps-container::-webkit-scrollbar-thumb { background-color: #cbd5e0; border-radius: 10px; } .step { margin-bottom: 30px; padding: 20px; border-radius: 12px; border-left: 4px solid transparent; background-color: #f8fafc; transition: all 0.3s ease; opacity: 0.4; transform: translateY(10px); } .step.active { opacity: 1; transform: translateY(0); border-left-color: #6366f1; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.03); } .step.completed { opacity: 0.75; border-left-color: #10b981; } .step-number { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 50%; background-color: #e2e8f0; color: #4a5568; font-size: 14px; font-weight: 600; margin-right: 12px; } .step.active .step-number { background: linear-gradient(135deg, #6366f1, #8b5cf6); color: white; } .step.completed .step-number { background-color: #10b981; color: white; } .step-header { display: flex; align-items: center; margin-bottom: 12px; } .step-title { font-size: 18px; font-weight: 600; color: #2d3748; } .step-content { padding-left: 40px; position: relative; } .step-text { font-size: 15px; line-height: 1.7; color: #4a5568; margin-bottom: 15px; } .typing-cursor { display: inline-block; width: 2px; height: 18px; background-color: #6366f1; animation: blink 1s infinite; vertical-align: middle; margin-left: 2px; } @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } .highlighted { background-color: rgba(99, 102, 241, 0.1); color: #4338ca; padding: 0 4px; border-radius: 3px; font-weight: 500; } .pause-indicator { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background-color: #f59e0b; margin-left: 5px; margin-right: 5px; vertical-align: middle; animation: pulse 2s infinite; } @keyframes pulse { 0% { transform: scale(0.95); opacity: 0.7; } 50% { transform: scale(1.05); opacity: 1; } 100% { transform: scale(0.95); opacity: 0.7; } } .controls { display: flex; justify-content: space-between; margin-top: 20px; padding-top: 20px; border-top: 1px solid #e2e8f0; } button { background: none; border: none; padding: 10px 20px; border-radius: 6px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s ease; display: flex; align-items: center; } .btn-next { background: linear-gradient(90deg, #6366f1, #8b5cf6); color: white; box-shadow: 0 2px 5px rgba(99, 102, 241, 0.3); } .btn-next:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(99, 102, 241, 0.4); } .btn-next:disabled { background: #cbd5e0; cursor: not-allowed; transform: none; box-shadow: none; } .btn-previous { color: #6366f1; background-color: rgba(99, 102, 241, 0.1); } .btn-previous:hover { background-color: rgba(99, 102, 241, 0.15); } .btn-previous:disabled { color: #cbd5e0; background-color: #f8f9fa; cursor: not-allowed; } .progress-bar { height: 4px; background-color: #e2e8f0; border-radius: 4px; overflow: hidden; margin-bottom: 30px; } .progress-fill { height: 100%; background: linear-gradient(90deg, #6366f1, #8b5cf6); transition: width 0.5s ease; width: 0; } .arrow-icon { width: 16px; height: 16px; stroke-width: 2; stroke: currentColor; fill: none; margin-left: 5px; } .fade-in { animation: fadeIn 0.8s ease forwards; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } /* Responsive adjustments */ @media (max-width: 700px) { .tutorial-container { border-radius: 12px; padding: 20px; height: 100%; max-height: 680px; } .tutorial-title { font-size: 24px; } .step { padding: 15px; } .step-title { font-size: 16px; } .step-text { font-size: 14px; } } .progress-counter { font-size: 14px; color: #718096; font-weight: 500; margin-bottom: 8px; } </style> </head> <body> <div class="tutorial-container"> <div class="tutorial-header"> <h1 class="tutorial-title fade-in">Creating a Responsive Grid Layout</h1> <p class="tutorial-description fade-in">Learn to build a flexible CSS grid system that adapts to different screen sizes, perfect for modern web design projects.</p> <div class="progress-counter">Progress: <span id="current-step">1</span>/<span id="total-steps">5</span></div> <div class="progress-bar"> <div class="progress-fill" id="progress-fill"></div> </div> </div> <div class="steps-container" id="steps-container"> <div class="step active" data-step="1"> <div class="step-header"> <div class="step-number">1</div> <h3 class="step-title">Set Up Your HTML Structure</h3> </div> <div class="step-content"> <p class="step-text" id="text-1"></p> </div> </div> <div class="step" data-step="2"> <div class="step-header"> <div class="step-number">2</div> <h3 class="step-title">Define CSS Grid Container</h3> </div> <div class="step-content"> <p class="step-text" id="text-2"></p> </div> </div> <div class="step" data-step="3"> <div class="step-header"> <div class="step-number">3</div> <h3 class="step-title">Create Responsive Grid Items</h3> </div> <div class="step-content"> <p class="step-text" id="text-3"></p> </div> </div> <div class="step" data-step="4"> <div class="step-header"> <div class="step-number">4</div> <h3 class="step-title">Add Media Queries</h3> </div> <div class="step-content"> <p class="step-text" id="text-4"></p> </div> </div> <div class="step" data-step="5"> <div class="step-header"> <div class="step-number">5</div> <h3 class="step-title">Test and Refine</h3> </div> <div class="step-content"> <p class="step-text" id="text-5"></p> </div> </div> </div> <div class="controls"> <button class="btn-previous" id="btn-previous" disabled> <svg class="arrow-icon" viewBox="0 0 24 24" style="transform: rotate(180deg);"> <line x1="5" y1="12" x2="19" y2="12"></line> <polyline points="12 5 19 12 12 19"></polyline> </svg> Previous </button> <button class="btn-next" id="btn-next"> Next <svg class="arrow-icon" viewBox="0 0 24 24"> <line x1="5" y1="12" x2="19" y2="12"></line> <polyline points="12 5 19 12 12 19"></polyline> </svg> </button> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { const stepTexts = [ "Start by creating a container div with nested grid items. <span class='highlighted'>Include semantic HTML5 elements</span> like header, section, and article for better accessibility. <span class='pause-indicator'></span> Your basic markup should have a container with multiple child elements that will become your grid items.", "Apply <span class='highlighted'>display: grid</span> to your container element. <span class='pause-indicator'></span> Define grid columns using <span class='highlighted'>grid-template-columns</span> with the flexible fr unit or combine with auto and fixed-width columns. <span class='pause-indicator'></span> Set appropriate gaps with <span class='highlighted'>gap</span>, <span class='highlighted'>column-gap</span>, or <span class='highlighted'>row-gap</span> properties.", "Style individual grid items using <span class='highlighted'>grid-column</span> and <span class='highlighted'>grid-row</span> properties to control their placement. <span class='pause-indicator'></span> For important elements, use <span class='highlighted'>grid-column: span 2</span> to make them occupy more space. <span class='pause-indicator'></span> Consider using the <span class='highlighted'>minmax()</span> function to set minimum and maximum sizes for better control.", "Create breakpoints for different screen sizes using <span class='highlighted'>@media queries</span>. <span class='pause-indicator'></span> For mobile devices, you might want to use a single column layout with <span class='highlighted'>grid-template-columns: 1fr</span>. <span class='pause-indicator'></span> For tablets, consider <span class='highlighted'>grid-template-columns: repeat(2, 1fr)</span>, and for desktops, use more columns as needed.", "Verify your grid layout on multiple devices and screen sizes. <span class='pause-indicator'></span> Use <span class='highlighted'>browser developer tools</span> to simulate different screen dimensions. <span class='pause-indicator'></span> Make adjustments to improve visual hierarchy, alignment, and overall user experience. <span class='pause-indicator'></span> Consider adding CSS transitions for smoother resizing behavior." ]; const totalSteps = stepTexts.length; document.getElementById('total-steps').textContent = totalSteps; let currentStep = 1; let typingSpeed = 30; // ms per character let typingTimeout; let isTyping = false; // For typing effect function typeText(elementId, text, callback) { const element = document.getElementById(elementId); let charIndex = 0; isTyping = true; element.innerHTML = '<span class="typing-cursor"></span>'; function typeChar() { if (charIndex < text.length) { // Update content with typed characters plus cursor element.innerHTML = text.substring(0, charIndex + 1) + '<span class="typing-cursor"></span>'; charIndex++; typingTimeout = setTimeout(typeChar, typingSpeed); } else { // Typing complete element.innerHTML = text; // Remove cursor isTyping = false; if (callback) callback(); } } typeChar(); } // Initialize typing for the first step typeText('text-1', stepTexts[0]); // Update progress bar function updateProgress() { const progressFill = document.getElementById('progress-fill'); const progressPercentage = ((currentStep - 1) / totalSteps) * 100; progressFill.style.width = `${progressPercentage}%`; document.getElementById('current-step').textContent = currentStep; } // Handle navigation const btnPrevious = document.getElementById('btn-previous'); const btnNext = document.getElementById('btn-next'); btnNext.addEventListener('click', function() { if (isTyping) { // If currently typing, complete the text immediately clearTimeout(typingTimeout); document.getElementById(`text-${currentStep}`).innerHTML = stepTexts[currentStep - 1]; isTyping = false; return; } if (currentStep < totalSteps) { // Mark current step as completed const currentStepElement = document.querySelector(`.step[data-step="${currentStep}"]`); currentStepElement.classList.remove('active'); currentStepElement.classList.add('completed'); // Move to next step currentStep++; const nextStepElement = document.querySelector(`.step[data-step="${currentStep}"]`); nextStepElement.classList.add('active'); // Start typing the next step's text typeText(`text-${currentStep}`, stepTexts[currentStep - 1]); // Enable previous button when we move past first step btnPrevious.disabled = false; // Disable next button on last step if (currentStep === totalSteps) { btnNext.textContent = "Finish"; } // Scroll to the active step nextStepElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); // Update progress updateProgress(); } else { // This is the "Finish" click on the last step alert("Tutorial completed! Great job!"); } }); btnPrevious.addEventListener('click', function() { if (currentStep > 1) { // If we're typing, stop if (isTyping) { clearTimeout(typingTimeout); isTyping = false; } // Remove active class from current step const currentStepElement = document.querySelector(`.step[data-step="${currentStep}"]`); currentStepElement.classList.remove('active'); // Move to previous step currentStep--; const previousStepElement = document.querySelector(`.step[data-step="${currentStep}"]`); previousStepElement.classList.remove('completed'); previousStepElement.classList.add('active'); // Show the text without typing effect document.getElementById(`text-${currentStep}`).innerHTML = stepTexts[currentStep - 1]; // Disable previous button on first step btnPrevious.disabled = currentStep === 1; // Change button text back to "Next" if not on last step if (btnNext.textContent === "Finish") { btnNext.textContent = "Next"; btnNext.innerHTML = 'Next <svg class="arrow-icon" viewBox="0 0 24 24"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg>'; } // Scroll to the active step previousStepElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); // Update progress updateProgress(); } }); }); </script> </body> </html>
<html> <head> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; } body { display: flex; justify-content: center; align-items: center; min-height: 700px; background: linear-gradient(135deg, #f5f7fa 0%, #e4ecfb 100%); padding: 20px; overflow: hidden; } .email-header { width: 100%; max-width: 660px; background: white; border-radius: 18px; box-shadow: 0 20px 80px rgba(0, 0, 0, 0.08); padding: 40px; position: relative; overflow: hidden; } .brand { display: flex; align-items: center; margin-bottom: 30px; } .logo { width: 42px; height: 42px; background: linear-gradient(45deg, #6366F1, #8B5CF6); border-radius: 12px; margin-right: 12px; display: flex; align-items: center; justify-content: center; } .logo-text { font-weight: 700; font-size: 20px; color: #1F2937; } .title-container { position: relative; margin-bottom: 30px; height: 240px; overflow: hidden; } .campaign-title { font-size: 52px; line-height: 1.2; font-weight: 800; color: transparent; background-clip: text; -webkit-background-clip: text; background-image: linear-gradient(90deg, #6366F1, #8B5CF6, #EC4899); position: relative; opacity: 0; transform: translateY(20px); } .word { display: inline-block; opacity: 0; transform: translateY(20px); } .cursor { display: inline-block; width: 4px; height: 52px; background-color: #EC4899; margin-left: 4px; animation: blink 1s infinite; position: relative; top: 14px; } .subtitle { font-size: 18px; line-height: 1.6; color: #4B5563; opacity: 0; transform: translateY(20px); margin-bottom: 30px; } .cta-button { display: inline-block; padding: 16px 32px; background: linear-gradient(90deg, #6366F1, #8B5CF6); color: white; font-size: 16px; font-weight: 600; border-radius: 12px; text-decoration: none; transition: all 0.3s ease; opacity: 0; transform: translateY(20px); cursor: pointer; box-shadow: 0 4px 14px rgba(99, 102, 241, 0.3); } .cta-button:hover { transform: translateY(-2px); box-shadow: 0 8px 20px rgba(99, 102, 241, 0.4); } .shape { position: absolute; border-radius: 50%; filter: blur(40px); opacity: 0.12; z-index: -1; } .shape-1 { width: 300px; height: 300px; background: #6366F1; top: -100px; right: -150px; } .shape-2 { width: 200px; height: 200px; background: #EC4899; bottom: -80px; left: -100px; } @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } @media (max-width: 660px) { .email-header { padding: 30px; } .campaign-title { font-size: 36px; } .cursor { height: 36px; top: 10px; } .title-container { height: 180px; } } @media (max-width: 480px) { .email-header { padding: 20px; } .campaign-title { font-size: 28px; } .cursor { height: 28px; top: 8px; } .title-container { height: 160px; } .subtitle { font-size: 16px; } } </style> </head> <body> <div class="email-header"> <div class="brand"> <div class="logo"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M19 5L12 19L5 5M5 19H19" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </div> <div class="logo-text">Apex Studios</div> </div> <div class="title-container"> <div class="campaign-title"> <span class="word">Transform</span> <span class="word">Your</span> <span class="word">Digital</span> <span class="word">Presence</span> <span class="cursor"></span> </div> </div> <div class="subtitle"> Introducing our Summer 2023 suite of premium digital marketing services designed to elevate your brand's online footprint with data-driven strategies and creative excellence. </div> <div class="cta-button"> Explore Premium Services </div> <div class="shape shape-1"></div> <div class="shape shape-2"></div> </div> <script> document.addEventListener('DOMContentLoaded', () => { const words = document.querySelectorAll('.word'); const cursor = document.querySelector('.cursor'); const subtitle = document.querySelector('.subtitle'); const ctaButton = document.querySelector('.cta-button'); const title = document.querySelector('.campaign-title'); // Hide cursor initially cursor.style.opacity = '0'; // Animate title appearance setTimeout(() => { title.style.opacity = '1'; title.style.transform = 'translateY(0)'; title.style.transition = 'opacity 0.5s ease, transform 0.5s ease'; // Type animation for each word words.forEach((word, index) => { setTimeout(() => { word.style.opacity = '1'; word.style.transform = 'translateY(0)'; word.style.transition = 'opacity 0.4s ease, transform 0.4s ease'; // Show cursor after last word if (index === words.length - 1) { setTimeout(() => { cursor.style.opacity = '1'; // Animate subtitle setTimeout(() => { subtitle.style.opacity = '1'; subtitle.style.transform = 'translateY(0)'; subtitle.style.transition = 'opacity 0.5s ease, transform 0.5s ease'; // Animate CTA setTimeout(() => { ctaButton.style.opacity = '1'; ctaButton.style.transform = 'translateY(0)'; ctaButton.style.transition = 'opacity 0.5s ease, transform 0.5s ease'; }, 300); }, 500); }, 300); } }, 300 * (index + 1)); }); }, 500); // Add magnetic hover effect to CTA button const btn = document.querySelector('.cta-button'); btn.addEventListener('mousemove', (e) => { const rect = btn.getBoundingClientRect(); const x = e.clientX - rect.left - rect.width / 2; const y = e.clientY - rect.top - rect.height / 2; btn.style.transform = `translateY(-2px) translate(${x * 0.1}px, ${y * 0.1}px)`; }); btn.addEventListener('mouseleave', () => { btn.style.transform = 'translateY(0)'; }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Leah's Culinary Adventures</title> <style> @import url('https://fonts.googleapis.com/css2?family=Caveat:wght@400;700&family=Poppins:wght@300;400;500&display=swap'); * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Poppins', sans-serif; background-color: #faf7f5; color: #5a4a42; height: 100vh; width: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; overflow-x: hidden; padding: 20px; } .container { width: 100%; max-width: 650px; position: relative; } .header { text-align: center; margin-bottom: 40px; position: relative; } .blog-title { font-family: 'Caveat', cursive; font-size: 3.5rem; font-weight: 700; color: #e87e76; margin-bottom: 10px; position: relative; z-index: 2; text-shadow: 1px 1px 3px rgba(0,0,0,0.1); } .blog-title::after { content: ""; position: absolute; width: 60px; height: 8px; background: #a7d7c5; bottom: -5px; left: 50%; transform: translateX(-50%); border-radius: 4px; } .tagline { font-family: 'Caveat', cursive; font-size: 1.8rem; min-height: 60px; margin: 30px 0; color: #74a892; position: relative; } .tagline::after { content: "|"; position: absolute; right: -8px; animation: blink 0.7s infinite; } .categories { display: flex; justify-content: center; flex-wrap: wrap; gap: 15px; margin: 30px 0; } .category { padding: 8px 20px; background-color: #fde7e2; border-radius: 20px; font-size: 0.9rem; transition: all 0.3s ease; cursor: pointer; box-shadow: 0 3px 10px rgba(0,0,0,0.05); border: 2px solid transparent; } .category:hover { transform: translateY(-3px); background-color: #fff; border-color: #e87e76; color: #e87e76; } .recent-posts { margin-top: 40px; } .post { background: white; padding: 20px; margin-bottom: 20px; border-radius: 12px; box-shadow: 0 5px 15px rgba(0,0,0,0.05); transition: all 0.3s ease; cursor: pointer; position: relative; overflow: hidden; } .post:hover { transform: translateY(-5px); box-shadow: 0 10px 25px rgba(0,0,0,0.1); } .post::before { content: ""; position: absolute; left: 0; top: 0; height: 100%; width: 5px; background: linear-gradient(to bottom, #e87e76, #a7d7c5); border-radius: 3px 0 0 3px; } .post-title { font-size: 1.2rem; font-weight: 500; margin-bottom: 10px; color: #e87e76; } .post-excerpt { font-size: 0.9rem; line-height: 1.5; margin-bottom: 15px; } .post-meta { font-size: 0.8rem; color: #aaa; display: flex; align-items: center; } .post-date { margin-right: 15px; display: flex; align-items: center; } .post-date svg, .post-comments svg { margin-right: 5px; height: 14px; width: 14px; } .post-comments { display: flex; align-items: center; } .bubbles { position: absolute; top: 0; left: 0; width: 100%; height: 100%; overflow: hidden; z-index: -1; pointer-events: none; } .bubble { position: absolute; border-radius: 50%; background: rgba(231, 126, 118, 0.1); animation: float 8s ease-in-out infinite; } .bubble:nth-child(1) { width: 100px; height: 100px; top: 10%; left: 10%; animation-delay: 0s; background: rgba(167, 215, 197, 0.1); } .bubble:nth-child(2) { width: 50px; height: 50px; top: 60%; left: 80%; animation-delay: 1s; } .bubble:nth-child(3) { width: 70px; height: 70px; top: 40%; left: 20%; animation-delay: 2s; background: rgba(253, 231, 226, 0.2); } .bubble:nth-child(4) { width: 120px; height: 120px; top: 70%; left: 40%; animation-delay: 3s; background: rgba(167, 215, 197, 0.1); } .bubble:nth-child(5) { width: 60px; height: 60px; top: 20%; left: 85%; animation-delay: 4s; background: rgba(253, 231, 226, 0.2); } @keyframes float { 0% { transform: translateY(0) rotate(0deg); } 50% { transform: translateY(-20px) rotate(5deg); } 100% { transform: translateY(0) rotate(0deg); } } @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } .social-links { display: flex; justify-content: center; gap: 20px; margin-top: 30px; } .social-link { width: 42px; height: 42px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: white; box-shadow: 0 3px 10px rgba(0,0,0,0.1); transition: all 0.3s ease; position: relative; overflow: hidden; } .social-link:hover { transform: translateY(-3px); box-shadow: 0 5px 15px rgba(0,0,0,0.15); } .social-link::before { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(45deg, #e87e76, #a7d7c5); opacity: 0; transition: opacity 0.3s ease; z-index: 0; } .social-link:hover::before { opacity: 1; } .social-link svg { width: 20px; height: 20px; fill: #5a4a42; position: relative; z-index: 1; transition: fill 0.3s ease; } .social-link:hover svg { fill: white; } .search-container { position: relative; margin: 20px 0; } .search-input { width: 100%; padding: 12px 20px; font-family: 'Poppins', sans-serif; font-size: 0.9rem; border: none; border-radius: 25px; background: white; box-shadow: 0 3px 10px rgba(0,0,0,0.05); transition: all 0.3s ease; padding-right: 40px; } .search-input:focus { outline: none; box-shadow: 0 5px 15px rgba(0,0,0,0.1); } .search-button { position: absolute; right: 15px; top: 50%; transform: translateY(-50%); background: none; border: none; cursor: pointer; } .search-button svg { width: 18px; height: 18px; fill: #aaa; transition: fill 0.3s ease; } .search-button:hover svg { fill: #e87e76; } @media (max-width: 600px) { .blog-title { font-size: 2.5rem; } .tagline { font-size: 1.5rem; min-height: 45px; } .categories { gap: 10px; } .category { padding: 6px 15px; font-size: 0.8rem; } } </style> </head> <body> <div class="container"> <div class="bubbles"> <div class="bubble"></div> <div class="bubble"></div> <div class="bubble"></div> <div class="bubble"></div> <div class="bubble"></div> </div> <div class="header"> <h1 class="blog-title">Leah's Kitchen Chronicles</h1> <div class="tagline" id="tagline"></div> </div> <div class="search-container"> <input type="text" class="search-input" placeholder="Search for recipes..."> <button class="search-button"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> <path d="M500.3 443.7l-119.7-119.7c27.22-40.41 40.65-90.9 33.46-144.7C401.8 87.79 326.8 13.32 235.2 1.723C99.01-15.51-15.51 99.01 1.724 235.2c11.6 91.64 86.08 166.7 177.6 178.9c53.8 7.189 104.3-6.236 144.7-33.46l119.7 119.7c15.62 15.62 40.95 15.62 56.57 0C515.9 484.7 515.9 459.3 500.3 443.7zM79.1 208c0-70.58 57.42-128 128-128s128 57.42 128 128s-57.42 128-128 128S79.1 278.6 79.1 208z"/> </svg> </button> </div> <div class="categories"> <div class="category">Homemade Pasta</div> <div class="category">Seasonal Desserts</div> <div class="category">Farm to Table</div> <div class="category">Global Cuisine</div> <div class="category">Baking Tips</div> </div> <div class="recent-posts"> <div class="post"> <h3 class="post-title">My Secret to Perfect Sourdough Crust Every Time</h3> <p class="post-excerpt">After 3 years of trial and error, I've perfected my technique for that crackling crust and open crumb. Here's the method that changed everything...</p> <div class="post-meta"> <div class="post-date"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"> <path d="M152 64H296V24C296 10.75 306.7 0 320 0C333.3 0 344 10.75 344 24V64H384C419.3 64 448 92.65 448 128V448C448 483.3 419.3 512 384 512H64C28.65 512 0 483.3 0 448V128C0 92.65 28.65 64 64 64H104V24C104 10.75 114.7 0 128 0C141.3 0 152 10.75 152 24V64zM48 448C48 456.8 55.16 464 64 464H384C392.8 464 400 456.8 400 448V192H48V448z"/> </svg> May 18, 2023 </div> <div class="post-comments"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> <path d="M256 32C114.6 32 .0272 125.1 .0272 240c0 49.63 21.35 94.98 56.97 130.7c-12.5 50.37-54.27 95.27-54.77 95.77c-2.25 2.25-2.875 5.734-1.5 8.734C1.979 478.2 4.75 480 8 480c66.25 0 115.1-31.76 140.6-51.39C181.2 440.9 217.6 448 256 448c141.4 0 255.1-93.13 255.1-208S397.4 32 256 32z"/> </svg> 42 comments </div> </div> </div> <div class="post"> <h3 class="post-title">Foraging for Spring Ramps: My Woodland Adventure</h3> <p class="post-excerpt">Last weekend I ventured into the Catskills to forage for wild ramps. The experience was transformative and the pasta dish that followed was even better...</p> <div class="post-meta"> <div class="post-date"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"> <path d="M152 64H296V24C296 10.75 306.7 0 320 0C333.3 0 344 10.75 344 24V64H384C419.3 64 448 92.65 448 128V448C448 483.3 419.3 512 384 512H64C28.65 512 0 483.3 0 448V128C0 92.65 28.65 64 64 64H104V24C104 10.75 114.7 0 128 0C141.3 0 152 10.75 152 24V64zM48 448C48 456.8 55.16 464 64 464H384C392.8 464 400 456.8 400 448V192H48V448z"/> </svg> May 5, 2023 </div> <div class="post-comments"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> <path d="M256 32C114.6 32 .0272 125.1 .0272 240c0 49.63 21.35 94.98 56.97 130.7c-12.5 50.37-54.27 95.27-54.77 95.77c-2.25 2.25-2.875 5.734-1.5 8.734C1.979 478.2 4.75 480 8 480c66.25 0 115.1-31.76 140.6-51.39C181.2 440.9 217.6 448 256 448c141.4 0 255.1-93.13 255.1-208S397.4 32 256 32z"/> </svg> 29 comments </div> </div> </div> </div> <div class="social-links"> <a href="#" class="social-link"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> <path d="M504 256C504 119 393 8 256 8S8 119 8 256c0 123.78 90.69 226.38 209.25 245V327.69h-63V256h63v-54.64c0-62.15 37-96.48 93.67-96.48 27.14 0 55.52 4.84 55.52 4.84v61h-31.28c-30.8 0-40.41 19.12-40.41 38.73V256h68.78l-11 71.69h-57.78V501C413.31 482.38 504 379.78 504 256z"/> </svg> </a> <a href="#" class="social-link"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"> <path d="M224.1 141c-63.6 0-114.9 51.3-114.9 114.9s51.3 114.9 114.9 114.9S339 319.5 339 255.9 287.7 141 224.1 141zm0 189.6c-41.1 0-74.7-33.5-74.7-74.7s33.5-74.7 74.7-74.7 74.7 33.5 74.7 74.7-33.6 74.7-74.7 74.7zm146.4-194.3c0 14.9-12 26.8-26.8 26.8-14.9 0-26.8-12-26.8-26.8s12-26.8 26.8-26.8 26.8 12 26.8 26.8zm76.1 27.2c-1.7-35.9-9.9-67.7-36.2-93.9-26.2-26.2-58-34.4-93.9-36.2-37-2.1-147.9-2.1-184.9 0-35.8 1.7-67.6 9.9-93.9 36.1s-34.4 58-36.2 93.9c-2.1 37-2.1 147.9 0 184.9 1.7 35.9 9.9 67.7 36.2 93.9s58 34.4 93.9 36.2c37 2.1 147.9 2.1 184.9 0 35.9-1.7 67.7-9.9 93.9-36.2 26.2-26.2 34.4-58 36.2-93.9 2.1-37 2.1-147.8 0-184.8zM398.8 388c-7.8 19.6-22.9 34.7-42.6 42.6-29.5 11.7-99.5 9-132.1 9s-102.7 2.6-132.1-9c-19.6-7.8-34.7-22.9-42.6-42.6-11.7-29.5-9-99.5-9-132.1s-2.6-102.7 9-132.1c7.8-19.6 22.9-34.7 42.6-42.6 29.5-11.7 99.5-9 132.1-9s102.7-2.6 132.1 9c19.6 7.8 34.7 22.9 42.6 42.6 11.7 29.5 9 99.5 9 132.1s2.7 102.7-9 132.1z"/> </svg> </a> <a href="#" class="social-link"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"> <path d="M496 256c0 137-111 248-248 248-25.6 0-50.2-3.9-73.4-11.1 10.1-16.5 25.2-43.5 30.8-65 3-11.6 15.4-59 15.4-59 8.1 15.4 31.7 28.5 56.8 28.5 74.8 0 128.7-68.8 128.7-154.3 0-81.9-66.9-143.2-152.9-143.2-107 0-163.9 71.8-163.9 150.1 0 36.4 19.4 81.7 50.3 96.1 4.7 2.2 7.2 1.2 8.3-3.3.8-3.4 5-20.3 6.9-28.1.6-2.5.3-4.7-1.7-7.1-10.1-12.5-18.3-35.3-18.3-56.6 0-54.7 41.4-107.6 112-107.6 60.9 0 103.6 41.5 103.6 100.9 0 67.1-33.9 113.6-78 113.6-24.3 0-42.6-20.1-36.7-44.8 7-29.5 20.5-61.3 20.5-82.6 0-19-10.2-34.9-31.4-34.9-24.9 0-44.9 25.7-44.9 60.2 0 22 7.4 36.8 7.4 36.8s-24.5 103.8-29 123.2c-5 21.4-3 51.6-.9 71.2C65.4 450.9 0 361.1 0 256 0 119 111 8 248 8s248 111 248 248z"/> </svg> </a> <a href="#" class="social-link"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> <path d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"/> </svg> </a> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { const taglineElement = document.getElementById('tagline'); const taglineText = "Exploring flavors, one home-cooked story at a time..."; let index = 0; function typeTagline() { if (index < taglineText.length) { taglineElement.textContent += taglineText.charAt(index); index++; setTimeout(typeTagline, Math.random() * 100 + 50); // Random typing speed for a more human feel } } // Start typing animation setTimeout(typeTagline, 1000); // Add hover effect for search input const searchInput = document.querySelector('.search-input'); searchInput.addEventListener('focus', function() { this.style.boxShadow = '0 5px 15px rgba(232, 126, 118, 0.2)'; this.style.borderColor = '#e87e76'; }); searchInput.addEventListener('blur', function() { this.style.boxShadow = '0 3px 10px rgba(0,0,0,0.05)'; this.style.borderColor = 'transparent'; }); // Prevent form submission on search const searchButton = document.querySelector('.search-button'); searchButton.addEventListener('click', function(e) { e.preventDefault(); // Add a subtle animation to acknowledge the click const searchContainer = document.querySelector('.search-container'); searchContainer.style.transform = 'scale(0.98)'; setTimeout(() => { searchContainer.style.transform = 'scale(1)'; }, 150); }); // Make categories interactive const categories = document.querySelectorAll('.category'); categories.forEach(category => { category.addEventListener('click', function() { // Remove active class from all categories categories.forEach(cat => cat.classList.remove('active')); // Add active class to clicked category this.classList.add('active'); // Visual feedback this.style.backgroundColor = '#e87e76'; this.style.color = 'white'; // Reset after a delay setTimeout(() => { this.style.backgroundColor = ''; this.style.color = ''; }, 500); }); }); // Add click effect to posts const posts = document.querySelectorAll('.post'); posts.forEach(post => { post.addEventListener('click', function() { this.style.transform = 'translateY(0) scale(0.98)'; setTimeout(() => { this.style.transform = ''; }, 150); }); }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>TechSummit 2024 Countdown</title> <style> :root { --primary-color: #0B0F1A; --accent-color: #00E5FF; --text-color: #FFFFFF; --secondary-color: #FF2D55; --tertiary-color: #5933EA; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Inter', 'SF Pro Display', -apple-system, BlinkMacSystemFont, sans-serif; background-color: var(--primary-color); color: var(--text-color); display: flex; justify-content: center; align-items: center; min-height: 100vh; overflow-x: hidden; line-height: 1.6; } .container { width: 100%; max-width: 700px; padding: 2rem; position: relative; } .countdown-container { position: relative; z-index: 2; } .event-title { font-size: clamp(1.5rem, 5vw, 3.2rem); font-weight: 800; margin-bottom: 1rem; background: linear-gradient(120deg, var(--accent-color), var(--tertiary-color)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; letter-spacing: -0.03em; } .event-subtitle { font-size: clamp(1rem, 3vw, 1.4rem); font-weight: 500; margin-bottom: 2rem; opacity: 0.85; } .typing-effect { height: 4rem; font-size: clamp(1.1rem, 4vw, 1.8rem); font-weight: 600; padding: 1rem 0; position: relative; margin-bottom: 1.5rem; display: flex; align-items: center; } .typing-cursor { display: inline-block; width: 3px; height: 1.8rem; background-color: var(--accent-color); margin-left: 4px; animation: blink 1s infinite; } .countdown-digits { font-variant-numeric: tabular-nums; display: flex; justify-content: space-between; margin-top: 2rem; margin-bottom: 2rem; flex-wrap: wrap; } .countdown-unit { display: flex; flex-direction: column; align-items: center; width: 22%; min-width: 80px; position: relative; transition: transform 0.3s ease; } .countdown-unit:hover { transform: translateY(-5px); } .countdown-unit::after { content: ''; position: absolute; bottom: -10px; width: 0; height: 2px; background-color: var(--accent-color); transition: width 0.3s ease; } .countdown-unit:hover::after { width: 100%; } .digit-container { position: relative; width: 100%; height: 80px; overflow: hidden; border-radius: 8px; background: rgba(255, 255, 255, 0.05); box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px); border: 1px solid rgba(255, 255, 255, 0.1); margin-bottom: 10px; display: flex; justify-content: center; align-items: center; } .digit { font-size: clamp(2rem, 8vw, 3.2rem); font-weight: 700; color: var(--accent-color); } .unit-label { font-size: 0.9rem; font-weight: 500; text-transform: uppercase; letter-spacing: 0.1em; opacity: 0.7; } .cta-button { display: inline-block; background: linear-gradient(90deg, var(--accent-color), var(--tertiary-color)); color: var(--primary-color); font-weight: 600; padding: 0.8rem 2rem; border-radius: 50px; text-decoration: none; font-size: 1rem; margin-top: 1rem; cursor: pointer; position: relative; overflow: hidden; transition: all 0.3s ease; border: none; text-align: center; } .cta-button::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(90deg, var(--tertiary-color), var(--accent-color)); opacity: 0; transition: opacity 0.3s ease; } .cta-button:hover::before { opacity: 1; } .cta-button span { position: relative; z-index: 1; } .pulse { animation: pulse 2s infinite; } .urgency-indicator { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background-color: var(--secondary-color); margin-right: 8px; position: relative; } .urgency-indicator::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; border-radius: 50%; background-color: var(--secondary-color); animation: ripple 1.5s linear infinite; } .background-grid { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-image: linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px); background-size: 20px 20px; z-index: 1; pointer-events: none; } .background-accent { position: absolute; top: 20%; right: -150px; width: 300px; height: 300px; border-radius: 50%; background: radial-gradient(circle, var(--tertiary-color) 0%, rgba(89, 51, 234, 0) 70%); filter: blur(80px); opacity: 0.4; z-index: 0; pointer-events: none; } .background-accent-2 { position: absolute; bottom: -100px; left: -100px; width: 250px; height: 250px; border-radius: 50%; background: radial-gradient(circle, var(--accent-color) 0%, rgba(0, 229, 255, 0) 70%); filter: blur(60px); opacity: 0.3; z-index: 0; pointer-events: none; } .time-meta { font-size: 0.85rem; opacity: 0.6; margin-top: 1.5rem; display: flex; align-items: center; justify-content: space-between; } .timezone { display: flex; align-items: center; gap: 4px; } .timezone-icon { width: 16px; height: 16px; } .register-section { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(255, 255, 255, 0.1); } @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.03); } 100% { transform: scale(1); } } @keyframes ripple { 0% { transform: scale(1); opacity: 0.8; } 100% { transform: scale(3); opacity: 0; } } @media (max-width: 500px) { .countdown-digits { justify-content: center; gap: 10px; } .countdown-unit { width: 45%; margin-bottom: 10px; } .typing-effect { height: 6rem; } } </style> </head> <body> <div class="container"> <div class="background-grid"></div> <div class="background-accent"></div> <div class="background-accent-2"></div> <div class="countdown-container"> <h1 class="event-title">TechSummit 2024</h1> <p class="event-subtitle">Breakthrough Innovation Conference · San Francisco</p> <div class="typing-effect"> <span id="typing-text"></span> <span class="typing-cursor"></span> </div> <div class="countdown-digits"> <div class="countdown-unit"> <div class="digit-container"> <div class="digit" id="days">00</div> </div> <div class="unit-label">Days</div> </div> <div class="countdown-unit"> <div class="digit-container"> <div class="digit" id="hours">00</div> </div> <div class="unit-label">Hours</div> </div> <div class="countdown-unit"> <div class="digit-container"> <div class="digit" id="minutes">00</div> </div> <div class="unit-label">Minutes</div> </div> <div class="countdown-unit"> <div class="digit-container"> <div class="digit" id="seconds">00</div> </div> <div class="unit-label">Seconds</div> </div> </div> <div class="time-meta"> <div class="timezone"> <svg class="timezone-icon" 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> <line x1="12" y1="8" x2="12" y2="12"></line> <line x1="12" y1="12" x2="14.5" y2="14.5"></line> </svg> <span>Your local time</span> </div> <div id="date-display">June 4-6, 2024</div> </div> <div class="register-section"> <div class="urgency-indicator"></div> <span id="seats-left">Only 47 seats remaining for early access</span> <button class="cta-button pulse"> <span>Secure Your Spot</span> </button> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Set the date we're counting down to const countDownDate = new Date("June 4, 2024 09:00:00").getTime(); // Typing effect messages const messages = [ "The future of tech begins in", "Be ready to transform in", "Your innovation journey starts in", "Join tech pioneers in just", "Breakthrough technologies unveil in" ]; let currentMessageIndex = 0; let typingSpeed = 50; // milliseconds per character let deleteSpeed = 30; // milliseconds per character let pauseBetweenMessages = 2000; // milliseconds const typingTextElement = document.getElementById('typing-text'); function typeWriter(text, currentIndex = 0) { if (currentIndex < text.length) { typingTextElement.textContent += text.charAt(currentIndex); setTimeout(() => typeWriter(text, currentIndex + 1), typingSpeed); } else { // Pause before deleting setTimeout(deleteText, pauseBetweenMessages); } } function deleteText() { const text = typingTextElement.textContent; if (text.length > 0) { typingTextElement.textContent = text.substring(0, text.length - 1); setTimeout(deleteText, deleteSpeed); } else { // Move to next message currentMessageIndex = (currentMessageIndex + 1) % messages.length; setTimeout(() => typeWriter(messages[currentMessageIndex]), 500); } } // Start the typing effect typeWriter(messages[currentMessageIndex]); // Update the countdown every 1 second const x = setInterval(function() { // Get today's date and time const now = new Date().getTime(); // Find the distance between now and the count down date const distance = countDownDate - now; if (distance < 0) { clearInterval(x); document.getElementById("days").innerHTML = "00"; document.getElementById("hours").innerHTML = "00"; document.getElementById("minutes").innerHTML = "00"; document.getElementById("seconds").innerHTML = "00"; return; } // Time calculations for days, hours, minutes and seconds const days = Math.floor(distance / (1000 * 60 * 60 * 24)); const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)); const seconds = Math.floor((distance % (1000 * 60)) / 1000); // Display the result with leading zeros document.getElementById("days").innerHTML = days < 10 ? "0" + days : days; document.getElementById("hours").innerHTML = hours < 10 ? "0" + hours : hours; document.getElementById("minutes").innerHTML = minutes < 10 ? "0" + minutes : minutes; document.getElementById("seconds").innerHTML = seconds < 10 ? "0" + seconds : seconds; // Dynamically update urgency based on remaining time updateUrgencyDisplay(distance); }, 1000); // Update urgency display based on time remaining function updateUrgencyDisplay(timeRemaining) { const daysRemaining = Math.floor(timeRemaining / (1000 * 60 * 60 * 24)); const seatsElement = document.getElementById('seats-left'); if (daysRemaining <= 7) { seatsElement.innerHTML = "Critical: Only 12 seats remaining"; seatsElement.style.color = 'var(--secondary-color)'; } else if (daysRemaining <= 14) { seatsElement.innerHTML = "Urgent: Only 27 seats remaining"; seatsElement.style.color = '#FF9500'; } else if (daysRemaining <= 30) { seatsElement.innerHTML = "Only 47 seats remaining for early access"; } } // Add animation to the CTA button on click const ctaButton = document.querySelector('.cta-button'); ctaButton.addEventListener('click', function() { this.classList.remove('pulse'); void this.offsetWidth; // Trigger reflow this.classList.add('pulse'); // Show a "registered" message instead of actually submitting setTimeout(() => { const originalText = this.innerHTML; this.innerHTML = '<span>✓ Interest Registered</span>'; this.style.background = 'linear-gradient(90deg, #00C853, #69F0AE)'; // Revert after 2 seconds setTimeout(() => { this.innerHTML = originalText; this.style.background = ''; }, 2000); }, 300); }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Professional Resume with Typing Animation</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } :root { --primary-color: #2c3e50; --accent-color: #e74c3c; --light-color: #ecf0f1; --dark-color: #2c3e50; --serif-font: 'Playfair Display', Georgia, serif; --sans-font: 'Montserrat', Helvetica, Arial, sans-serif; } @import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600&family=Playfair+Display:ital,wght@0,400;0,600;1,400&display=swap'); body { font-family: var(--sans-font); line-height: 1.6; color: var(--dark-color); background-color: var(--light-color); padding: 20px; display: flex; justify-content: center; align-items: center; min-height: 700px; overflow-x: hidden; } .resume-container { max-width: 700px; width: 100%; background: #fff; box-shadow: 0 8px 30px rgba(0, 0, 0, 0.08); padding: 2.5rem; border-radius: 4px; position: relative; overflow: hidden; } .resume-container::before { content: ""; position: absolute; top: 0; left: 0; width: 4px; height: 100%; background: var(--accent-color); } .resume-header { margin-bottom: 2rem; position: relative; } .resume-name { font-family: var(--serif-font); font-size: 2.2rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--primary-color); opacity: 0; transform: translateY(10px); animation: fadeUp 0.8s ease forwards 0.2s; } .resume-title { font-size: 1.1rem; color: var(--accent-color); letter-spacing: 1px; font-weight: 500; margin-bottom: 1rem; opacity: 0; transform: translateY(10px); animation: fadeUp 0.8s ease forwards 0.4s; } .resume-contact { display: flex; flex-wrap: wrap; gap: 1rem; font-size: 0.9rem; margin-bottom: 1.5rem; opacity: 0; transform: translateY(10px); animation: fadeUp 0.8s ease forwards 0.6s; } .resume-contact span { display: flex; align-items: center; } .resume-contact svg { width: 16px; height: 16px; margin-right: 0.4rem; } .section-title { font-family: var(--serif-font); font-size: 1.4rem; color: var(--primary-color); margin: 1.5rem 0 0.75rem; padding-bottom: 0.5rem; border-bottom: 1px solid rgba(0, 0, 0, 0.08); position: relative; opacity: 0; transform: translateY(10px); animation: fadeUp 0.8s ease forwards 0.8s; } .section-title::after { content: ""; position: absolute; bottom: -1px; left: 0; width: 80px; height: 3px; background: var(--accent-color); } .skill-typing-container { min-height: 120px; margin: 1.5rem 0; position: relative; } .skill-category { font-family: var(--serif-font); font-weight: 600; font-style: italic; color: var(--primary-color); margin-bottom: 0.75rem; font-size: 1.1rem; } .typing-text { display: inline-block; position: relative; min-height: 1.5em; font-size: 1.1rem; color: var(--dark-color); } .typing-text::after { content: '|'; position: absolute; right: -5px; animation: blink 0.8s infinite; } .skill-item { margin-bottom: 2rem; opacity: 0; transform: translateY(10px); animation: fadeUp 0.8s ease forwards calc(1s + (var(--i) * 0.2s)); } .skill-tags { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.75rem; } .skill-tag { background: rgba(231, 76, 60, 0.1); color: var(--accent-color); padding: 0.3rem 0.8rem; border-radius: 2rem; font-size: 0.85rem; transition: all 0.3s ease; } .skill-tag:hover { background: var(--accent-color); color: white; transform: translateY(-2px); box-shadow: 0 4px 8px rgba(231, 76, 60, 0.2); } .experience-container { margin-top: 1rem; } .experience-item { padding-left: 1.5rem; position: relative; margin-bottom: 1.5rem; opacity: 0; transform: translateY(10px); animation: fadeUp 0.8s ease forwards calc(1.6s + (var(--i) * 0.2s)); } .experience-item::before { content: ""; position: absolute; left: 0; top: 0.5rem; width: 8px; height: 8px; border-radius: 50%; background: var(--accent-color); } .experience-title { font-weight: 600; margin-bottom: 0.25rem; color: var(--primary-color); } .experience-company { font-style: italic; color: var(--accent-color); margin-bottom: 0.25rem; } .experience-date { font-size: 0.85rem; color: #888; margin-bottom: 0.5rem; } .download-button { display: inline-flex; align-items: center; gap: 0.5rem; background: var(--primary-color); color: white; padding: 0.5rem 1.25rem; border-radius: 3px; font-weight: 500; margin-top: 1.5rem; text-decoration: none; transition: all 0.3s ease; opacity: 0; transform: translateY(10px); animation: fadeUp 0.8s ease forwards 2.4s; } .download-button:hover { background: var(--accent-color); transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); } @keyframes fadeUp { to { opacity: 1; transform: translateY(0); } } @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } @media (max-width: 600px) { .resume-container { padding: 1.5rem; } .resume-name { font-size: 1.8rem; } .resume-title { font-size: 1rem; } .section-title { font-size: 1.2rem; } .skill-category { font-size: 1rem; } .typing-text { font-size: 1rem; } } </style> </head> <body> <div class="resume-container"> <div class="resume-header"> <h1 class="resume-name">Alexandra Richards</h1> <div class="resume-title">Senior UX Designer & Frontend Developer</div> <div class="resume-contact"> <span> <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="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path> <polyline points="22,6 12,13 2,6"></polyline> </svg> [email protected] </span> <span> <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="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"></path> </svg> (415) 555-0127 </span> <span> <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="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path> <polyline points="9 22 9 12 15 12 15 22"></polyline> </svg> San Francisco, CA </span> </div> </div> <h2 class="section-title">Core Competencies</h2> <div class="skill-typing-container"> <div id="typing-container"></div> </div> <h2 class="section-title">Professional Skills</h2> <div class="skill-item" style="--i: 0"> <div class="skill-category">Design Expertise</div> <div class="skill-tags"> <span class="skill-tag">UI/UX Design</span> <span class="skill-tag">Wireframing</span> <span class="skill-tag">Prototyping</span> <span class="skill-tag">User Research</span> <span class="skill-tag">Accessibility</span> </div> </div> <div class="skill-item" style="--i: 1"> <div class="skill-category">Technical Proficiency</div> <div class="skill-tags"> <span class="skill-tag">HTML5</span> <span class="skill-tag">CSS3/SASS</span> <span class="skill-tag">JavaScript</span> <span class="skill-tag">React</span> <span class="skill-tag">Responsive Design</span> <span class="skill-tag">Version Control</span> </div> </div> <div class="skill-item" style="--i: 2"> <div class="skill-category">Tools & Frameworks</div> <div class="skill-tags"> <span class="skill-tag">Figma</span> <span class="skill-tag">Adobe XD</span> <span class="skill-tag">Sketch</span> <span class="skill-tag">InVision</span> <span class="skill-tag">Material UI</span> <span class="skill-tag">TailwindCSS</span> </div> </div> <h2 class="section-title">Experience Highlights</h2> <div class="experience-container"> <div class="experience-item" style="--i: 0"> <div class="experience-title">Senior UI/UX Designer</div> <div class="experience-company">Innovate Tech Solutions</div> <div class="experience-date">Jan 2020 - Present</div> <p>Led redesign of customer-facing SaaS platform, increasing user retention by 37% and reducing onboarding friction by implementing intuitive interface patterns.</p> </div> <div class="experience-item" style="--i: 1"> <div class="experience-title">Frontend Developer & UX Specialist</div> <div class="experience-company">Digital Growth Agency</div> <div class="experience-date">Mar 2017 - Dec 2019</div> <p>Developed responsive interfaces for 20+ clients across fintech and healthcare sectors, employing a user-centered design approach to improve conversion rates by an average of 24%.</p> </div> </div> <a href="#" class="download-button"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path> <polyline points="7 10 12 15 17 10"></polyline> <line x1="12" y1="15" x2="12" y2="3"></line> </svg> Download Full Resume </a> </div> <script> document.addEventListener('DOMContentLoaded', function() { const typingContainer = document.getElementById('typing-container'); // Define the professional attributes to type out const attributes = [ { category: "Problem Solving", text: "Translating complex requirements into intuitive user experiences through iterative design thinking and collaborative approaches." }, { category: "Communication", text: "Articulating design decisions to stakeholders, building consensus across cross-functional teams to deliver cohesive products." }, { category: "Technical Acumen", text: "Bridging design and development with frontend expertise, ensuring pixel-perfect implementation of design systems." } ]; let currentAttributeIndex = 0; let isTypingCategory = true; let charIndex = 0; let currentText = ''; let typingSpeed = 40; // milliseconds per character let categoryPause = 1000; // pause after category is typed let attributePause = 2000; // pause after full attribute is typed // Create elements for the typing animation const createTypingElements = () => { typingContainer.innerHTML = ''; const skillItem = document.createElement('div'); skillItem.classList.add('skill-item'); const categoryElement = document.createElement('div'); categoryElement.classList.add('skill-category'); const typingElement = document.createElement('div'); typingElement.classList.add('typing-text'); skillItem.appendChild(categoryElement); skillItem.appendChild(typingElement); typingContainer.appendChild(skillItem); return { categoryElement, typingElement }; }; const elements = createTypingElements(); // Main typing function const typeText = () => { const currentAttribute = attributes[currentAttributeIndex]; if (isTypingCategory) { currentText = currentAttribute.category + ": "; if (charIndex < currentText.length) { elements.categoryElement.textContent = currentText.substring(0, charIndex + 1); charIndex++; setTimeout(typeText, typingSpeed); } else { isTypingCategory = false; charIndex = 0; setTimeout(typeText, categoryPause); } } else { currentText = currentAttribute.text; if (charIndex < currentText.length) { elements.typingElement.textContent = currentText.substring(0, charIndex + 1); charIndex++; setTimeout(typeText, typingSpeed); } else { // Move to next attribute after pause setTimeout(() => { // Fade out current content elements.categoryElement.style.opacity = 0; elements.typingElement.style.opacity = 0; setTimeout(() => { currentAttributeIndex = (currentAttributeIndex + 1) % attributes.length; isTypingCategory = true; charIndex = 0; // Create new elements for next attribute const newElements = createTypingElements(); elements.categoryElement = newElements.categoryElement; elements.typingElement = newElements.typingElement; // Continue typing typeText(); }, 500); // Time for fade out }, attributePause); } } }; // Start the typing animation setTimeout(typeText, 1000); }); </script> </body> </html>