A Pen by Adriel Santos on CodePen.
Created
November 26, 2025 00:48
-
-
Save adrielcruz9966-a11y/3bf88da5153b78923bf8fb25c2ffd2ed to your computer and use it in GitHub Desktop.
Untitled
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="pt-br"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Wealth Tracker | R$ 1.000.000</title> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;600&family=Inter:wght@300;400;500&display=swap" rel="stylesheet"> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| background: linear-gradient(135deg, #0c0c0c 0%, #1a1a1a 100%); | |
| min-height: 100vh; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| padding: 20px; | |
| color: #e0e0e0; | |
| } | |
| .luxury-card { | |
| background: rgba(18, 18, 18, 0.95); | |
| border-radius: 16px; | |
| padding: 50px 40px; | |
| width: 100%; | |
| max-width: 500px; | |
| text-align: center; | |
| border: 1px solid rgba(255, 215, 0, 0.1); | |
| box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .luxury-card::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| height: 1px; | |
| background: linear-gradient(90deg, transparent, #ffd700, transparent); | |
| } | |
| .header { | |
| margin-bottom: 40px; | |
| } | |
| h1 { | |
| font-family: 'Playfair Display', serif; | |
| font-size: 2.2rem; | |
| font-weight: 600; | |
| color: #ffd700; | |
| margin-bottom: 8px; | |
| letter-spacing: 0.5px; | |
| } | |
| .subtitle { | |
| color: #b0b0b0; | |
| font-size: 1rem; | |
| font-weight: 300; | |
| letter-spacing: 1px; | |
| } | |
| .target-display { | |
| position: relative; | |
| margin: 50px 0; | |
| height: 120px; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| } | |
| .target-number { | |
| font-size: 4rem; | |
| font-weight: 300; | |
| color: rgba(255, 255, 255, 0.03); | |
| position: absolute; | |
| letter-spacing: 4px; | |
| font-family: 'Playfair Display', serif; | |
| } | |
| .current-value { | |
| font-size: 3.2rem; | |
| font-weight: 300; | |
| color: #ffffff; | |
| position: relative; | |
| transition: all 8s cubic-bezier(0.16, 1, 0.3, 1); | |
| letter-spacing: 2px; | |
| font-family: 'Playfair Display', serif; | |
| } | |
| .currency { | |
| font-size: 1.8rem; | |
| color: #ffd700; | |
| margin-right: 8px; | |
| font-weight: 300; | |
| } | |
| .input-section { | |
| margin: 40px 0 30px; | |
| } | |
| .input-container { | |
| position: relative; | |
| margin-bottom: 25px; | |
| } | |
| .input-icon { | |
| position: absolute; | |
| left: 16px; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| color: #ffd700; | |
| font-size: 1.1rem; | |
| } | |
| input { | |
| width: 100%; | |
| padding: 16px 16px 16px 50px; | |
| font-size: 1.1rem; | |
| background: rgba(30, 30, 30, 0.8); | |
| border: 1px solid rgba(255, 215, 0, 0.2); | |
| border-radius: 8px; | |
| color: #ffffff; | |
| transition: all 0.3s; | |
| font-weight: 300; | |
| } | |
| input:focus { | |
| outline: none; | |
| border-color: #ffd700; | |
| box-shadow: 0 0 0 1px rgba(255, 215, 0, 0.2); | |
| } | |
| input::placeholder { | |
| color: #666; | |
| } | |
| .luxury-button { | |
| background: transparent; | |
| color: #ffd700; | |
| border: 1px solid rgba(255, 215, 0, 0.3); | |
| padding: 16px 30px; | |
| font-size: 1rem; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| width: 100%; | |
| font-weight: 400; | |
| letter-spacing: 1px; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .luxury-button:hover { | |
| background: rgba(255, 215, 0, 0.05); | |
| border-color: rgba(255, 215, 0, 0.5); | |
| transform: translateY(-2px); | |
| } | |
| .luxury-button:active { | |
| transform: translateY(0); | |
| } | |
| .progress-section { | |
| margin: 40px 0; | |
| } | |
| .progress-header { | |
| display: flex; | |
| justify-content: space-between; | |
| margin-bottom: 12px; | |
| font-size: 0.9rem; | |
| color: #b0b0b0; | |
| } | |
| .progress-percent { | |
| color: #ffd700; | |
| } | |
| .progress-bar-container { | |
| height: 2px; | |
| background: rgba(255, 255, 255, 0.1); | |
| border-radius: 1px; | |
| overflow: hidden; | |
| margin-bottom: 15px; | |
| } | |
| .progress-bar { | |
| height: 100%; | |
| background: #ffd700; | |
| width: 0%; | |
| transition: width 8s cubic-bezier(0.16, 1, 0.3, 1); | |
| position: relative; | |
| } | |
| .progress-bar::after { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| right: 0; | |
| bottom: 0; | |
| width: 20px; | |
| background: linear-gradient(90deg, transparent, rgba(255, 215, 0, 0.7)); | |
| } | |
| .milestones { | |
| display: flex; | |
| justify-content: space-between; | |
| margin-top: 30px; | |
| position: relative; | |
| } | |
| .milestone { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| position: relative; | |
| flex: 1; | |
| } | |
| .milestone::before { | |
| content: ''; | |
| position: absolute; | |
| top: 10px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| width: 8px; | |
| height: 8px; | |
| border-radius: 50%; | |
| background: rgba(255, 215, 0, 0.3); | |
| z-index: 2; | |
| } | |
| .milestone.active::before { | |
| background: #ffd700; | |
| box-shadow: 0 0 10px rgba(255, 215, 0, 0.5); | |
| } | |
| .milestone-line { | |
| position: absolute; | |
| top: 14px; | |
| left: 0; | |
| right: 0; | |
| height: 1px; | |
| background: rgba(255, 255, 255, 0.1); | |
| z-index: 1; | |
| } | |
| .milestone-label { | |
| margin-top: 20px; | |
| font-size: 0.8rem; | |
| color: #888; | |
| } | |
| .milestone.active .milestone-label { | |
| color: #ffd700; | |
| } | |
| .stats { | |
| display: flex; | |
| justify-content: space-between; | |
| margin-top: 40px; | |
| padding-top: 30px; | |
| border-top: 1px solid rgba(255, 255, 255, 0.05); | |
| } | |
| .stat { | |
| text-align: center; | |
| flex: 1; | |
| } | |
| .stat-value { | |
| font-size: 1.4rem; | |
| color: #ffd700; | |
| margin-bottom: 5px; | |
| font-weight: 300; | |
| } | |
| .stat-label { | |
| font-size: 0.75rem; | |
| color: #888; | |
| letter-spacing: 0.5px; | |
| } | |
| .footer { | |
| margin-top: 30px; | |
| font-size: 0.75rem; | |
| color: #666; | |
| letter-spacing: 0.5px; | |
| } | |
| @media (max-width: 600px) { | |
| .luxury-card { | |
| padding: 40px 25px; | |
| } | |
| h1 { | |
| font-size: 1.8rem; | |
| } | |
| .target-number { | |
| font-size: 3rem; | |
| } | |
| .current-value { | |
| font-size: 2.5rem; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="luxury-card"> | |
| <div class="header"> | |
| <h1>Wealth Progress</h1> | |
| <p class="subtitle">TRACKING TOWARDS FINANCIAL FREEDOM</p> | |
| </div> | |
| <div class="target-display"> | |
| <div class="target-number">1.000.000</div> | |
| <div class="current-value"> | |
| <span class="currency">R$</span> | |
| <span id="currentNumber">0</span> | |
| </div> | |
| </div> | |
| <div class="input-section"> | |
| <div class="input-container"> | |
| <div class="input-icon"> | |
| <i class="fas fa-pen"></i> | |
| </div> | |
| <input type="number" id="currentValue" placeholder="Enter current balance" min="0" max="1000000"> | |
| </div> | |
| <button class="luxury-button" id="animateButton"> | |
| UPDATE PROGRESS | |
| </button> | |
| </div> | |
| <div class="progress-section"> | |
| <div class="progress-header"> | |
| <span>Progress</span> | |
| <span class="progress-percent" id="progressPercent">0%</span> | |
| </div> | |
| <div class="progress-bar-container"> | |
| <div class="progress-bar" id="progressBar"></div> | |
| </div> | |
| <div class="milestones"> | |
| <div class="milestone-line"></div> | |
| <div class="milestone" id="milestone1"> | |
| <div class="milestone-label">250K</div> | |
| </div> | |
| <div class="milestone" id="milestone2"> | |
| <div class="milestone-label">500K</div> | |
| </div> | |
| <div class="milestone" id="milestone3"> | |
| <div class="milestone-label">750K</div> | |
| </div> | |
| <div class="milestone" id="milestone4"> | |
| <div class="milestone-label">1M</div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="stats"> | |
| <div class="stat"> | |
| <div class="stat-value" id="remainingValue">R$ 1.000.000</div> | |
| <div class="stat-label">REMAINING</div> | |
| </div> | |
| <div class="stat"> | |
| <div class="stat-value" id="percentageValue">0%</div> | |
| <div class="stat-label">ACHIEVED</div> | |
| </div> | |
| </div> | |
| <div class="footer"> | |
| <p>Wealth Tracker • Designed for Excellence</p> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| const currentNumber = document.getElementById('currentNumber'); | |
| const currentValue = document.getElementById('currentValue'); | |
| const animateButton = document.getElementById('animateButton'); | |
| const progressBar = document.getElementById('progressBar'); | |
| const progressPercent = document.getElementById('progressPercent'); | |
| const remainingValue = document.getElementById('remainingValue'); | |
| const percentageValue = document.getElementById('percentageValue'); | |
| const milestones = [ | |
| document.getElementById('milestone1'), | |
| document.getElementById('milestone2'), | |
| document.getElementById('milestone3'), | |
| document.getElementById('milestone4') | |
| ]; | |
| // Formatar número com separadores de milhar | |
| function formatNumber(num) { | |
| return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, "."); | |
| } | |
| // Atualizar milestones | |
| function updateMilestones(value) { | |
| const milestoneValues = [250000, 500000, 750000, 1000000]; | |
| milestones.forEach((milestone, index) => { | |
| if (value >= milestoneValues[index]) { | |
| milestone.classList.add('active'); | |
| } else { | |
| milestone.classList.remove('active'); | |
| } | |
| }); | |
| } | |
| // Atualizar estatísticas | |
| function updateStats(value) { | |
| const percent = (value / 1000000) * 100; | |
| const remaining = 1000000 - value; | |
| progressPercent.textContent = percent.toFixed(1) + '%'; | |
| remainingValue.textContent = 'R$ ' + formatNumber(remaining); | |
| percentageValue.textContent = percent.toFixed(1) + '%'; | |
| updateMilestones(value); | |
| } | |
| // Animação do contador | |
| function animateCounter(targetValue) { | |
| // Limpar qualquer animação anterior | |
| currentNumber.style.transition = 'none'; | |
| currentNumber.style.transform = 'translateY(0)'; | |
| // Definir valores iniciais | |
| const startValue = 0; | |
| const endValue = targetValue; | |
| const duration = 8000; // 8 segundos | |
| const startTime = performance.now(); | |
| // Atualizar a barra de progresso | |
| const progressPercentValue = (targetValue / 1000000) * 100; | |
| progressBar.style.width = '0%'; | |
| // Função de animação | |
| function updateCounter(currentTime) { | |
| const elapsedTime = currentTime - startTime; | |
| const progress = Math.min(elapsedTime / duration, 1); | |
| // Calcular valor atual com easing | |
| const easeProgress = 1 - Math.pow(1 - progress, 3); | |
| const currentValue = Math.floor(startValue + (endValue - startValue) * easeProgress); | |
| // Atualizar display | |
| currentNumber.textContent = formatNumber(currentValue); | |
| // Atualizar barra de progresso | |
| progressBar.style.width = `${progressPercentValue * progress}%`; | |
| // Atualizar estatísticas | |
| updateStats(currentValue); | |
| // Aplicar efeito de subida | |
| currentNumber.style.transform = `translateY(${-10 * progress}px)`; | |
| // Continuar animação se não terminou | |
| if (progress < 1) { | |
| requestAnimationFrame(updateCounter); | |
| } else { | |
| // Finalizar animação | |
| currentNumber.textContent = formatNumber(endValue); | |
| progressBar.style.width = `${progressPercentValue}%`; | |
| updateStats(endValue); | |
| } | |
| } | |
| // Iniciar animação | |
| requestAnimationFrame(updateCounter); | |
| } | |
| // Iniciar animação quando o botão for clicado | |
| animateButton.addEventListener('click', function() { | |
| const targetValue = parseInt(currentValue.value); | |
| if (isNaN(targetValue) || targetValue < 0) { | |
| alert('Please enter a valid value greater than or equal to zero.'); | |
| return; | |
| } | |
| if (targetValue > 1000000) { | |
| alert('Value cannot be greater than 1,000,000.'); | |
| return; | |
| } | |
| animateCounter(targetValue); | |
| }); | |
| // Permitir Enter para iniciar a animação | |
| currentValue.addEventListener('keyup', function(event) { | |
| if (event.key === 'Enter') { | |
| animateButton.click(); | |
| } | |
| }); | |
| // Exemplo inicial | |
| currentValue.value = '350000'; | |
| animateCounter(350000); | |
| }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment