
Aquí tienes una versión avanzada, con animación suave, cambio de color según el progreso, soporte para “prefiere reducir movimiento”, cálculo sobre el área de lectura (artículo) y alto rendimiento (usa transform + requestAnimationFrame). Incluye todo: PHP para WordPress, CSS y JavaScript.
Código HTML para añadir en WordPress
Código para functions.php (tema hijo recomendado):
<?php
/**
* Barra de progreso de lectura: salida HTML, estilos y encolado del JS.
*/
// 1) Inserta el contenedor de la barra lo antes posible en el <body>
function rp_output_progress_bar() {
// role="progressbar" + atributos ARIA para accesibilidad
echo '<div id="reading-progress" role="progressbar" aria-label="Progreso de lectura" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"></div>';
}
add_action('wp_body_open', 'rp_output_progress_bar'); // requiere WP 5.2+. Si tu tema no llama a wp_body_open, cambia a wp_footer.
// 2) Estilos (puedes mover esto a style.css si prefieres)
function rp_progress_bar_styles() {
?>
<style>
/* Contenedor de la barra */
#reading-progress{
position: fixed;
top: 0;
left: 0;
height: 6px;
width: 100%;
/* Usamos transform en lugar de width por rendimiento (GPU-friendly) */
transform: scaleX(0);
transform-origin: left center;
/* Color dinámico con variable, por defecto azul */
--bar-color: #0ea5e9;
background: linear-gradient(90deg, var(--bar-color), var(--bar-color));
box-shadow: 0 2px 10px rgba(0,0,0,.18), 0 0 12px color-mix(in srgb, var(--bar-color) 60%, transparent);
border-bottom-right-radius: 12px;
border-top-right-radius: 12px;
z-index: 9999;
transition: transform 120ms ease-out, background-color 200ms linear, box-shadow 200ms linear;
/* Suave relieve visual */
backdrop-filter: saturate(120%) blur(0.5px);
}
/* Modo reducido de movimiento */
@media (prefers-reduced-motion: reduce){
#reading-progress{ transition: none; }
}
/* Evita tapar menús fijos altos: desplázala un poco si tienes admin bar */
@media screen and (min-width: 782px){
body.admin-bar #reading-progress{ top: 32px; }
}
</style>
<?php
}
add_action('wp_head', 'rp_progress_bar_styles');
// 3) Encola el script (crea el archivo /reading-progress.js en tu tema hijo)
function rp_enqueue_progress_script() {
// Dependencias vacías; en pie de página para no bloquear
wp_enqueue_script(
'reading-progress',
get_stylesheet_directory_uri() . '/reading-progress.js',
array(),
'1.0.0',
true
);
// Selector del área de lectura: ajusta si tu tema usa otros
$config = array(
'selectors' => array('article', '.entry-content', 'main', '#primary'),
'minHeight' => 400 // píxeles mínimos para activar barra
);
wp_add_inline_script('reading-progress', 'window.__READING_PROGRESS_CONFIG__ = ' . wp_json_encode($config) . ';', 'before');
}
add_action('wp_enqueue_scripts', 'rp_enqueue_progress_script');
Código Javascript para añadir al Wordpress
Contenido de reading-progress.js (guárdalo en la raíz del tema hijo o donde apuntaste arriba):
(function () {
'use strict';
const cfg = (window.__READING_PROGRESS_CONFIG__) || {};
const selectors = cfg.selectors || ['article', '.entry-content', 'main', '#primary'];
const MIN_HEIGHT = typeof cfg.minHeight === 'number' ? cfg.minHeight : 400;
const bar = document.getElementById('reading-progress');
if (!bar) return;
// Encuentra el contenedor de lectura más pertinente
function findReadingTarget() {
for (const sel of selectors) {
const el = document.querySelector(sel);
if (el && el.getBoundingClientRect) {
const h = el.scrollHeight || el.offsetHeight || 0;
if (h >= MIN_HEIGHT) return el;
}
}
// fallback: documento entero
return document.documentElement;
}
let target = findReadingTarget();
// Métricas para cálculo de progreso
let startY = 0; // posición inicial del contenedor
let endY = 0; // posición final "leíble" (parte inferior menos viewport)
let range = 1; // endY - startY (evitar division by zero)
let rafPending = false;
function clamp01(x) { return Math.max(0, Math.min(1, x)); }
function recalc() {
const rect = target === document.documentElement
? { top: 0, height: document.documentElement.scrollHeight }
: target.getBoundingClientRect();
// top absoluto del target
const targetTop = (target === document.documentElement)
? 0
: (window.scrollY + rect.top);
const targetHeight = (target === document.documentElement)
? document.documentElement.scrollHeight
: target.scrollHeight || rect.height;
const viewport = window.innerHeight || document.documentElement.clientHeight || 0;
startY = targetTop;
endY = targetTop + targetHeight - viewport;
range = Math.max(1, endY - startY);
update(); // actualiza inmediatamente tras recalcular
}
function colorForProgress(p) {
// De verde (#22c55e) a amarillo (#eab308) a rojo (#ef4444)
// p: 0..1. Interpolamos en dos tramos.
const stops = [
{ p: 0.0, r: 34, g:197, b: 94 }, // verde #22c55e
{ p: 0.5, r:234, g:179, b: 8 }, // amarillo #eab308
{ p: 1.0, r:239, g: 68, b: 68 } // rojo #ef4444
];
let c1 = stops[0], c2 = stops[2];
if (p <= 0.5) { c1 = stops[0]; c2 = stops[1]; p = p / 0.5; }
else { c1 = stops[1]; c2 = stops[2]; p = (p - 0.5) / 0.5; }
const r = Math.round(c1.r + (c2.r - c1.r) * p);
const g = Math.round(c1.g + (c2.g - c1.g) * p);
const b = Math.round(c1.b + (c2.b - c1.b) * p);
return `rgb(${r}, ${g}, ${b})`;
}
function update() {
rafPending = false;
const y = window.scrollY || window.pageYOffset || 0;
const p = clamp01((y - startY) / range);
// Escala de la barra
bar.style.transform = `scaleX(${p})`;
// Color y brillo dinámicos
const c = colorForProgress(p);
bar.style.setProperty('--bar-color', c);
bar.style.boxShadow = `0 2px 10px rgba(0,0,0,.18), 0 0 ${8 + 16*p}px ${c}`;
// ARIA
const val = Math.round(p * 100);
bar.setAttribute('aria-valuenow', String(val));
}
function onScroll() {
if (!rafPending) {
rafPending = true;
window.requestAnimationFrame(update);
}
}
// Recalcula al cargar y cuando cambie el layout
window.addEventListener('load', recalc);
window.addEventListener('resize', () => { recalc(); });
document.addEventListener('readystatechange', () => {
if (document.readyState === 'interactive') recalc();
});
// Observa cambios en el tamaño del objetivo (contenido que se expande, comentarios, etc.)
let resizeObs;
if ('ResizeObserver' in window) {
resizeObs = new ResizeObserver(() => recalc());
if (target && target !== document.documentElement) resizeObs.observe(target);
}
// Si el target no era válido al principio, intenta encontrarlo cuando el DOM cambie
const mo = new MutationObserver(() => {
const newTarget = findReadingTarget();
if (newTarget !== target) {
if (resizeObs && target && target !== document.documentElement) resizeObs.unobserve(target);
target = newTarget;
if (resizeObs && target && target !== document.documentElement) resizeObs.observe(target);
recalc();
}
});
mo.observe(document.documentElement, { childList: true, subtree: true });
// Scroll listener
window.addEventListener('scroll', onScroll, { passive: true });
// Primer cálculo inmediato
recalc();
})();
Añadir código CSS para WordPress
Alternativa si prefieres llevar los estilos a style.css (en vez de inyectarlos desde PHP):
/* style.css (opcional si no usas la inyección en wp_head) */
#reading-progress{
position: fixed;
top: 0;
left: 0;
height: 6px;
width: 100%;
transform: scaleX(0);
transform-origin: left center;
--bar-color: #0ea5e9;
background: linear-gradient(90deg, var(--bar-color), var(--bar-color));
box-shadow: 0 2px 10px rgba(0,0,0,.18), 0 0 12px color-mix(in srgb, var(--bar-color) 60%, transparent);
border-bottom-right-radius: 12px;
border-top-right-radius: 12px;
z-index: 9999;
transition: transform 120ms ease-out, background-color 200ms linear, box-shadow 200ms linear;
backdrop-filter: saturate(120%) blur(0.5px);
}
@media (prefers-reduced-motion: reduce){
#reading-progress{ transition: none; }
}
body.admin-bar #reading-progress{ top: 32px; }
Notas rápidas:
- Si tu tema no llama a
wp_body_open, cambia eladd_action('wp_body_open', ...)poradd_action('wp_footer', ...)para imprimir la barra al final del body. - Ajusta los selectores en
__READING_PROGRESS_CONFIG__si tu tema usa otro contenedor principal de artículo. - El color progresa de verde→amarillo→rojo a medida que avanzas, y la animación se suaviza; se desactiva si el usuario tiene “reducir movimiento”.
Si quieres que lo adapte a tu tema concreto (por ejemplo, Astra, GeneratePress, Divi, etc.) o que la barra evite solaparse con un header fijo personalizado no lo dudes y suscribete a algunos de nuestros Planes de Optimizacion WordPress. Nos vemos!
Categoría: CSS personalizado para WordPress