Spaces:
Running
Running
| class CustomProjects extends HTMLElement { | |
| connectedCallback() { | |
| this.attachShadow({ mode: 'open' }); | |
| this.shadowRoot.innerHTML = ` | |
| <style> | |
| :host { | |
| display: block; | |
| padding: 6rem 1rem; | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| } | |
| .section-header { | |
| text-align: center; | |
| margin-bottom: 4rem; | |
| } | |
| h2 { | |
| font-size: 2.5rem; | |
| font-weight: 700; | |
| color: white; | |
| margin-bottom: 0.5rem; | |
| } | |
| .subtitle { | |
| color: #9ca3af; | |
| font-family: 'Fira Code', monospace; | |
| } | |
| .projects-wrapper { | |
| position: relative; | |
| } | |
| .projects-scroll { | |
| display: flex; | |
| gap: 2rem; | |
| overflow-x: auto; | |
| scroll-snap-type: x mandatory; | |
| padding: 1rem; | |
| scroll-behavior: smooth; | |
| -ms-overflow-style: none; | |
| scrollbar-width: none; | |
| } | |
| .projects-scroll::-webkit-scrollbar { | |
| display: none; | |
| } | |
| .project-container { | |
| background: #171717; | |
| border: 1px solid #262626; | |
| border-radius: 1rem; | |
| overflow: hidden; | |
| display: grid; | |
| grid-template-columns: 1fr; | |
| min-width: 100%; | |
| transition: border-color 0.3s ease; | |
| scroll-snap-align: center; | |
| } | |
| @media (min-width: 768px) { | |
| .project-container { | |
| grid-template-columns: 1.2fr 0.8fr; | |
| min-width: calc(50% - 1rem); | |
| } | |
| } | |
| .project-container { | |
| transform-style: preserve-3d; | |
| transition: all 0.5s ease; | |
| } | |
| .project-container:hover { | |
| border-color: #EAB308; | |
| transform: translateY(-10px) rotateX(5deg); | |
| } | |
| .project-image { | |
| width: 100%; | |
| height: 300px; | |
| object-fit: cover; | |
| border-bottom: 1px solid #262626; | |
| } | |
| @media (min-width: 768px) { | |
| .project-image { | |
| height: 100%; | |
| border-bottom: none; | |
| border-right: 1px solid #262626; | |
| } | |
| } | |
| .project-content { | |
| padding: 2.5rem; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| } | |
| .folder-icon { | |
| color: #EAB308; | |
| margin-bottom: 1rem; | |
| } | |
| h3 { | |
| font-size: 1.75rem; | |
| color: white; | |
| margin-bottom: 1rem; | |
| font-weight: 700; | |
| } | |
| p { | |
| color: #a1a1aa; | |
| margin-bottom: 2rem; | |
| line-height: 1.6; | |
| } | |
| .tech-stack { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 0.75rem; | |
| margin-bottom: 2rem; | |
| font-family: 'Fira Code', monospace; | |
| font-size: 0.8rem; | |
| } | |
| .tech-item { | |
| color: #EAB308; | |
| } | |
| .project-links { | |
| display: flex; | |
| gap: 1.5rem; | |
| } | |
| .link-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| color: #e5e7eb; | |
| text-decoration: none; | |
| font-size: 0.9rem; | |
| transition: color 0.3s; | |
| } | |
| .link-item:hover { | |
| color: #EAB308; | |
| } | |
| .link-item svg { | |
| width: 18px; | |
| height: 18px; | |
| transition: transform 0.3s ease; | |
| } | |
| .link-item:hover svg { | |
| transform: translateX(5px); | |
| } | |
| .project-tags { | |
| display: flex; | |
| gap: 0.5rem; | |
| margin-bottom: 1rem; | |
| } | |
| .tag { | |
| background: rgba(234, 179, 8, 0.1); | |
| color: #EAB308; | |
| padding: 0.25rem 0.75rem; | |
| border-radius: 9999px; | |
| font-size: 0.75rem; | |
| font-family: 'Fira Code', monospace; | |
| } | |
| .nav-btn { | |
| position: absolute; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| width: 50px; | |
| height: 50px; | |
| background: #171717; | |
| border: 1px solid #262626; | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| color: #EAB308; | |
| transition: all 0.3s ease; | |
| z-index: 10; | |
| } | |
| .nav-btn:hover { | |
| background: #EAB308; | |
| color: black; | |
| border-color: #EAB308; | |
| } | |
| .nav-btn.prev { | |
| left: -25px; | |
| } | |
| .nav-btn.next { | |
| right: -25px; | |
| } | |
| .nav-btn svg { | |
| width: 24px; | |
| height: 24px; | |
| } | |
| @media (max-width: 768px) { | |
| .nav-btn { | |
| width: 40px; | |
| height: 40px; | |
| } | |
| .nav-btn.prev { | |
| left: 10px; | |
| } | |
| .nav-btn.next { | |
| right: 10px; | |
| } | |
| } | |
| .dots-container { | |
| display: flex; | |
| justify-content: center; | |
| gap: 0.5rem; | |
| margin-top: 2rem; | |
| } | |
| .dot { | |
| width: 10px; | |
| height: 10px; | |
| border-radius: 50%; | |
| background: #262626; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| } | |
| .dot.active { | |
| background: #EAB308; | |
| width: 30px; | |
| border-radius: 5px; | |
| } | |
| .dot:hover { | |
| background: #404040; | |
| } | |
| .dot.active:hover { | |
| background: #EAB308; | |
| } | |
| </style> | |
| <section id="projects"> | |
| <div class="section-header"> | |
| <h2>Featured Projects</h2> | |
| <p class="subtitle">Some of my recent work</p> | |
| </div> | |
| <div class="projects-wrapper"> | |
| <button class="nav-btn prev" id="prev-btn"> | |
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg> | |
| </button> | |
| <div class="projects-scroll" id="projects-scroll"> | |
| <!-- Project 1 --> | |
| <div class="project-container"> | |
| <img src="http://static.photos/technology/640x360/42" alt="Mooshieblob.com Project" class="project-image"> | |
| <div class="project-content"> | |
| <div class="project-tags"> | |
| <span class="tag">Featured</span> | |
| </div> | |
| <div class="folder-icon"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg> | |
| </div> | |
| <h3>mooshieblob.com</h3> | |
| <p> | |
| My fun alias profile showcasing my AI projects and online persona, featuring interactive demos, creative experiments, and real-time AI integrations. | |
| </p> | |
| <div class="tech-stack"> | |
| <span class="tech-item">Nuxt.js</span> | |
| <span class="tech-item">Tailwind CSS</span> | |
| <span class="tech-item">Cloudflare AI</span> | |
| <span class="tech-item">Vercel</span> | |
| </div> | |
| <div class="project-links"> | |
| <a href="https://github.com/Mooshieblob1/mooshieblob1.github.io" target="_blank" class="link-item"> | |
| <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="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path></svg> | |
| Code | |
| </a> | |
| <a href="https://mooshieblob.com/" target="_blank" class="link-item"> | |
| <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="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg> | |
| Visit Live | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Project 2 --> | |
| <div class="project-container"> | |
| <img src="http://static.photos/office/640x360/123" alt="Cloud Dashboard Project" class="project-image"> | |
| <div class="project-content"> | |
| <div class="folder-icon"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg> | |
| </div> | |
| <div class="project-tags"> | |
| <span class="tag">Enterprise</span> | |
| </div> | |
| <div class="folder-icon"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg> | |
| </div> | |
| <h3>Cloud Dashboard</h3> | |
| <p> | |
| A comprehensive cloud infrastructure monitoring dashboard with real-time metrics, intelligent alerts, automated scaling controls, and predictive analytics. | |
| </p> | |
| <div class="tech-stack"> | |
| <span class="tech-item">React</span> | |
| <span class="tech-item">TypeScript</span> | |
| <span class="tech-item">AWS</span> | |
| <span class="tech-item">Terraform</span> | |
| <span class="tech-item">Grafana</span> | |
| <span class="tech-item">Prometheus</span> | |
| </div> | |
| <div class="project-links"> | |
| <a href="#" target="_blank" class="link-item"> | |
| <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="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path></svg> | |
| Code | |
| </a> | |
| <a href="#" target="_blank" class="link-item"> | |
| <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="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg> | |
| Visit Live | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Project 3 --> | |
| <div class="project-container"> | |
| <img src="http://static.photos/nature/640x360/456" alt="E-Commerce Platform Project" class="project-image"> | |
| <div class="project-content"> | |
| <div class="folder-icon"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg> | |
| </div> | |
| <div class="project-tags"> | |
| <span class="tag">SaaS</span> | |
| </div> | |
| <div class="folder-icon"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg> | |
| </div> | |
| <h3>E-Commerce Platform</h3> | |
| <p> | |
| A full-stack e-commerce solution with headless CMS, seamless payment integration, real-time inventory management, and global multi-region deployment. | |
| </p> | |
| <div class="tech-stack"> | |
| <span class="tech-item">Next.js</span> | |
| <span class="tech-item">Stripe</span> | |
| <span class="tech-item">PostgreSQL</span> | |
| <span class="tech-item">Redis</span> | |
| <span class="tech-item">Docker</span> | |
| <span class="tech-item">GraphQL</span> | |
| </div> | |
| <div class="project-links"> | |
| <a href="#" target="_blank" class="link-item"> | |
| <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="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path></svg> | |
| Code | |
| </a> | |
| <a href="#" target="_blank" class="link-item"> | |
| <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="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg> | |
| Visit Live | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Project 4 --> | |
| <div class="project-container"> | |
| <img src="http://static.photos/abstract/640x360/789" alt="DevOps Pipeline Project" class="project-image"> | |
| <div class="project-content"> | |
| <div class="folder-icon"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg> | |
| </div> | |
| <div class="project-tags"> | |
| <span class="tag">Infrastructure</span> | |
| </div> | |
| <div class="folder-icon"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg> | |
| </div> | |
| <h3>DevOps Pipeline</h3> | |
| <p> | |
| Automated CI/CD pipeline solution with self-hosted runners, comprehensive security scanning, automated testing, and multi-environment deployments. | |
| </p> | |
| <div class="tech-stack"> | |
| <span class="tech-item">GitHub Actions</span> | |
| <span class="tech-item">Kubernetes</span> | |
| <span class="tech-item">Helm</span> | |
| <span class="tech-item">ArgoCD</span> | |
| <span class="tech-item">SonarQube</span> | |
| </div> | |
| <div class="project-links"> | |
| <a href="#" target="_blank" class="link-item"> | |
| <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="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path></svg> | |
| Code | |
| </a> | |
| <a href="#" target="_blank" class="link-item"> | |
| <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="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg> | |
| Visit Live | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <button class="nav-btn next" id="next-btn"> | |
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg> | |
| </button> | |
| <div class="dots-container" id="dots-container"></div> | |
| </div> | |
| </section> | |
| `; | |
| } | |
| initScrollNavigation() { | |
| const scrollContainer = this.shadowRoot.getElementById('projects-scroll'); | |
| const prevBtn = this.shadowRoot.getElementById('prev-btn'); | |
| const nextBtn = this.shadowRoot.getElementById('next-btn'); | |
| const dotsContainer = this.shadowRoot.getElementById('dots-container'); | |
| const projects = scrollContainer.querySelectorAll('.project-container'); | |
| // Create dots | |
| projects.forEach((_, index) => { | |
| const dot = document.createElement('div'); | |
| dot.classList.add('dot'); | |
| if (index === 0) dot.classList.add('active'); | |
| dot.addEventListener('click', () => { | |
| projects[index].scrollIntoView({ behavior: 'smooth', inline: 'center' }); | |
| }); | |
| dotsContainer.appendChild(dot); | |
| }); | |
| // Update active dot on scroll | |
| const updateDots = () => { | |
| const scrollLeft = scrollContainer.scrollLeft; | |
| const containerWidth = scrollContainer.offsetWidth; | |
| projects.forEach((project, index) => { | |
| const projectLeft = project.offsetLeft; | |
| const projectWidth = project.offsetWidth; | |
| if (scrollLeft >= projectLeft - containerWidth / 2 && | |
| scrollLeft < projectLeft + projectWidth - containerWidth / 2) { | |
| dotsContainer.querySelectorAll('.dot').forEach(dot => dot.classList.remove('active')); | |
| dotsContainer.querySelectorAll('.dot')[index]?.classList.add('active'); | |
| } | |
| }); | |
| }; | |
| scrollContainer.addEventListener('scroll', updateDots); | |
| // Navigation buttons | |
| prevBtn.addEventListener('click', () => { | |
| const scrollAmount = scrollContainer.offsetWidth * 0.9; | |
| scrollContainer.scrollBy({ left: -scrollAmount, behavior: 'smooth' }); | |
| }); | |
| nextBtn.addEventListener('click', () => { | |
| const scrollAmount = scrollContainer.offsetWidth * 0.9; | |
| scrollContainer.scrollBy({ left: scrollAmount, behavior: 'smooth' }); | |
| }); | |
| // Touch/drag support for mobile | |
| let isDown = false; | |
| let startX; | |
| let scrollLeft; | |
| scrollContainer.addEventListener('mousedown', (e) => { | |
| isDown = true; | |
| startX = e.pageX - scrollContainer.offsetLeft; | |
| scrollLeft = scrollContainer.scrollLeft; | |
| }); | |
| scrollContainer.addEventListener('mouseleave', () => isDown = false); | |
| scrollContainer.addEventListener('mouseup', () => isDown = false); | |
| scrollContainer.addEventListener('mousemove', (e) => { | |
| if (!isDown) return; | |
| e.preventDefault(); | |
| const x = e.pageX - scrollContainer.offsetLeft; | |
| const walk = (x - startX) * 2; | |
| scrollContainer.scrollLeft = scrollLeft - walk; | |
| }); | |
| } | |
| } | |
| customElements.define('custom-projects', CustomProjects); | |