Tutorial: Crea tu propio Budget Planner con HTML, CSS y JavaScript

Compartir

Aprende a crear un planificador de presupuesto (Budget Planner) interactivo usando HTML, CSS y JavaScript, con gráficos, alertas y un diseño moderno con Bootstrap 5. Ideal para controlar tus gastos mensuales y visualizar tus finanzas.

Tecnologías necesarias:

Paso 1: Crear la estructura HTML (index.html)

Creamos el archivo index.html que contendrá la estructura de nuestra aplicación, incluyendo la navbar, secciones de presupuesto, gastos, historial y análisis. También enlazamos el CSS y JavaScript externos.

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Budget Planner - Planificador de Presupuesto</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <link href="style.css" rel="stylesheet">
    <style>

</style> </head> <body> <div class="floating-shapes"> <div class="shape"></div> <div class="shape"></div> <div class="shape"></div> </div>

<!-- Navbar --> <nav class="navbar navbar-expand-lg navbar-glass fixed-top"> <div class="container"> <a class="navbar-brand text-white fw-bold" href="#"> <i class="fas fa-wallet me-2"></i>Budget Planner </a> <button class="navbar-toggler border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"> <i class="fas fa-bars text-white"></i> </button> <div class="collapse navbar-collapse" id="navbarNav"> <ul class="navbar-nav ms-auto"> <li class="nav-item"> <a class="nav-link text-white" href="#dashboard">Dashboard</a> </li> <li class="nav-item"> <a class="nav-link text-white" href="#expenses">Gastos</a> </li> <li class="nav-item"> <a class="nav-link text-white" href="#analytics">Análisis</a> </li> </ul> </div> </div> </nav>

<div class="container mt-5 pt-5" style="position: relative; z-index: 2;"> <!-- Budget Setup Section --> <section id="dashboard" class="mb-5"> <div class="row"> <div class="col-12 text-center mb-4"> <h1 class="display-4 fw-bold text-white mb-3"> <span class="text-gradient">Budget</span> Planner </h1> <p class="lead text-white-50">Controla tus finanzas de manera inteligente</p> </div> </div>

<div class="row g-4"> <!-- Budget Setup Card --> <div class="col-lg-6"> <div class="glass-card p-4"> <h3 class="text-white mb-4"> <i class="fas fa-cog me-2"></i>Configurar Presupuesto </h3> <form onsubmit="setBudget(event)"> <div class="mb-3"> <label class="form-label text-white-50">Presupuesto Mensual</label> <div class="input-group"> <span class="input-group-text bg-transparent border-light text-white">$</span> <input type="number" id="budgetInput" class="form-control form-control-modern" placeholder="5000" required> </div> </div> <button type="submit" class="btn btn-modern w-100"> <i class="fas fa-save me-2"></i>Establecer Presupuesto </button> </form> </div> </div>

<!-- Budget Overview Card --> <div class="col-lg-6"> <div class="glass-card p-4 text-center"> <h3 class="text-white mb-3"> <i class="fas fa-chart-line me-2"></i>Resumen </h3> <div class="budget-display" id="budgetDisplay">$0</div> <p class="text-white-50 mb-3">Presupuesto Total</p> <div class="row text-center"> <div class="col-6"> <h4 class="text-success" id="remainingAmount">$0</h4> <small class="text-white-50">Disponible</small> </div> <div class="col-6"> <h4 class="text-danger" id="spentAmount">$0</h4> <small class="text-white-50">Gastado</small> </div> </div> </div> </div> </div>

<!-- Progress Bar --> <div class="row mt-4"> <div class="col-12"> <div class="glass-card p-4"> <div class="d-flex justify-content-between align-items-center mb-3"> <h5 class="text-white mb-0">Progreso del Presupuesto</h5> <span class="text-white-50" id="progressPercentage">0%</span> </div> <div class="progress-modern"> <div class="progress-bar-modern" id="progressBar" style="width: 0%"></div> </div> </div> </div> </div>

<!-- Alert Area --> <div id="alertArea" class="mt-3"></div> </section>

<!-- Add Expense Section --> <section id="expenses" class="mb-5"> <div class="row"> <div class="col-lg-8"> <div class="glass-card p-4"> <h3 class="text-white mb-4"> <i class="fas fa-plus-circle me-2"></i>Agregar Gasto </h3> <form onsubmit="addExpense(event)"> <div class="row g-3"> <div class="col-md-6"> <label class="form-label text-white-50">Descripción</label> <input type="text" id="expenseDescription" class="form-control form-control-modern" placeholder="Ej: Supermercado" required> </div> <div class="col-md-3"> <label class="form-label text-white-50">Monto</label> <div class="input-group"> <span class="input-group-text bg-transparent border-light text-white">$</span> <input type="number" id="expenseAmount" class="form-control form-control-modern" placeholder="100" step="0.01" required> </div> </div> <div class="col-md-3"> <label class="form-label text-white-50">Categoría</label> <select id="expenseCategory" class="form-control form-control-modern" required> <option value="">Seleccionar</option> <option value="comida"> Comida</option> <option value="transporte"> Transporte</option> <option value="entretenimiento"> Entretenimiento</option> <option value="servicios"> Servicios</option> <option value="compras"> Compras</option> <option value="salud">⚕️ Salud</option> <option value="otros"> Otros</option>

</select> </div> <div class="col-12"> <button type="submit" class="btn btn-modern"> <i class="fas fa-plus me-2"></i>Agregar Gasto </button> </div> </div> </form> </div> </div>

<!-- Chart Section --> <div class="col-lg-4"> <div class="glass-card p-4 text-center"> <h5 class="text-white mb-4">Distribución del Presupuesto</h5> <div class="chart-container"> <canvas id="budgetChart"></canvas> </div> </div> </div> </div> </section>

<!-- Expenses List --> <section class="mb-5"> <div class="glass-card p-4"> <div class="d-flex justify-content-between align-items-center mb-4"> <h3 class="text-white mb-0"> <i class="fas fa-list me-2"></i>Historial de Gastos </h3> <button class="btn btn-secondary-modern" onclick="clearAllExpenses()"> <i class="fas fa-trash me-2"></i>Limpiar Todo </button> </div> <div id="expensesList"> <div class="text-center text-white-50 py-4"> <i class="fas fa-receipt fa-3x mb-3"></i> <p>No hay gastos registrados aún</p> </div> </div> </div> </section>

<!-- Analytics Section --> <section id="analytics" class="mb-5"> <div class="row g-4"> <div class="col-md-3"> <div class="glass-card p-4 text-center"> <i class="fas fa-shopping-cart fa-2x text-gradient mb-3"></i> <h4 class="text-white" id="totalExpenses">0</h4> <p class="text-white-50 mb-0">Total Gastos</p> </div> </div> <div class="col-md-3"> <div class="glass-card p-4 text-center"> <i class="fas fa-calendar-day fa-2x text-gradient mb-3"></i> <h4 class="text-white" id="dailyAverage">$0</h4> <p class="text-white-50 mb-0">Promedio Diario</p> </div> </div> <div class="col-md-3"> <div class="glass-card p-4 text-center"> <i class="fas fa-tags fa-2x text-gradient mb-3"></i> <h4 class="text-white" id="topCategory">-</h4> <p class="text-white-50 mb-0">Categoría Top</p> </div> </div> <div class="col-md-3"> <div class="glass-card p-4 text-center"> <i class="fas fa-calendar-alt fa-2x text-gradient mb-3"></i> <h4 class="text-white" id="daysLeft">30</h4> <p class="text-white-50 mb-0">Días Restantes</p> </div> </div> </div> </section> </div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> <script src="script.js"></script> </body> </html>

 

En esta primera parte creamos la estructura principal de la app. Usamos Bootstrap 5 para el diseño, FontAwesome para iconos y Chart.js para los gráficos de distribución de presupuesto. La página incluye un navbar, secciones para el dashboard, gastos, historial y análisis.

Paso 2: Estilos modernos con CSS (style.css)

Creamos style.css para darle un estilo moderno y glassmorphism, con colores degradados, botones estilizados y animaciones de formas flotantes.

       body {
            box-sizing: border-box;
            font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
        }

.glass-card { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 20px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); }

.floating-shapes { position: fixed; width: 100%; height: 100%; overflow: hidden; z-index: 1; pointer-events: none; }

.shape { position: absolute; background: rgba(255, 255, 255, 0.1); border-radius: 50%; animation: float 6s ease-in-out infinite; }

.shape:nth-child(1) { width: 80px; height: 80px; top: 20%; left: 10%; animation-delay: 0s; }

.shape:nth-child(2) { width: 120px; height: 120px; top: 60%; right: 15%; animation-delay: 2s; }

.shape:nth-child(3) { width: 60px; height: 60px; bottom: 20%; left: 20%; animation-delay: 4s; }

@keyframes float { 0%, 100% { transform: translateY(0px) rotate(0deg); } 50% { transform: translateY(-20px) rotate(180deg); } }

.btn-modern { background: linear-gradient(45deg, #ff6b6b, #feca57); border: none; border-radius: 50px; padding: 12px 30px; color: white; font-weight: 600; text-transform: uppercase; letter-spacing: 1px; transition: all 0.3s ease; box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3); }

.btn-modern:hover { transform: translateY(-2px); box-shadow: 0 8px 25px rgba(255, 107, 107, 0.4); color: white; }

.btn-secondary-modern { background: transparent; border: 2px solid rgba(255, 255, 255, 0.3); border-radius: 50px; padding: 12px 30px; color: white; font-weight: 600; text-transform: uppercase; letter-spacing: 1px; transition: all 0.3s ease; }

.btn-secondary-modern:hover { background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.5); transform: translateY(-2px); color: white; }

.navbar-glass { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(10px); border-bottom: 1px solid rgba(255, 255, 255, 0.1); }

.text-gradient { background: linear-gradient(45deg, #ff6b6b, #feca57); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }

.budget-display { font-size: 3rem; font-weight: 700; background: linear-gradient(45deg, #ff6b6b, #feca57); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }

.expense-item { background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 15px; padding: 15px; margin-bottom: 10px; transition: all 0.3s ease; }

.expense-item:hover { background: rgba(255, 255, 255, 0.1); transform: translateX(5px); }

.progress-modern { height: 20px; border-radius: 10px; background: rgba(255, 255, 255, 0.1); overflow: hidden; position: relative; }

.progress-bar-modern { background: linear-gradient(45deg, #ff6b6b, #feca57); border-radius: 10px; transition: width 0.5s ease; height: 100%; display: block; }

.alert-modern { background: rgba(255, 107, 107, 0.2); border: 1px solid rgba(255, 107, 107, 0.3); border-radius: 15px; color: white; }

.chart-container { position: relative; height: 300px; width: 300px; margin: 0 auto; }

.form-control-modern { background: rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 15px; color: white; padding: 12px 20px; }

.form-control-modern::placeholder { color: rgba(255, 255, 255, 0.6); }

.form-control-modern:focus { background: rgba(255, 255, 255, 0.15); border-color: #feca57; box-shadow: 0 0 0 0.2rem rgba(254, 202, 87, 0.25); color: grey; }

.category-badge { background: linear-gradient(45deg, #667eea, #764ba2); color: white; padding: 5px 15px; border-radius: 20px; font-size: 0.8rem; font-weight: 600; }

@media (max-width: 768px) { .budget-display { font-size: 2rem; } .chart-container { height: 250px; width: 250px; } }

 

Este CSS le da un estilo moderno tipo glassmorphism con degradados y sombras suaves. Los botones y formularios tienen animaciones sutiles, mientras que las formas flotantes le dan dinamismo a la interfaz.

Paso 3: Funcionalidad con JavaScript (script.js)

Creamos script.js para manejar el presupuesto, gastos, historial, gráficos y alertas dinámicas.

 let budget = 0;
        let expenses = [];
        let chart = null;

// Inicializar el gráfico function initChart() { const ctx = document.getElementById('budgetChart').getContext('2d'); chart = new Chart(ctx, { type: 'doughnut', data: { labels: ['Gastado', 'Disponible'], datasets: [{ data: [0, 100], backgroundColor: [ 'rgba(255, 107, 107, 0.8)', 'rgba(102, 126, 234, 0.8)' ], borderColor: [ 'rgba(255, 107, 107, 1)', 'rgba(102, 126, 234, 1)' ], borderWidth: 2 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { labels: { color: 'white' } } } } }); }

// Establecer presupuesto function setBudget(event) { event.preventDefault(); const budgetInput = document.getElementById('budgetInput'); budget = parseFloat(budgetInput.value);

document.getElementById('budgetDisplay').textContent = `$${budget.toLocaleString()}`; updateBudgetDisplay(); showAlert('¡Presupuesto establecido correctamente!', 'success'); }

// Agregar gasto function addExpense(event) { event.preventDefault();

const description = document.getElementById('expenseDescription').value; const amount = parseFloat(document.getElementById('expenseAmount').value); const category = document.getElementById('expenseCategory').value;

const expense = { id: Date.now(), description, amount, category, date: new Date().toLocaleDateString() };

expenses.push(expense); updateBudgetDisplay(); renderExpenses(); updateAnalytics();

// Limpiar el formulario event.target.reset();

// Comprobar si se ha excedido el presupuesto const totalSpent = expenses.reduce((sum, exp) => sum + exp.amount, 0); if (totalSpent > budget && budget > 0) { showAlert('¡Atención! Has superado tu presupuesto mensual', 'danger'); }

showAlert('Gasto agregado correctamente', 'success'); }

// Actualizar la visualización del presupuesto function updateBudgetDisplay() { const totalSpent = expenses.reduce((sum, exp) => sum + exp.amount, 0); const remaining = budget - totalSpent; const percentage = budget > 0 ? (totalSpent / budget) * 100 : 0;

document.getElementById('spentAmount').textContent = `$${totalSpent.toLocaleString()}`; document.getElementById('remainingAmount').textContent = `$${remaining.toLocaleString()}`; document.getElementById('progressPercentage').textContent = `${Math.round(percentage)}%`;

const progressBar = document.getElementById('progressBar'); progressBar.style.width = `${Math.min(percentage, 100)}%`;

// Cambiar de color si se supera el presupuesto if (percentage > 100) { progressBar.style.background = 'linear-gradient(45deg, #ff4757, #ff3838)'; } else if (percentage > 80) { progressBar.style.background = 'linear-gradient(45deg, #ffa502, #ff6348)'; } else { progressBar.style.background = 'linear-gradient(45deg, #ff6b6b, #feca57)'; }

// Actualizar el gráfico if (chart && budget > 0) { chart.data.datasets[0].data = [totalSpent, Math.max(0, remaining)]; chart.update(); } }

// Renderizas la lista de gastos function renderExpenses() { const expensesList = document.getElementById('expensesList');

if (expenses.length === 0) { expensesList.innerHTML = ` <div class="text-center text-white-50 py-4"> <i class="fas fa-receipt fa-3x mb-3"></i> <p>No hay gastos registrados aún</p> </div> `; return; }

expensesList.innerHTML = expenses.map(expense => ` <div class="expense-item"> <div class="d-flex justify-content-between align-items-center"> <div> <h6 class="text-white mb-1">${expense.description}</h6> <small class="text-white-50">${expense.date}</small> <span class="category-badge ms-2">${expense.category}</span> </div> <div class="text-end"> <h5 class="text-danger mb-0">-$${expense.amount.toLocaleString()}</h5> <button class="btn btn-sm btn-outline-danger mt-1" onclick="deleteExpense(${expense.id})"> <i class="fas fa-trash"></i> </button> </div> </div> </div> `).join(''); }

// Eliminar gasto function deleteExpense(id) {

if (confirm("Estas seguro que deseas eliminar este gasto?")){ expenses = expenses.filter(exp => exp.id !== id); updateBudgetDisplay(); renderExpenses(); updateAnalytics(); showAlert('Gasto eliminado', 'info'); }

}

// Eliminar todos los gastos function clearAllExpenses() { if (confirm('¿Estás seguro de que quieres eliminar todos los gastos?')) { expenses = []; updateBudgetDisplay(); renderExpenses(); updateAnalytics(); showAlert('Todos los gastos han sido eliminados', 'info'); } }

// Actualizar analíticas function updateAnalytics() { const totalExpenses = expenses.length; const totalSpent = expenses.reduce((sum, exp) => sum + exp.amount, 0); const dailyAverage = totalExpenses > 0 ? totalSpent / 30 : 0;

// Encontrar la categoría principal const categoryTotals = {}; expenses.forEach(exp => { categoryTotals[exp.category] = (categoryTotals[exp.category] || 0) + exp.amount; });

const topCategory = Object.keys(categoryTotals).reduce((a, b) => categoryTotals[a] > categoryTotals[b] ? a : b, '-' );

document.getElementById('totalExpenses').textContent = totalExpenses; document.getElementById('dailyAverage').textContent = `$${dailyAverage.toFixed(2)}`; document.getElementById('topCategory').textContent = topCategory;

// Calcular los días que quedan en el mes const now = new Date(); const daysInMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate(); const daysLeft = daysInMonth - now.getDate(); document.getElementById('daysLeft').textContent = daysLeft; }

// Muestra alertas function showAlert(message, type = 'info') { const alertArea = document.getElementById('alertArea'); const alertClass = type === 'danger' ? 'alert-modern' : 'alert-info'; const icon = type === 'danger' ? 'fa-exclamation-triangle' : type === 'success' ? 'fa-check-circle' : 'fa-info-circle';

const alert = document.createElement('div'); alert.className = `alert ${alertClass} alert-dismissible fade show`; alert.innerHTML = ` <i class="fas ${icon} me-2"></i> ${message} <button type="button" class="btn-close btn-close-white" data-bs-dismiss="alert"></button> `;

alertArea.appendChild(alert);

// Eliminar automáticamente después de 5 segundos setTimeout(() => { if (alert.parentNode) { alert.remove(); } }, 5000); }

// Desplazamiento suave para la navegación document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); });

// Inicializar la aplicación document.addEventListener('DOMContentLoaded', function() { initChart(); updateAnalytics(); });

// Fondo de la barra de navegación al desplazarse window.addEventListener('scroll', () => { const navbar = document.querySelector('.navbar'); if (window.scrollY > 50) { navbar.style.background = 'rgba(255, 255, 255, 0.15)'; } else { navbar.style.background = 'rgba(255, 255, 255, 0.1)'; } });

Con este script manejamos todas las funcionalidades: establecer presupuesto, agregar/eliminar gastos, actualizar gráficos de Chart.js, mostrar alertas y calcular métricas como gasto diario, categoría top y días restantes.

 Apuntes del tutorial

Con estos tres archivos (index.html, style.css, script.js) tendrás un Budget Planner completo con:

  • Configuración de presupuesto mensual
  • Registro de gastos con categoría y fecha
  • Visualización de progreso y gráficos
  • Historial de gastos
  • Alertas y análisis diario
  • El diseño es moderno, responsive y atractivo, listo para integrarse

VER DEMOSTRACIÓN DESCARGAR ARCHIVOS

Obed Alvarado

Sobre el autor

Artículo redactado por Obed Alvarado. Comparto experiencias prácticas sobre PHP, MySQL, aplicaciones web, instalación en hosting y herramientas para negocios.

Comentarios