Created
January 24, 2026 21:48
-
-
Save KrishnanSriram/71b59374c6847eaf7fe1e356d4eaa3eb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>My Simple SPA</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; | |
| line-height: 1.6; | |
| color: #333; | |
| } | |
| header { | |
| background: #4a90e2; | |
| color: white; | |
| padding: 1rem 2rem; | |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); | |
| } | |
| nav { | |
| display: flex; | |
| gap: 2rem; | |
| margin-top: 1rem; | |
| } | |
| nav a { | |
| color: white; | |
| text-decoration: none; | |
| padding: 0.5rem 1rem; | |
| border-radius: 4px; | |
| transition: background 0.3s; | |
| } | |
| nav a:hover { | |
| background: rgba(255, 255, 255, 0.2); | |
| } | |
| nav a.active { | |
| background: rgba(255, 255, 255, 0.3); | |
| font-weight: bold; | |
| } | |
| main { | |
| max-width: 1200px; | |
| margin: 2rem auto; | |
| padding: 0 2rem; | |
| } | |
| .view { | |
| display: none; | |
| animation: fadeIn 0.3s; | |
| } | |
| .view.active { | |
| display: block; | |
| } | |
| @keyframes fadeIn { | |
| from { | |
| opacity: 0; | |
| transform: translateY(10px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| .card { | |
| background: white; | |
| padding: 2rem; | |
| border-radius: 8px; | |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); | |
| margin-bottom: 2rem; | |
| } | |
| h1, | |
| h2 { | |
| margin-bottom: 1rem; | |
| color: #2c3e50; | |
| } | |
| .feature-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); | |
| gap: 1.5rem; | |
| margin-top: 2rem; | |
| } | |
| .feature-card { | |
| background: #f8f9fa; | |
| padding: 1.5rem; | |
| border-radius: 8px; | |
| border-left: 4px solid #4a90e2; | |
| } | |
| .feature-card h3 { | |
| color: #4a90e2; | |
| margin-bottom: 0.5rem; | |
| } | |
| footer { | |
| text-align: center; | |
| padding: 2rem; | |
| background: #f8f9fa; | |
| margin-top: 4rem; | |
| color: #666; | |
| } | |
| form { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 1rem; | |
| max-width: 500px; | |
| } | |
| input, | |
| textarea { | |
| padding: 0.75rem; | |
| border: 1px solid #ddd; | |
| border-radius: 4px; | |
| font-family: inherit; | |
| } | |
| button { | |
| background: #4a90e2; | |
| color: white; | |
| padding: 0.75rem 1.5rem; | |
| border: none; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| font-size: 1rem; | |
| transition: background 0.3s; | |
| } | |
| button:hover { | |
| background: #357abd; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <h1>My Simple SPA</h1> | |
| <nav id="nav"> | |
| <a href="#home" class="nav-link active">Home</a> | |
| <a href="#about" class="nav-link">About</a> | |
| <a href="#contact" class="nav-link">Contact</a> | |
| </nav> | |
| </header> | |
| <main> | |
| <!-- Home View --> | |
| <div id="home" class="view active"> | |
| <div class="card"> | |
| <h2>Welcome to My Single Page Application</h2> | |
| <p>This is a simple SPA built with vanilla HTML, CSS, and JavaScript. No frameworks required!</p> | |
| <div class="feature-grid"> | |
| <div class="feature-card"> | |
| <h3>⚡ Fast</h3> | |
| <p>No page reloads, instant navigation between views</p> | |
| </div> | |
| <div class="feature-card"> | |
| <h3>📱 Responsive</h3> | |
| <p>Works seamlessly on desktop, tablet, and mobile devices</p> | |
| </div> | |
| <div class="feature-card"> | |
| <h3>🎨 Modern</h3> | |
| <p>Clean design with smooth animations and transitions</p> | |
| </div> | |
| <div class="feature-card"> | |
| <h3>🚀 Simple</h3> | |
| <p>Easy to deploy as a static website anywhere</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- About View --> | |
| <div id="about" class="view"> | |
| <div class="card"> | |
| <h2>About This Project</h2> | |
| <p>This SPA demonstrates the core concepts of single-page applications:</p> | |
| <ul style="margin-left: 2rem; margin-top: 1rem; line-height: 2;"> | |
| <li>Client-side routing using URL hash</li> | |
| <li>Dynamic content switching without page reloads</li> | |
| <li>Smooth transitions between views</li> | |
| <li>Responsive design</li> | |
| </ul> | |
| <h3 style="margin-top: 2rem; margin-bottom: 1rem;">Technologies Used</h3> | |
| <p>This project uses only vanilla web technologies - HTML5, CSS3, and JavaScript (ES6+). No dependencies or | |
| build tools required!</p> | |
| </div> | |
| </div> | |
| <!-- Contact View --> | |
| <div id="contact" class="view"> | |
| <div class="card"> | |
| <h2>Contact Us</h2> | |
| <p>Have questions? Send us a message!</p> | |
| <form id="contactForm" style="margin-top: 2rem;"> | |
| <input type="text" id="name" placeholder="Your Name" required> | |
| <input type="email" id="email" placeholder="Your Email" required> | |
| <textarea id="message" rows="5" placeholder="Your Message" required></textarea> | |
| <button type="submit">Send Message</button> | |
| </form> | |
| <div id="formMessage" style="margin-top: 1rem; padding: 1rem; border-radius: 4px; display: none;"></div> | |
| </div> | |
| </div> | |
| </main> | |
| <footer> | |
| <p>© 2026 My Simple SPA. Built with ❤️ using vanilla JavaScript.</p> | |
| </footer> | |
| <script> | |
| // Router functionality | |
| class Router { | |
| constructor() { | |
| this.routes = {}; | |
| this.currentRoute = null; | |
| // Listen for hash changes | |
| window.addEventListener('hashchange', () => this.handleRoute()); | |
| // Handle initial load | |
| this.handleRoute(); | |
| } | |
| addRoute(path, handler) { | |
| this.routes[path] = handler; | |
| } | |
| handleRoute() { | |
| const hash = window.location.hash.slice(1) || 'home'; | |
| // Hide all views | |
| document.querySelectorAll('.view').forEach(view => { | |
| view.classList.remove('active'); | |
| }); | |
| // Remove active class from all nav links | |
| document.querySelectorAll('.nav-link').forEach(link => { | |
| link.classList.remove('active'); | |
| }); | |
| // Show current view | |
| const view = document.getElementById(hash); | |
| if (view) { | |
| view.classList.add('active'); | |
| this.currentRoute = hash; | |
| } | |
| // Add active class to current nav link | |
| const activeLink = document.querySelector(`a[href="#${hash}"]`); | |
| if (activeLink) { | |
| activeLink.classList.add('active'); | |
| } | |
| // Execute route handler if exists | |
| if (this.routes[hash]) { | |
| this.routes[hash](); | |
| } | |
| } | |
| } | |
| // Initialize router | |
| const router = new Router(); | |
| // Add route handlers | |
| router.addRoute('home', () => { | |
| console.log('Home page loaded'); | |
| }); | |
| router.addRoute('about', () => { | |
| console.log('About page loaded'); | |
| }); | |
| router.addRoute('contact', () => { | |
| console.log('Contact page loaded'); | |
| }); | |
| // Handle form submission | |
| document.getElementById('contactForm').addEventListener('submit', (e) => { | |
| e.preventDefault(); | |
| const name = document.getElementById('name').value; | |
| const email = document.getElementById('email').value; | |
| const message = document.getElementById('message').value; | |
| // Show success message | |
| const formMessage = document.getElementById('formMessage'); | |
| formMessage.style.display = 'block'; | |
| formMessage.style.background = '#d4edda'; | |
| formMessage.style.color = '#155724'; | |
| formMessage.style.border = '1px solid #c3e6cb'; | |
| formMessage.textContent = `Thank you, ${name}! Your message has been received. We'll get back to you at ${email} soon.`; | |
| // Reset form | |
| e.target.reset(); | |
| // Hide message after 5 seconds | |
| setTimeout(() => { | |
| formMessage.style.display = 'none'; | |
| }, 5000); | |
| }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment