Spaces:
Running
Running
| class CustomSkills 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; | |
| margin-bottom: 1rem; | |
| color: white; | |
| } | |
| h2 span { | |
| color: #EAB308; | |
| } | |
| .subtitle { | |
| color: #9ca3af; | |
| font-family: 'Fira Code', monospace; | |
| } | |
| .skills-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); | |
| gap: 2rem; | |
| } | |
| .skill-card { | |
| background: #171717; | |
| border: 1px solid #262626; | |
| padding: 2rem; | |
| border-radius: 0.75rem; | |
| transition: all 0.3s ease; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .skill-card::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 4px; | |
| background: #EAB308; | |
| transform: scaleX(0); | |
| transform-origin: left; | |
| transition: transform 0.3s ease; | |
| } | |
| .skill-card:hover { | |
| transform: translateY(-5px); | |
| border-color: #404040; | |
| box-shadow: 0 10px 30px -10px rgba(0,0,0,0.5); | |
| } | |
| .skill-card:hover::before { | |
| transform: scaleX(1); | |
| } | |
| .card-icon { | |
| margin-bottom: 1.5rem; | |
| color: #EAB308; | |
| } | |
| h3 { | |
| font-size: 1.25rem; | |
| color: white; | |
| margin-bottom: 1rem; | |
| font-weight: 600; | |
| } | |
| .tech-list { | |
| list-style: none; | |
| padding: 0; | |
| margin: 0; | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 0.75rem; | |
| } | |
| .tech-item { | |
| background: rgba(255, 255, 255, 0.05); | |
| padding: 0.35rem 0.75rem; | |
| border-radius: 4px; | |
| font-size: 0.875rem; | |
| color: #d4d4d8; | |
| transition: all 0.2s; | |
| } | |
| .tech-item:hover { | |
| background: #EAB308; | |
| color: black; | |
| font-weight: 500; | |
| transform: translateY(-2px); | |
| } | |
| .filter-buttons { | |
| display: flex; | |
| justify-content: center; | |
| gap: 1rem; | |
| margin-bottom: 3rem; | |
| flex-wrap: wrap; | |
| } | |
| .filter-btn { | |
| padding: 0.5rem 1.5rem; | |
| background: transparent; | |
| border: 1px solid #262626; | |
| color: #9ca3af; | |
| border-radius: 9999px; | |
| cursor: pointer; | |
| font-size: 0.9rem; | |
| font-family: 'Fira Code', monospace; | |
| transition: all 0.3s ease; | |
| } | |
| .filter-btn:hover, .filter-btn.active { | |
| border-color: #EAB308; | |
| color: #EAB308; | |
| background: rgba(234, 179, 8, 0.1); | |
| } | |
| .skill-card.hidden { | |
| display: none; | |
| } | |
| </style> | |
| <section id="skills"> | |
| <div class="section-header"> | |
| <h2>Skills & <span>Expertise</span></h2> | |
| <p class="subtitle">Technologies I work with</p> | |
| </div> | |
| <div class="filter-buttons"> | |
| <button class="filter-btn active" data-filter="all">All</button> | |
| <button class="filter-btn" data-filter="frontend">Frontend</button> | |
| <button class="filter-btn" data-filter="backend">Backend</button> | |
| <button class="filter-btn" data-filter="devops">DevOps</button> | |
| </div> | |
| <div class="skills-grid"> | |
| <!-- Frontend --> | |
| <div class="skill-card" data-category="frontend"> | |
| <div class="card-icon"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect><line x1="8" y1="21" x2="16" y2="21"></line><line x1="12" y1="17" x2="12" y2="21"></line></svg> | |
| </div> | |
| <h3>Frontend</h3> | |
| <ul class="tech-list"> | |
| <li class="tech-item">React</li> | |
| <li class="tech-item">Vue.js</li> | |
| <li class="tech-item">Svelte</li> | |
| <li class="tech-item">Next.js</li> | |
| <li class="tech-item">Nuxt.js</li> | |
| <li class="tech-item">TypeScript</li> | |
| <li class="tech-item">Tailwind</li> | |
| <li class="tech-item">SCSS</li> | |
| </ul> | |
| </div> | |
| <!-- Backend --> | |
| <div class="skill-card" data-category="backend"> | |
| <div class="card-icon"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg> | |
| </div> | |
| <h3>Backend</h3> | |
| <ul class="tech-list"> | |
| <li class="tech-item">Node.js</li> | |
| <li class="tech-item">Express</li> | |
| <li class="tech-item">Python</li> | |
| <li class="tech-item">Django</li> | |
| <li class="tech-item">PostgreSQL</li> | |
| <li class="tech-item">MongoDB</li> | |
| <li class="tech-item">REST APIs</li> | |
| <li class="tech-item">GraphQL</li> | |
| </ul> | |
| </div> | |
| <!-- DevOps --> | |
| <div class="skill-card" data-category="devops"> | |
| <div class="card-icon"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z"></path></svg> | |
| </div> | |
| <h3>DevOps & Cloud</h3> | |
| <ul class="tech-list"> | |
| <li class="tech-item">AWS</li> | |
| <li class="tech-item">Azure</li> | |
| <li class="tech-item">GCP</li> | |
| <li class="tech-item">Docker</li> | |
| <li class="tech-item">Kubernetes</li> | |
| <li class="tech-item">CI/CD</li> | |
| <li class="tech-item">GitHub Actions</li> | |
| <li class="tech-item">Terraform</li> | |
| </ul> | |
| </div> | |
| </div> | |
| </section> | |
| `; | |
| } | |
| initFilter() { | |
| const filterBtns = this.shadowRoot.querySelectorAll('.filter-btn'); | |
| const skillCards = this.shadowRoot.querySelectorAll('.skill-card'); | |
| filterBtns.forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| const filter = btn.dataset.filter; | |
| // Update active button | |
| filterBtns.forEach(b => b.classList.remove('active')); | |
| btn.classList.add('active'); | |
| // Filter cards | |
| skillCards.forEach(card => { | |
| if (filter === 'all' || card.dataset.category === filter) { | |
| card.classList.remove('hidden'); | |
| card.style.animation = 'fadeIn 0.5s ease forwards'; | |
| } else { | |
| card.classList.add('hidden'); | |
| } | |
| }); | |
| }); | |
| }); | |
| } | |
| } | |
| customElements.define('custom-skills', CustomSkills); | |