Un tema visual propio para el blog¶
La documentación de clase y el blog comparten el mismo sitio MkDocs, pero no tienen por qué tener la misma cara. La documentación usa el tema Material con su paleta azul habitual; el blog, en cambio, está pensado para tener un aspecto más cercano a un diario de desarrollo: tipografía monoespaciada, fondo oscuro opcional y cabeceras al estilo terminal. En este artículo vamos a ver cómo conseguir eso sin tocar el tema global ni romper el comportamiento del sitio al navegar entre secciones.
El problema: no hay CSS por sección en MkDocs¶
MkDocs no ofrece un mecanismo nativo para cargar estilos solo en determinadas secciones. Los ficheros declarados en extra_css se cargan en todas las páginas del sitio. Por tanto, la única forma de aislar un tema visual a la sección de blog es escribir CSS que solo se active cuando el <body> tenga una clase concreta — en nuestro caso, blog-page.
El flujo es sencillo: un fichero JavaScript comprueba la URL actual y añade o quita esa clase; el CSS la usa como selector raíz para que sus reglas sean completamente inofensivas en el resto del sitio.
Inyectar la clase con JavaScript¶
El script blog-class.js es el encargado de esa lógica. Comprueba si la ruta actual empieza por /blog y, según el resultado, añade o quita la clase blog-page del <body>:
function applyBlogClass() {
var isBlog = /\/blog(\/|$)/.test(location.pathname);
if (document.body) {
document.body.classList.toggle("blog-page", isBlog);
}
}
applyBlogClass();
if (typeof document$ !== "undefined") {
document$.subscribe(applyBlogClass);
}
La función se ejecuta una vez al cargar el script — lo que cubre la carga inicial de la página. Pero MkDocs Material tiene la navegación instantánea activada (navigation.instant): cuando el usuario hace clic en un enlace, el contenido cambia sin recargar el navegador. En ese caso, la función solo se ejecutaría una vez y la clase quedaría fija en el valor de la primera página visitada. Para evitarlo, nos suscribimos al observable document$ que Material expone: emite un evento en cada transición de página, de forma que la función se ejecuta de nuevo y la clase se actualiza correctamente.
El CSS: paleta GitHub y estilo terminal¶
Con la clase blog-page como selector raíz, el fichero blog-terminal.css puede contener un tema visual completamente distinto sin interferir con el resto del sitio. La paleta está inspirada en GitHub: fondo casi negro (#0d1117) y verde terminal (#3fb950) para el modo oscuro, blanco puro con verde más apagado (#1a7f37) para el modo claro.
Lo primero que hace el fichero es definir las variables de color y tipografía como propiedades CSS personalizadas, usando el esquema de color de Material como discriminador:
/* === Variables — dark mode === */
.blog-page[data-md-color-scheme="slate"] {
--blog-bg: #0d1117;
--blog-bg-elev: #161b22;
--blog-border: #30363d;
--blog-text: #c9d1d9;
--blog-text-dim: #8b949e;
--blog-accent: #3fb950;
--blog-link: #58a6ff;
}
/* === Variables — light mode === */
.blog-page,
.blog-page[data-md-color-scheme="default"] {
--blog-bg: #ffffff;
--blog-bg-elev: #f6f8fa;
--blog-border: #d0d7de;
--blog-text: #1f2328;
--blog-text-dim: #656d76;
--blog-accent: #1a7f37;
--blog-link: #0969da;
}
Esas variables se mapean después a las propiedades internas de Material (--md-default-bg-color, --md-typeset-a-color, etc.), así todos los componentes del tema adoptan la nueva paleta sin tener que sobrescribir selector a selector.
Para las tipografías importamos JetBrains Mono de Google Fonts — para títulos, navegación y código — e Inter para el cuerpo del texto. El objetivo es reforzar la sensación de entorno de desarrollo frente al tono más académico de los apuntes.
Cabeceras al estilo terminal¶
Uno de los detalles más visibles son los prefijos de los encabezados. Cada nivel de título tiene un pseudo-elemento ::before que inserta un símbolo distinto en verde terminal:
h1inserta$— como el prompt de una shellh2inserta#— como un comentario de shell o encabezado Markdownh3inserta>>— como redirección de salida
El h1 además va dentro de un recuadro con fondo elevado y un borde izquierdo verde de 3px, que lo hace destacar visualmente como si fuera el título de una ventana de terminal.
El fix de alineación de números de línea¶
Al activar linenums_style: table en pymdownx.highlight, Pygments genera la numeración en una celda de tabla separada de la del código. Eso introduce un problema: la celda td.linenos tiene un padding-top que la celda del código no tiene, así que los números empiezan visualmente más abajo que la primera línea. Además, si el tamaño de fuente o el interlineado difieren aunque sea mínimamente entre las dos celdas, el desajuste crece con cada línea.
La solución es forzar exactamente los mismos valores calculados en las dos columnas. Los valores concretos se obtuvieron con getComputedStyle desde la consola del navegador y se aplican como píxeles absolutos:
.blog-page .md-typeset .highlighttable td.linenos {
padding-top: 0 !important;
padding-bottom: 0 !important;
}
.blog-page .md-typeset .highlighttable td.linenos,
.blog-page .md-typeset .highlighttable .linenodiv,
.blog-page .md-typeset .highlighttable .linenodiv pre,
.blog-page .md-typeset .highlighttable .linenodiv pre span {
font-size: 13.94px;
line-height: 19.5167px;
}
Usar píxeles absolutos en lugar de unidades relativas es deliberado: cualquier variación porcentual entre la celda de números y la del código volvería a producir el desajuste, aunque fuera mínima.
Registrar los ficheros en mkdocs.yml¶
Los dos ficheros se declaran en mkdocs.yml para que MkDocs los incluya en todas las páginas:
Aunque se cargan globalmente, el CSS es inerte fuera del blog — todos sus selectores empiezan por .blog-page. El JavaScript también es inofensivo: solo añade o quita una clase en función de la URL, sin efectos secundarios sobre el resto del sitio.
Con esto, la sección de blog tiene una identidad visual completamente independiente del resto del sitio, sin necesidad de configuración adicional ni de mantener dos proyectos MkDocs separados. El truco es sencillo: una clase en el <body> controlada por un script de doce líneas, y un CSS que la usa como interruptor.