Spaces:
Running
Running
| class CustomHero extends HTMLElement { | |
| connectedCallback() { | |
| this.attachShadow({ mode: 'open' }); | |
| this.shadowRoot.innerHTML = ` | |
| <style> | |
| :host { | |
| display: block; | |
| min-height: 100vh; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| position: relative; | |
| padding: 6rem 1rem; | |
| overflow: hidden; | |
| } | |
| .container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| text-align: center; | |
| z-index: 2; | |
| } | |
| .status-badge { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| padding: 0.5rem 1rem; | |
| background: rgba(234, 179, 8, 0.1); | |
| border: 1px solid rgba(234, 179, 8, 0.3); | |
| border-radius: 9999px; | |
| color: #EAB308; | |
| font-family: 'Fira Code', monospace; | |
| font-size: 0.875rem; | |
| margin-bottom: 1.5rem; | |
| } | |
| .status-dot { | |
| width: 8px; | |
| height: 8px; | |
| background-color: #EAB308; | |
| border-radius: 50%; | |
| box-shadow: 0 0 10px #EAB308; | |
| animation: pulse 2s infinite; | |
| } | |
| @keyframes pulse { | |
| 0% { opacity: 1; } | |
| 50% { opacity: 0.5; } | |
| 100% { opacity: 1; } | |
| } | |
| h1 { | |
| font-size: clamp(2.5rem, 5vw, 4.5rem); | |
| font-weight: 800; | |
| line-height: 1.1; | |
| margin-bottom: 1rem; | |
| color: white; | |
| } | |
| .highlight { | |
| color: #EAB308; | |
| display: block; | |
| } | |
| p { | |
| font-size: 1.125rem; | |
| color: #9ca3af; | |
| max-width: 600px; | |
| margin: 0 auto 2.5rem auto; | |
| line-height: 1.6; | |
| } | |
| .cta-group { | |
| display: flex; | |
| gap: 1rem; | |
| justify-content: center; | |
| align-items: center; | |
| } | |
| .btn-primary { | |
| background-color: #EAB308; | |
| color: black; | |
| padding: 0.75rem 2rem; | |
| border-radius: 0.375rem; | |
| font-weight: 600; | |
| text-decoration: none; | |
| transition: all 0.3s ease; | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| .btn-primary:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 10px 15px -3px rgba(234, 179, 8, 0.3); | |
| } | |
| .btn-secondary { | |
| color: white; | |
| padding: 0.75rem 2rem; | |
| border-radius: 0.375rem; | |
| font-weight: 500; | |
| text-decoration: none; | |
| border: 1px solid #333; | |
| transition: all 0.3s ease; | |
| } | |
| .btn-secondary:hover { | |
| border-color: #EAB308; | |
| color: #EAB308; | |
| } | |
| /* Background Decor */ | |
| .glow-bg { | |
| position: absolute; | |
| width: 600px; | |
| height: 600px; | |
| background: radial-gradient(circle, rgba(234, 179, 8, 0.08) 0%, rgba(0,0,0,0) 70%); | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| z-index: 1; | |
| pointer-events: none; | |
| } | |
| .grid-bg { | |
| 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: 50px 50px; | |
| z-index: 0; | |
| mask-image: radial-gradient(circle at center, black 30%, transparent 100%); | |
| animation: gridMove 20s linear infinite; | |
| } | |
| @keyframes gridMove { | |
| 0% { transform: perspective(500px) rotateX(60deg) translateY(0); } | |
| 100% { transform: perspective(500px) rotateX(60deg) translateY(50px); } | |
| } | |
| .floating-shapes { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| overflow: hidden; | |
| z-index: 1; | |
| } | |
| .shape { | |
| position: absolute; | |
| border-radius: 50%; | |
| opacity: 0.1; | |
| animation: float 15s ease-in-out infinite; | |
| } | |
| .shape-1 { | |
| width: 300px; | |
| height: 300px; | |
| background: #EAB308; | |
| top: 10%; | |
| right: 10%; | |
| animation-delay: 0s; | |
| } | |
| .shape-2 { | |
| width: 200px; | |
| height: 200px; | |
| background: #EAB308; | |
| bottom: 20%; | |
| left: 10%; | |
| animation-delay: 5s; | |
| } | |
| .shape-3 { | |
| width: 150px; | |
| height: 150px; | |
| background: #EAB308; | |
| top: 50%; | |
| left: 5%; | |
| animation-delay: 10s; | |
| } | |
| @keyframes float { | |
| 0%, 100% { transform: translateY(0) rotate(0deg); } | |
| 25% { transform: translateY(-20px) rotate(5deg); } | |
| 50% { transform: translateY(0) rotate(0deg); } | |
| 75% { transform: translateY(20px) rotate(-5deg); } | |
| } | |
| .typing-text { | |
| display: inline-block; | |
| overflow: hidden; | |
| border-right: 3px solid #EAB308; | |
| white-space: nowrap; | |
| animation: typing 3s steps(40, end), blink-caret 0.75s step-end infinite; | |
| } | |
| @keyframes typing { | |
| from { width: 0 } | |
| to { width: 100% } | |
| } | |
| @keyframes blink-caret { | |
| from, to { border-color: transparent } | |
| 50% { border-color: #EAB308 } | |
| } | |
| </style> | |
| <div class="grid-bg"></div> | |
| <div class="floating-shapes"> | |
| <div class="shape shape-1"></div> | |
| <div class="shape shape-2"></div> | |
| <div class="shape shape-3"></div> | |
| </div> | |
| <div class="glow-bg"></div> | |
| <div class="container animate-fade-in"> | |
| <div class="status-badge"> | |
| <div class="status-dot"></div> | |
| <span>Open to Work</span> | |
| </div> | |
| <h1> | |
| Hi, I'm <span style="background: linear-gradient(135deg, #EAB308, #CA8A04); -webkit-background-clip: text; -webkit-text-fill-color: transparent;">Kent Vuong</span> | |
| <span class="highlight">Full-Stack & DevOps Engineer</span> | |
| </h1> | |
| <p> | |
| I build scalable web applications and robust cloud infrastructure. | |
| Passionate about clean code, automation, and delivering exceptional user experiences. | |
| </p> | |
| <div class="cta-group"> | |
| <a href="#projects" class="btn-primary"> | |
| View Projects <i data-feather="arrow-right" style="width: 18px;"></i> | |
| </a> | |
| <a href="#contact" class="btn-secondary">Contact Me</a> | |
| </div> | |
| </div> | |
| `; | |
| // Re-run feather replace for this shadow DOM content | |
| // Since feather.replace() in main body won't reach shadow root | |
| const svg = this.shadowRoot.querySelector('svg'); | |
| // We manually set the SVG content or assume the global feather script handles it if the element exists in DOM. | |
| // However, Feather Icons replace() only targets elements in the document. | |
| // A quick workaround for Shadow DOM: | |
| const icons = this.shadowRoot.querySelectorAll('[data-feather]'); | |
| icons.forEach(icon => { | |
| const name = icon.getAttribute('data-feather'); | |
| // Simple SVG strings for icons used in this component | |
| let svgContent = ''; | |
| if(name === 'arrow-right') { | |
| svgContent = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg>`; | |
| } | |
| icon.outerHTML = svgContent; | |
| }); | |
| } | |
| } | |
| customElements.define('custom-hero', CustomHero); |