Table of Contents
Introducción: ¿Por qué una tabla de precios dinámicos
Las tablas de precios son una piedra angular de cualquier producto o sitio web de servicio. Una tabla estática, de tamaño único-fits-all puede presentar sus puntos de precio, pero una tabla de precios dinamicos — una que actualiza en tiempo real basado en las interacciones de los usuarios— puede aumentar significativamente las tasas de conversión. Al permitir a los visitantes cambiar el código mensual y anual, vea el costo total con funciones de tablas interactivas,
Configuración de la estructura HTML para un rejilla de precios
La base de cualquier tabla de precios dinámico es una estructura HTML limpia y semántica. Comenzaremos con un contenedor que se convertirá en nuestro CSS Grid, luego anidamos tarjetas de precios individuales dentro de él. Cada tarjeta debe tener el nombre del plan, precio, una lista de características, y un botón de llamada a acción. Para habilitar actualizaciones dinámicas, vamos a incluir un atributo en cada tarjeta y estructura el contenedor de precio con una clase que podemos apuntar JavaScript.
<div class="pricing-grid" role="group" aria-label="Pricing plans">
<div class="pricing-card" data-plan="basic">
<h3 class="plan-name">Basic</h3>
<p class="price">
<span class="amount">$10</span>
<span class="period">/mo</span>
</p>
<ul class="features">
<li>5 projects</li>
<li>10 GB storage</li>
<li>Basic support</li>
</ul>
<a href="#" class="btn">Get Started</a>
</div>
<div class="pricing-card featured" data-plan="pro">
<h3 class="plan-name">Pro</h3>
<p class="price">
<span class="amount">$25</span>
<span class="period">/mo</span>
</p>
<ul class="features">
<li>Unlimited projects</li>
<li>100 GB storage</li>
<li>Priority support</li>
</ul>
<a href="#" class="btn">Get Started</a>
</div>
<div class="pricing-card" data-plan="enterprise">
<h3 class="plan-name">Enterprise</h3>
<p class="price">
<span class="amount">$50</span>
<span class="period">/mo</span>
</p>
<ul class="features">
<li>Unlimited projects</li>
<li>1 TB storage</li>
<li>24/7 support</li>
</ul>
<a href="#" class="btn">Contact Sales</a>
</div>
</div>
Observe la y para mejorar la accesibilidad. La clase en la tarjeta Pro nos permite destacar la opción más popular sin tener JavaScript extra.
Construyendo el diseño de la caja de la CSS
CSS Grid hace que sea sencillo crear una red receptiva que ajuste el número de columnas basadas en el puerto de visualización. Definimos el contenedor como una cuadrícula, establece un número flexible de columnas con y , y agregamos espaciamiento con . En cada tarjeta, utilizamos CSS Grid internamente para alinear el precio, las características y el botón.
.pricing-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 1.5rem;
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
.pricing-card {
display: grid;
grid-template-rows: auto auto 1fr auto;
border: 1px solid #e0e0e0;
border-radius: 12px;
padding: 2rem;
text-align: center;
background: #fff;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.pricing-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0,0,0,0.12);
}
.pricing-card.featured {
border: 2px solid #007bff;
position: relative;
}
.pricing-card.featured::before {
content: "Most Popular";
position: absolute;
top: -12px;
left: 50%;
transform: translateX(-50%);
background: #007bff;
color: #fff;
padding: 4px 16px;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
}
.plan-name {
font-size: 1.5rem;
margin: 0 0 0.5rem;
}
.price {
margin: 0 0 1.5rem;
}
.amount {
font-size: 2.5rem;
font-weight: 700;
}
.period {
font-size: 1rem;
color: #666;
}
.features {
list-style: none;
padding: 0;
margin: 0 0 1.5rem;
}
.features li {
padding: 0.5rem 0;
border-bottom: 1px solid #eee;
}
.features li:last-child {
border-bottom: none;
}
.btn {
display: inline-block;
padding: 0.75rem 1.5rem;
background: #007bff;
color: #fff;
text-decoration: none;
border-radius: 8px;
transition: background 0.3s;
}
.btn:hover {
background: #0056b3;
}
El garantiza que el botón siempre se queda en la parte inferior, incluso si las listas de características difieren en longitud. La placa de la tarjeta ofrecida "Más Popular" se crea con un seudoelement para evitar el marcado adicional.
Agregar la Interactividad JavaScript
Ahora viene la parte dinámica. Agregaremos un interruptor de rebote que permite a los visitantes cambiar entre los precios mensuales y anuales. Cuando el cambio de rebote, JavaScript actualiza los precios de todas las tarjetas. También incluimos un ayudante para formatear precios y una transición suave para el cambio numérico.
<div class="pricing-toggle">
<label class="toggle-label">
<span>Monthly</span>
<input type="checkbox" id="billing-toggle" role="switch" aria-checked="false">
<span class="toggle-slider"></span>
<span>Yearly (save up to 20%)</span>
</label>
</div>
CSS para la rebosa:
.pricing-toggle {
text-align: center;
margin-bottom: 2rem;
}
.toggle-label {
display: inline-flex;
align-items: center;
gap: 0.75rem;
cursor: pointer;
user-select: none;
}
.toggle-label input[type="checkbox"] {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.toggle-slider {
width: 50px;
height: 26px;
background: #ccc;
border-radius: 13px;
position: relative;
transition: background 0.3s;
}
.toggle-slider::after {
content: "";
position: absolute;
top: 3px;
left: 3px;
width: 20px;
height: 20px;
background: white;
border-radius: 50%;
transition: transform 0.3s;
}
input:checked + .toggle-slider {
background: #007bff;
}
input:checked + .toggle-slider::after {
transform: translateX(24px);
}
JavaScript que potencia la actualización:
document.addEventListener('DOMContentLoaded', () => {
const toggle = document.getElementById('billing-toggle');
const prices = {
basic: { monthly: 10, yearly: 8 }, // per month when billed yearly
pro: { monthly: 25, yearly: 20 },
enterprise: { monthly: 50, yearly: 40 }
};
function updatePrices(isYearly) {
document.querySelectorAll('.pricing-card').forEach(card => {
const plan = card.dataset.plan;
if (!plan || !prices[plan]) return;
const amountElement = card.querySelector('.amount');
const periodElement = card.querySelector('.period');
let amount, period;
if (isYearly) {
amount = prices[plan].yearly;
period = ' /mo (billed yearly)';
} else {
amount = prices[plan].monthly;
period = ' /mo';
}
amountElement.textContent = `$${amount}`;
periodElement.textContent = period;
});
}
toggle.addEventListener('change', () => {
const isYearly = toggle.checked;
updatePrices(isYearly);
toggle.setAttribute('aria-checked', isYearly);
});
// Initialize with monthly
updatePrices(false);
});
El objeto almacena tarifas mensuales y anuales. Los precios anuales representan el costo por mes al pagar anualmente — un patrón común. La función itera sobre cada tarjeta, lee su , y actualiza la cantidad y el período mostrados. También actualizamos el atributo para mantener la rebosa accesible.
Mejora de la transición
Para hacer que el cambio de precio se sienta suave, podemos añadir una breve transición CSS en los elementos y . Sin embargo, como cambia instantáneamente, podemos simular un efecto de cuenta con un intervalo pequeño, o simplemente utilizar un fade‐in. Para un sitio de producción, una transición rápida de opacidad en el contenedor de precio funciona bien.
.price {
transition: opacity 0.2s ease;
}
.updating .price {
opacity: 0;
}
Luego en JavaScript, agregue y quite brevemente la clase en cada tarjeta. Pero para la mayoría de los casos de uso, la actualización instantánea está bien.
Hacer que la tabla sea responsable y accesible
CSS Grid ya maneja la capacidad de respuesta, pero asegura que en pantallas muy pequeñas las tarjetas se apilan en una sola columna. El valor lo hace automáticamente. Para la accesibilidad, debemos asegurar que la rebosadora sea operable y anuncia su estado. y ] ya añadimos ayuda a los lectores de pantalla.
Además, proporciona un indicador de enfoque visible para los usuarios del teclado. El esquema predeterminado funciona, pero puede estilo para que coincida con su marca:
.toggle-label input:focus-visible + .toggle-slider {
outline: 2px solid #007bff;
outline-offset: 4px;
}
Prueba la tabla de precios con un lector de pantalla para verificar que se anuncian cambios de precio. Para mejorar esto, después de actualizar el precio, puede utilizar una región en vivo:
<div aria-live="polite" aria-atomic="true" class="sr-only"></div>
Luego, en JavaScript, establece su texto a algo como “Precios actualizados a facturación anual” después de cada rebote. Este elemento de pantalla sólo anunciará el cambio sin interrupción visual.
Añadiendo más características dinámicas
Una vez que el núcleo de la palanca funciona, puede ampliar la tabla de precios con interactividad adicional:
Moda de la característica
Agregue las casillas de verificación para add‐ons (por ejemplo, almacenamiento adicional, miembros del equipo) que actualizan el precio total. Almacene los precios de base y añada los costos en un objeto. Cuando una casilla de verificación cambia, vuelva a calcular el total para cada tarjeta en consecuencia.
Selector de divisas
Si su audiencia es global, agregue una desplegable para cambiar entre USD, EUR, GBP, etc. Almacene las tasas de conversión y multiplifique el precio base. Actualice el texto con formato de moneda adecuado utilizando .
Contrata de precios animados
Para un efecto pulido, anima el número desde el valor antiguo hasta el nuevo valor de unos pocos cientos milisegundos. Las bibliotecas como animate.css[] no son necesarias — un simple útero funciona. Sin embargo, manténgalo performant: sólo anima las tarjetas visibles.
Ahorros de Badge
Cuando la facturación anual se rebosa, muestre una placa “Guardar 20%” junto al precio. Calcula los ahorros como y mostrarlo dinámicamente. Agregue un atributo a cada tarjeta en el HTML y permita que JavaScript lo popule o computa en la mosca.
Consideraciones de la ejecución
Con sólo unas pocas tarjetas de precios, el rendimiento no es una preocupación. Pero si usted tiene docenas de planes (por ejemplo, SaaS que genera planes de una base de datos), considere estas optimizaciones:
- Use la delegación del evento: Adjunte un oyente del evento a un contenedor padre en lugar de cada tarjeta. Para la rebosa, eso ya está hecho. Para las casillas de verificación dentro de las tarjetas, delegado.
- Cache DOM references: En la inicialización, almacena referencias a los elementos y en una matriz o Mapa por clave del plan. Entonces, al actualizar, no necesita volver a preguntar al DOM.
- Use para animaciones:] Programar actualizaciones visuales para el próximo ciclo de pintura para evitar el estruendo de diseño.
- Minimizar reflujos: Batch DOM cambia al actualizar múltiples tarjetas. En nuestra función leemos y escribimos al DOM en un único bucle —eso está bien. Si agregas clases o estilos de línea, considera leer primero todos los valores, luego escribir todos los cambios.
Casos y ejemplos de uso real-mundial
Las tablas de precios dinámicos se utilizan en todas partes de servicios de suscripción como Dropbox y Spotify a proveedores de alojamiento como DigitalOcean. Un patrón común es ofrecer un ahorro mensual/año con una etiqueta de “Mejor valor” en el próximo año de $.
Otra variación es escalas deslizantes para la cantidad. Una tabla de precios para un producto SaaS puede tener deslizadores para el número de usuarios o almacenamiento. Como el usuario mueve el deslizador, las actualizaciones de precios en vivo. CSS Grid maneja el diseño, y JavaScript actualiza los valores de precio. Esto se puede construir con los mismos principios: los niveles de precios de almacén, escuchar cambios de entrada, y actualizar el DOM.
Para un enfoque más avanzado, usted podría cargar datos de precios de un archivo JSON o API, haciendo la tabla realmente basado en datos. Si su sitio está construido con un CMS sin cabeza como Directus, los datos de precios vendrían de una colección, y su inicio JavaScript lo buscaría y renderizaría la red dinámicamente. Este enfoque mantiene el precio en sincronización con su backend.
Soporte de prueba y navegador
CSS Grid es compatible con todos los navegadores modernos, incluyendo Internet Explorer 11 con el prefijo , aunque recomendamos utilizar Autoprefixer o ignorar IE11 para nuevos proyectos. El JavaScript utilizado aquí (ES6 , , funciones de flecha, , plantillas literales
Enfoque alternativo: CSS Grid con propiedades personalizadas CSS
Un método interesante para evitar JavaScript para reducir los precios es utilizar las propiedades personalizadas de CSS y (cuando el soporte del navegador madura). Usted establecería los precios como etc., y cambiarlos por agrupar una clase en el cuerpo. Sin embargo, para la verdadera lógica dinámica (por ejemplo, añadir dos add-ons), JavaScript es todavía necesario. La combinación de ambos — CSS para la distribución y transiciones robustas.
Conclusión
Construir una tabla de precios dinámicos con JavaScript y CSS Grid es factible y se extiende. Al comenzar con HTML semántico, estructurando la rejilla con CSS Grid, y añadiendo JavaScript directo para la interactividad, creas una herramienta que puede adaptarse a modelos de precios complejos sin dependencias hinchadas.
Ahora implemente estos patrones en su propio proyecto. Sus usuarios apreciarán la claridad, y sus tasas de conversión pueden agradecerle.