Saltar al contenido

davidatb.com: el tema, el plugin y lo que lo vigila

Qué resuelve

davidatb.com es un WordPress. El trabajo de este proyecto no es que el sitio se vea bien: es que no afirme nada que no pueda comprobar por sí mismo.

Los contadores de la portada son el caso corto. Eran tres números escritos a mano —60+, 220+, 22K— y publicar no los movía. Lo que abrió ese encargo fue directo: en palabras de David, los números eran estáticos, los commits no sirven como métrica y los había puesto como placeholder hacía años. Hoy hay cuatro, y los cuatro se calculan al servir la página, y el mismo criterio ordena el resto: tema y plugin escritos a mano, compuertas que se ven rojas antes de verse verdes, y un despliegue verificado archivo por archivo.

Que la banda es dinámica de verdad tiene una prueba con fecha: el 28 de agosto de 2026 se retiraron dos artículos del sitio y el contador pasó de 25 a 23 sin tocar una línea de código, comprobado en el HTML servido.

Cómo está construido, y por qué así

Tema clásico propio, y ningún constructor dentro del tema

noctra-davidatb es un tema clásico de WordPress escrito en PHP: header.php, single.php, archive.php y compañía. La regla está escrita en la cabecera de functions.php y es una restricción, no un eslogan: las plantillas no componen ni reinterpretan la página. El contenido manda sobre su propio layout.

Eso no significa que el sitio no tenga constructor: Elementor sigue instalado (comprobado el 28 de agosto de 2026). El tema se aparta cuando lo encuentra: detecta esas páginas por su metadato _elementor_edit_mode y les retira la anchura y la cabecera que impone al resto.

No hay nada que compilar

En todo el árbol no hay un solo package.json: ni paso de build, ni node_modules, ni bundle. Los componentes interactivos son islas de React montadas sobre window.wp.element, el React que WordPress ya trae. No se descarga una segunda copia.

Esa restricción se defendió cuando estorbó. El fondo animado de la portada es un port de DotField, de ReactBits, reescrito a IIFE, createElement y useEffect sobre el React que ya trae WordPress: fuera GSAP, fuera InertiaPlugin, ningún bundle nuevo. El aviso de licencia del original viaja en la cabecera del port. El CSS va troceado en siete hojas que se encolan por superficie.

Ninguno de los cuatro KPI está escrito a mano

Artículos y proyectos se cuentan con wp_count_posts. Las áreas temáticas son las categorías raíz no vacías menos la predeterminada, leída por ID con get_option('default_category') y no por slug, para que renombrarla no cambie la cuenta. De los años se guardan dos constantes, el año y el mes de inicio; la cifra es la resta contra la fecha de WordPress. El día no se guarda a propósito, porque el mes es aproximado.

El valor final lo imprime PHP en el HTML: si el JavaScript no llega a ejecutarse, se ve el número, no un cero ni un hueco. La animación arranca al entrar en pantalla, una sola vez, y con prefers-reduced-motion no arranca.

La parte que costó no es contar, es no publicar un cero falso. La primera versión confiaba en is_wp_error(), y ante un error de SQL no fatal wp_count_posts() no propaga ningún error: convierte el resultado vacío en un objeto con todos los estados a cero, y encima lo cachea. La banda habría publicado «0 artículos publicados» como si fuera un dato. Ahora se compara $wpdb->last_error antes y después de cada consulta; si cambió, ese KPI vale null y desaparece de la banda en vez de mentir. Si no sobrevive ninguno, la banda no se pinta.

Las compuertas viven en ganchos de git, no en un servicio

Ninguno de los dos repositorios tiene .github/workflows. La razón está escrita en el informe de esa tanda: las compuertas son locales y no consumen minutos de GitHub Actions.

En pre-commit va lo barato. Un escáner de secretos que, para lo ambiguo, no busca «esto parece una contraseña» sino los valores literales del .env y de las claves de ~/.ssh: dos intentos con patrones heurísticos dieron 25 y 20 falsos positivos sobre prosa en español, y buscar el valor exacto tiene cero por construcción. Los patrones solo sobreviven para cuatro formatos que no admiten duda, como la cabecera de una clave privada. Lee los bytes del índice siempre que el archivo ya esté staged, para cazar un secreto ya preparado para el commit aunque se haya borrado del archivo. Y un comprobador de fines de línea, que no es cosmética: el despliegue se verifica por md5sum, y con finales mezclados los mismos archivos dan hash distinto siendo idénticos línea a línea.

En pre-push va lo caro, que se paga una vez al publicar: una compuerta diferencial del CSS del plugin contra la base remota que git entrega por su entrada estándar, y no contra HEAD~1, que se saltaría una regresión introducida dos commits antes. Comprueba selectores y declaraciones perdidos, que cada clase que emite el PHP tenga una regla donde sea sujeto y no una subcadena, y los colores fuera de var(), reservas incluidas.

El banco de mutaciones

Una compuerta que nadie ha visto roja no es una compuerta. El pre-push ejecuta un banco que rompe el CSS a propósito de 18 formas distintas y exige el resultado de cada una: 16 roturas que tienen que salir rojas y 2 controles negativos —un color dentro de un comentario, un comentario cambiado— que tienen que salir verdes, contados el 4 de septiembre de 2026. Una compuerta que se pone roja con todo tampoco discrimina.

Entre las 18: bajar la especificidad del botón de (0,3,0) a (0,2,0), que es el fallo real que perdía contra el (0,2,1) del tema; borrar el @media de 1040 px, con lo que tres y cuatro columnas colapsan a dos; estropear la reserva de un var() dejando el token bien. El push se detiene si el banco no termina con cero escapes. La compuerta anterior dejaba pasar 14 de 22 mutaciones —el número que dejó escrito el informe de esa tanda— y se reescribió por eso.

El despliegue se verifica, no se supone

No hay git en el servidor. El tema se copia con scp y se comprueba con md5sum dos veces: antes, contra el commit base, para no pisar cambios que no están en el repositorio; y después, para saber que llegó lo que querías. Luego revisión de sintaxis, purga de las tres capas de caché y comprobación en el HTML servido.

Qué hace y qué no hace

Lo que sí: tema y plugin propios sin nada que compilar; contadores que degradan a nada antes que a un cero; secretos y fines de línea comprobados en cada commit, y el contrato del CSS del plugin en cada push. Y la enumeración de usuarios cerrada —/wp-json/wp/v2/users devolvía sin autenticar el id, el nombre real completo y el slug de autor—, en un mu-plugin que sobrevive a un cambio de tema.

Lo que no:

  • Las compuertas son locales. Corren en la máquina donde están instaladas y un --no-verify las salta. No hay nada en el servidor que las vuelva a comprobar.
  • Los ganchos viven en el repositorio del plugin. El tema es otro repositorio y todavía no tiene los suyos.
  • El despliegue se dispara a mano. Nada sincroniza repositorio y producción por sí solo: la deriva se detecta con un script, no se impide.
  • El contenido de la portada vive en tres superficies —la página en la base de datos, la semilla del tema y un patrón—, y hay que tocar las tres o un re-sembrado revierte el cambio.
  • Los dos repositorios son privados. Se puede ver el resultado; el código, no.

El producto

Es esta página: el tema pinta lo que estás leyendo, y la rejilla de /portafolio/ la sirve el plugin.

davidatb.com

Captura de la portada de davidatb.com