SEO: metadatos, datos estructurados y encabezados semánticos
All checks were successful
Zola / build-and-deploy (push) Successful in 17s

- <html lang="es"> en lugar de "en"
- <title> con sufijo del sitio, canonical, meta robots y author
- Open Graph completo (url, type, locale, image, article:*) y Twitter Card
- JSON-LD: WebSite + Person en portada, BlogPosting en cada post
- rel="me" hacia Mastodon/GitHub/X
- Imagen para redes (og-image.png) y favicon
- Títulos de página como <h1> (antes <div>); listados y modal de búsqueda en <h2>
- Notas sueltas con noindex y fuera del sitemap; sin paginación duplicada
- Páginas de etiquetas y 404 en español, con títulos propios
- alt/width/height/lazy en los badges de la portada
- MathJax solo cuando un post lo pida ([extra] mathjax = true)
- Descripción en /posts y etiquetas en "Hola Mundo"
- Limpieza de ficheros de respaldo del editor + .gitignore

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Alejandro Guerrero 2026-09-25 12:10:08 +02:00
parent 1d74bceae8
commit 00dc194e86
22 changed files with 714 additions and 26 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
public/
*~
.*.un~

View file

@ -22,17 +22,26 @@ include_content = true
index_format = "elasticlunr_json" index_format = "elasticlunr_json"
[extra] [extra]
# SEO
site_name = "Alejandro GS"
author = "Alejandro GS"
locale = "es_ES"
twitter_handle = "@alejandro_gs73"
og_image = "og-image.png"
favicon = "favicon.png"
stylesheets = ["css/seo.css"]
theme = "toggle" theme = "toggle"
fancy_code = true fancy_code = true
fediverse = true fediverse = true
fediverse_creator = "@alejandrogs73@fosstodon.org" fediverse_creator = "@alejandrogs73@fosstodon.org"
mathjax = true mathjax = false # activar por post con [extra] mathjax = true
mathjax_dollar_inline_enable = true mathjax_dollar_inline_enable = true
socials = [ socials = [
{ name = "twitter", url = "https://twitter.com/alejandro_gs73", icon = "twitter" }, { name = "twitter", url = "https://x.com/alejandro_gs73", icon = "twitter" },
{ name = "github", url = "https://github.com/alejandrogs73/", icon = "github" }, { name = "github", url = "https://github.com/alejandrogs73", icon = "github" },
{ name = "mastodon", url = "https://fosstodon.org/@alejandrogs73/", icon = "mastodon" }, { name = "mastodon", url = "https://fosstodon.org/@alejandrogs73", icon = "mastodon" },
{ name = "email", url = "mailto:mail@alejandrogs.es", icon = "email" }, { name = "email", url = "mailto:mail@alejandrogs.es", icon = "email" },

View file

@ -7,7 +7,7 @@ path = "about"
> Sobre el autor, la soberanía, la trinchera y las estrellas. > Sobre el autor, la soberanía, la trinchera y las estrellas.
# Lo que hay detrás del teclado ## Lo que hay detrás del teclado
No busques aquí un currículum ni una biografía corporativa. Soy programador, hispanista y, sobre todo, un disidente de la inercia moderna. No busques aquí un currículum ni una biografía corporativa. Soy programador, hispanista y, sobre todo, un disidente de la inercia moderna.
@ -15,7 +15,7 @@ Mi relación con la tecnología nació de la curiosidad, no de la academia. Empe
Esa lección forjó mi carácter: no acepto cajas negras. Esa lección forjó mi carácter: no acepto cajas negras.
## Soberanía o Nada ### Soberanía o Nada
Hoy, mi guerra no es solo aprender lenguajes, sino conquistar la **soberanía tecnológica**. Hoy, mi guerra no es solo aprender lenguajes, sino conquistar la **soberanía tecnológica**.
@ -23,13 +23,13 @@ Vivimos en un feudalismo digital donde alquilamos nuestra vida a grandes corpora
Odio la web moderna: lenta, obesa y llena de espías. Por eso este sitio es un retorno a la esencia. HTML crudo, CSS ligero y respeto por tu tiempo. Lo que ves es lo que hay. Odio la web moderna: lenta, obesa y llena de espías. Por eso este sitio es un retorno a la esencia. HTML crudo, CSS ligero y respeto por tu tiempo. Lo que ves es lo que hay.
## Dios, Patria y Código ### Dios, Patria y Código
La técnica sin espíritu es una jaula. Soy uno de los fundadores y **Coordinador General** del **[Partido Nacional Hispanista](https://partidonacionalhispanista.09072007.xyz)** porque entiendo que la batalla cultural es tan vital como la tecnológica. La técnica sin espíritu es una jaula. Soy uno de los fundadores y **Coordinador General** del **[Partido Nacional Hispanista](https://partidonacionalhispanista.09072007.xyz)** porque entiendo que la batalla cultural es tan vital como la tecnológica.
Busco restaurar una visión del mundo que combine la tradición hispánica con la innovación necesaria, lejos del nihilismo que impera hoy. La política, para mí, no es un juego de escaños, sino la defensa de una cosmovisión. Busco restaurar una visión del mundo que combine la tradición hispánica con la innovación necesaria, lejos del nihilismo que impera hoy. La política, para mí, no es un juego de escaños, sino la defensa de una cosmovisión.
## Alcanzando las estrellas ### Alcanzando las estrellas
Junto a unos camaradas nos propusimos lo imposible: tocar el cielo. Así nació el **Proyecto ONZA Φ**, un intento de cohete de propulsión sólida. De aquel sueño solo llegamos a construir el motor. La falta de recursos y la inexperiencia nos cortaron las alas, pero el golpe de gracia fue un fallo informático que eliminó casi todo el registro documental del proyecto. Solo quedó nuestra memoria. Junto a unos camaradas nos propusimos lo imposible: tocar el cielo. Así nació el **Proyecto ONZA Φ**, un intento de cohete de propulsión sólida. De aquel sueño solo llegamos a construir el motor. La falta de recursos y la inexperiencia nos cortaron las alas, pero el golpe de gracia fue un fallo informático que eliminó casi todo el registro documental del proyecto. Solo quedó nuestra memoria.
@ -45,10 +45,10 @@ Nuestro lema reza:
Una frase que combina la ambición poética con una advertencia muy literal sobre la seguridad en la propulsión sólida. Una frase que combina la ambición poética con una advertencia muy literal sobre la seguridad en la propulsión sólida.
### Nota al pie: Sobre el nombre ONZA #### Nota al pie: Sobre el nombre ONZA
Este nombre surge como homenaje al grupo de rock progresivo ***ONZA*** (ahora ONZAProg), cuya inspiración artística y docente motivó a los fundadores a unir ciencia, arte y espíritu. Este nombre surge como homenaje al grupo de rock progresivo ***ONZA*** (ahora ONZAProg), cuya inspiración artística y docente motivó a los fundadores a unir ciencia, arte y espíritu.
## Zona de Pruebas y Error ### Zona de Pruebas y Error
Este blog no es un escaparate; es un taller con el suelo manchado de aceite. Es mi **zona cero**, mi **búnker** y mi **laboratorio**. Este blog no es un escaparate; es un taller con el suelo manchado de aceite. Es mi **zona cero**, mi **búnker** y mi **laboratorio**.

View file

@ -4,7 +4,7 @@ description = "Frecuencias abiertas y cifradas."
template = "page.html" template = "page.html"
+++ +++
# Protocolo de Comunicación ## Protocolo de Comunicación
No estoy siempre disponible, pero siempre estoy escuchando. Si buscas discutir sobre código, soberanía tecnológica o hispanidad, este es el lugar. No estoy siempre disponible, pero siempre estoy escuchando. Si buscas discutir sobre código, soberanía tecnológica o hispanidad, este es el lugar.
@ -16,7 +16,7 @@ No estoy siempre disponible, pero siempre estoy escuchando. Si buscas discutir s
| 🐦 **Twitter** | [@Alejandro_GS73](https://x.com/alejandro_gs73) | | 🐦 **Twitter** | [@Alejandro_GS73](https://x.com/alejandro_gs73) |
| 🐘 **Mastodon** | [@alejandrogs73@fosstodon.org](https://fosstodon.org/@alejandrogs73) | | 🐘 **Mastodon** | [@alejandrogs73@fosstodon.org](https://fosstodon.org/@alejandrogs73) |
## Criptografía ### Criptografía
Si necesitas enviarme información sensible o verificar que realmente soy yo, utiliza mi clave PGP pública. Si necesitas enviarme información sensible o verificar que realmente soy yo, utiliza mi clave PGP pública.

Binary file not shown.

Binary file not shown.

View file

View file

@ -1,4 +1,7 @@
+++ +++
# Las notas se muestran en la portada. Sus páginas sueltas llevan noindex
# y no salen en el sitemap (ver templates/).
render = false
title = "Notas de Campo" title = "Notas de Campo"
sort_by = "date" sort_by = "date"
template = "section.html" template = "section.html"

View file

@ -1,6 +1,7 @@
+++ +++
paginate_by = 7 paginate_by = 7
title = "Bitácora" title = "Bitácora"
description = "Artículos sobre software libre, soberanía tecnológica, NixOS, self-hosting e ingeniería."
sort_by = "date" sort_by = "date"
insert_anchor_links = "heading" insert_anchor_links = "heading"

View file

@ -3,6 +3,9 @@ title = "Hola Mundo"
date = 2025-12-17 date = 2025-12-17
description = "¿Un blog en 2025? Sí, y hace más falta que nunca" description = "¿Un blog en 2025? Sí, y hace más falta que nunca"
draft = false draft = false
[taxonomies]
tags = ["blog", "software libre", "soberanía digital"]
+++ +++
¿Un blog en 2025? Sí. ¿Un blog en 2025? Sí.

17
static/css/seo.css Normal file
View file

@ -0,0 +1,17 @@
/* Ajustes tras convertir los títulos del tema en encabezados semánticos */
h1.page-header { font-weight: normal; }
h1.page-header::before { content: none; }
.post-list h2.title,
.tag-list h2.title {
margin: 0;
font-weight: normal;
font-family: var(--header-font);
}
.post-list h2.title::before,
.tag-list h2.title::before { content: none; }
.post-list h2.title a,
.tag-list h2.title a { border-bottom: none; }
#modal-content h2#modalTitle { margin-top: 0; margin-bottom: 1rem; font-size: 1.2rem; }
#modal-content h2#modalTitle::before { content: "# "; }

BIN
static/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
static/og-image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

8
templates/404.html Normal file
View file

@ -0,0 +1,8 @@
{% extends "page.html" %}
{% block main_content %}
<main class="not-found-header">
{{ post_macros::page_header(title="404") }}
<p>Esta página no existe. Vuelve a la <a href="{{ config.base_url }}">portada</a> o echa un vistazo a la <a href="{{ get_url(path='posts') }}">bitácora</a>.</p>
</main>
{% endblock main_content %}

41
templates/base.html Normal file
View file

@ -0,0 +1,41 @@
{% import "macros/macros.html" as post_macros %}
<!DOCTYPE html>
<html lang="{{ lang | default(value=config.default_language) }}" class="dark light">
{% include "partials/header.html" %}
<body>
<div class="left-content">
{% block left_content %}
{% endblock left_content %}
</div>
<div class="content">
{% include "partials/nav.html" %}
{# Post page is the default #}
{% block main_content %}
Nothing here?!
{% endblock main_content %}
{% if page.extra.comment is defined %}
{% set show_comment = page.extra.comment %}
{% else %}
{% set show_comment = false %}
{% endif %}
{% if show_comment %}
<div class="giscus">
</div>
{% include "_giscus_script.html" %}
{% endif %}
</div>
<div class="right-content">
{% block right_content %}
{% endblock right_content %}
</div>
</body>
</html>

View file

@ -101,6 +101,8 @@
ul.rrss-list a { text-decoration: none; color: #ccc; transition: color 0.2s; } ul.rrss-list a { text-decoration: none; color: #ccc; transition: color 0.2s; }
ul.rrss-list a:hover { color: #fe8019; } ul.rrss-list a:hover { color: #fe8019; }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* Estilos para el Markdown del Blogroll */ /* Estilos para el Markdown del Blogroll */
.webroll-content ul { list-style: none; padding: 0; margin: 0; } .webroll-content ul { list-style: none; padding: 0; margin: 0; }
.webroll-content li { margin-bottom: 0.5rem; font-family: monospace; font-size: 0.85rem; } .webroll-content li { margin-bottom: 0.5rem; font-family: monospace; font-size: 0.85rem; }
@ -147,10 +149,10 @@
<div class="module-header">:: CONEXIONES ::</div> <div class="module-header">:: CONEXIONES ::</div>
<ul class="rrss-list"> <ul class="rrss-list">
<li><span>📧</span> <a href="mailto:mail@alejandrogs.es">Correo</a></li> <li><span>📧</span> <a href="mailto:mail@alejandrogs.es">Correo</a></li>
<li><span>🛡️</span> <a href="https://git.alejandrogs.es" target="_blank">Forgejo</a></li> <li><span>🛡️</span> <a href="https://git.alejandrogs.es" target="_blank" rel="me noopener">Forgejo</a></li>
<li><span>🐙</span> <a href="https://github.com/alejandrogs73" target="_blank">GitHub</a></li> <li><span>🐙</span> <a href="https://github.com/alejandrogs73" target="_blank" rel="me noopener">GitHub</a></li>
<li><span>🐦</span> <a href="https://x.com/alejandro_gs73" target="_blank">Twitter</a></li> <li><span>🐦</span> <a href="https://x.com/alejandro_gs73" target="_blank" rel="me noopener">Twitter</a></li>
<li><span>🐘</span> <a href="https://fosstodon.org/@alejandrogs73" target="_blank">Mastodon</a></li> <li><span>🐘</span> <a href="https://fosstodon.org/@alejandrogs73" target="_blank" rel="me noopener">Mastodon</a></li>
</ul> </ul>
</div> </div>
</aside> </aside>
@ -174,7 +176,7 @@
{{ page.description | default(value="Sin descripción.") }} {{ page.description | default(value="Sin descripción.") }}
</p> </p>
<div style="margin-top: 1rem;"> <div style="margin-top: 1rem;">
<a href="{{ page.permalink }}" style="font-size: 0.85rem; text-decoration: underline; color: #fe8019;">Leer entrada &rarr;</a> <a href="{{ page.permalink }}" style="font-size: 0.85rem; text-decoration: underline; color: #fe8019;">Leer entrada<span class="visually-hidden">: {{ page.title }}</span> &rarr;</a>
</div> </div>
</article> </article>
{% endfor %} {% endfor %}
@ -188,7 +190,7 @@
<aside class="sidebar-right"> <aside class="sidebar-right">
<a href="/atom.xml" style="display: block; width: 100%; box-sizing: border-box; background: #fe8019; color: #000; text-align: center; padding: 1rem; font-family: monospace; font-weight: bold; font-size: 1.1rem; text-decoration: none; border: 1px solid #fe8019; margin-bottom: 2rem; text-transform: uppercase;"> <a href="/atom.xml" type="application/atom+xml" title="Suscríbete por RSS/Atom" style="display: block; width: 100%; box-sizing: border-box; background: #fe8019; color: #000; text-align: center; padding: 1rem; font-family: monospace; font-weight: bold; font-size: 1.1rem; text-decoration: none; border: 1px solid #fe8019; margin-bottom: 2rem; text-transform: uppercase;">
📡 RSS FEED 📡 RSS FEED
</a> </a>
@ -221,14 +223,14 @@
</div> </div>
<div class="badge-rack"> <div class="badge-rack">
<img src="https://yesterweb.org/no-to-web3/img/roly-saynotoweb3.gif" alt="NoWEB3"/> <img src="https://yesterweb.org/no-to-web3/img/roly-saynotoweb3.gif" alt="Di no a la Web3" width="88" height="31" loading="lazy" decoding="async" />
<img src="https://88x31.kate.pet/madeon_linux.gif" height="31" /> <img src="https://88x31.kate.pet/madeon_linux.gif" alt="Hecho con Linux" width="88" height="31" loading="lazy" decoding="async" />
<img src="https://88x31.kate.pet/mastodon-flat.png" height="31" /> <img src="https://88x31.kate.pet/mastodon-flat.png" alt="Mastodon" width="88" height="31" loading="lazy" decoding="async" />
<img src="https://88x31.kate.pet/visitmini.gif" height="31" /> <img src="https://88x31.kate.pet/visitmini.gif" alt="Visitar" width="88" height="31" loading="lazy" decoding="async" />
<img src="https://88x31.kate.pet/firefox_now.png" height="31" /> <img src="https://88x31.kate.pet/firefox_now.png" alt="Firefox" width="88" height="31" loading="lazy" decoding="async" />
<img src="https://88x31.kate.pet/gamecube.png" height="31" /> <img src="https://88x31.kate.pet/gamecube.png" alt="GameCube" width="88" height="31" loading="lazy" decoding="async" />
<img src="https://88x31.kate.pet/yoshihatchbutton.gif" height="31" /> <img src="https://88x31.kate.pet/yoshihatchbutton.gif" alt="Yoshi" width="88" height="31" loading="lazy" decoding="async" />
<img src="https://diegologs.com/assets/images/banners/neovim.gif" height="31" /> <img src="https://diegologs.com/assets/images/banners/neovim.gif" alt="Neovim" width="88" height="31" loading="lazy" decoding="async" />
</div> </div>

View file

@ -0,0 +1,55 @@
{% macro list_post(page) %}
<li class="list-item">
<section>
<div class="post-header">
<time>{{ page.date | date(format="%Y-%m-%d") }}</time>
<div>
<h2 class="title">
<a href={{ page.permalink | safe }}>{{ page.title }}</a>
{% if page.draft %}
<span class="draft-label">DRAFT</span>
{% endif %}
</h2>
<div class="meta">
<div class="description">
{% if page.description %}
{{ page.description }}
{% elif page.summary %}
{{ page.summary | safe }}&hellip;
{% else %}
{% set hide_read_more = true %}
{% endif %}
</div>
{% if not hide_read_more %}
<a class="readmore" href={{ page.permalink | safe }}>Read more ⟶</a>
{% endif %}
</div>
</div>
</div>
</section>
</li>
{% endmacro list_post %}
{% macro list_posts(pages) %}
<ul>
{%- for page in pages %}
{{ post_macros::list_post(page=page) }}
{% endfor -%}
</ul>
{% endmacro list_posts %}
{% macro page_header(title) %}
{% if title %}
<h1 class="page-header">
{{ title }}
</h1>
{% endif %}
{% endmacro page_header %}
{% macro site_title() %}
{{ config.title | default(value="Home") }}
{% endmacro %}

View file

@ -0,0 +1,396 @@
{% import "macros/macros.html" as post_macros %}
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="base" content="{{ config.base_url }}" />
{% if config.extra.fediverse %}
<meta name="fediverse:creator"
content="{{ config.extra.fediverse_creator }}" />
{% endif %}
{% if page.extra.meta %}
<!-- the meta data config goes here -->
{% for data in page.extra.meta %}
<meta {% for key, value in data %} {% if key == "property" and value == "og:title" %} {% set_global page_has_og_title = true -%} {% endif %}
{% if key == "property" and value == "og:description" %} {% set_global page_has_og_description = true -%} {% endif %}
{% if key == "name" and value == "description" %} {% set_global page_has_description = true -%} {% endif %}
{{ key }}
None="{{ value }}"
{% endfor %} />
{% endfor %}
{% endif %}
{# ===== SEO ===== #}
{% set is_404 = current_path is not defined %}
{% set current_path = current_path | default(value="/") %}
{% set site_name = config.extra.site_name | default(value=config.title) %}
{% set author = config.extra.author | default(value=site_name) %}
{# Título #}
{% if is_404 %}
{% set_global seo_title = "Página no encontrada | " ~ site_name %}
{% elif current_path == "/" %}
{% set_global seo_title = site_name ~ " — " ~ config.description | trim_end_matches(pat=".") %}
{% elif term %}
{% set_global seo_title = "Entradas sobre «" ~ term.name ~ "» | " ~ site_name %}
{% elif taxonomy %}
{% set_global seo_title = "Etiquetas | " ~ site_name %}
{% elif page.title %}
{% set_global seo_title = page.title ~ " | " ~ site_name %}
{% elif section.title %}
{% set_global seo_title = section.title ~ " | " ~ site_name %}
{% else %}
{% set_global seo_title = site_name %}
{% endif %}
{# Descripción #}
{% if page.description %}
{% set_global seo_description = page.description %}
{% elif section.description %}
{% set_global seo_description = section.description %}
{% elif term %}
{% set_global seo_description = "Artículos de " ~ site_name ~ " sobre " ~ term.name ~ "." %}
{% elif taxonomy %}
{% set_global seo_description = "Todas las etiquetas del blog de " ~ site_name ~ "." %}
{% else %}
{% set_global seo_description = config.description %}
{% endif %}
{# URL canónica #}
{% if page.permalink %}
{% set_global seo_url = page.permalink %}
{% elif section.permalink %}
{% set_global seo_url = section.permalink %}
{% elif term %}
{% set_global seo_url = term.permalink %}
{% else %}
{% set_global seo_url = current_url | default(value=config.base_url) %}
{% endif %}
{# Imagen para redes sociales #}
{% if page.extra.image %}
{% set_global seo_image = get_url(path=page.extra.image) %}
{% elif section.extra.image %}
{% set_global seo_image = get_url(path=section.extra.image) %}
{% elif config.extra.og_image %}
{% set_global seo_image = get_url(path=config.extra.og_image) %}
{% else %}
{% set_global seo_image = "" %}
{% endif %}
{% set is_article = page.date and page.components and page.components | first == "posts" %}
<title>{{ seo_title }}</title>
{% if not is_404 %}<link rel="canonical" href="{{ seo_url | safe }}" />{% endif %}
<meta name="author" content="{{ author }}" />
{% set is_nota = page.components and page.components | first == "notas" %}
{% if page.extra.noindex or section.extra.noindex or is_404 or is_nota %}
<meta name="robots" content="noindex, follow" />
{% else %}
<meta name="robots" content="index, follow, max-image-preview:large" />
{% endif %}
{% if not page_has_description %}
<meta name="description" content="{{ seo_description }}" />
{% endif %}
{# Open Graph #}
<meta property="og:site_name" content="{{ site_name }}" />
<meta property="og:locale" content="{{ config.extra.locale | default(value='es_ES') }}" />
<meta property="og:url" content="{{ seo_url | safe }}" />
{% if not page_has_og_title %}
<meta property="og:title" content="{% if page.title %}{{ page.title }}{% else %}{{ seo_title }}{% endif %}" />
{% endif %}
{% if not page_has_og_description %}
<meta property="og:description" content="{{ seo_description }}" />
{% endif %}
{% if seo_image %}
<meta property="og:image" content="{{ seo_image | safe }}" />
<meta property="og:image:alt" content="{{ seo_title }}" />
{% endif %}
{% if is_article %}
<meta property="og:type" content="article" />
<meta property="article:published_time" content="{{ page.date | date(format='%Y-%m-%d') }}" />
{% if page.updated %}
<meta property="article:modified_time" content="{{ page.updated | date(format='%Y-%m-%d') }}" />
{% endif %}
<meta property="article:author" content="{{ author }}" />
{% if page.taxonomies.tags %}
{% for tag in page.taxonomies.tags %}
<meta property="article:tag" content="{{ tag }}" />
{% endfor %}
{% endif %}
{% else %}
<meta property="og:type" content="website" />
{% endif %}
{# Twitter / X #}
<meta name="twitter:card" content="{% if seo_image %}summary_large_image{% else %}summary{% endif %}" />
{% if config.extra.twitter_handle %}
<meta name="twitter:site" content="{{ config.extra.twitter_handle }}" />
<meta name="twitter:creator" content="{{ config.extra.twitter_handle }}" />
{% endif %}
{# Verificación de identidad (Mastodon, IndieWeb) #}
{% set_global same_as = [] %}
{% for social in config.extra.socials | default(value=[]) %}
{% if social.url is not starting_with("mailto:") %}
{% set_global same_as = same_as | concat(with=social.url) %}
<link rel="me" href="{{ social.url }}" />
{% endif %}
{% endfor %}
{# Datos estructurados (schema.org) #}
{% if current_path == "/" and not is_404 %}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebSite",
"name": {{ site_name | json_encode | safe }},
"url": {{ config.base_url | json_encode | safe }},
"description": {{ config.description | json_encode | safe }},
"inLanguage": {{ config.default_language | json_encode | safe }},
"author": {
"@type": "Person",
"name": {{ author | json_encode | safe }},
"url": {{ config.base_url | json_encode | safe }},
"sameAs": {{ same_as | json_encode | safe }}
}
}
</script>
{% elif is_article %}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": {{ page.title | json_encode | safe }},
"description": {{ seo_description | json_encode | safe }},
"url": {{ page.permalink | json_encode | safe }},
"mainEntityOfPage": {{ page.permalink | json_encode | safe }},
"datePublished": {{ page.date | date(format="%Y-%m-%d") | json_encode | safe }},
"dateModified": {{ page.updated | default(value=page.date) | date(format="%Y-%m-%d") | json_encode | safe }},
"inLanguage": {{ page.lang | json_encode | safe }},
"wordCount": {{ page.word_count }},
{% if page.taxonomies.tags %}"keywords": {{ page.taxonomies.tags | join(sep=", ") | json_encode | safe }},{% endif %}
{% if seo_image %}"image": {{ seo_image | json_encode | safe }},{% endif %}
"author": {
"@type": "Person",
"name": {{ author | json_encode | safe }},
"url": {{ config.base_url | json_encode | safe }}
},
"publisher": {
"@type": "Person",
"name": {{ author | json_encode | safe }}
}
}
</script>
{% endif %}
{# ===== /SEO ===== #}
{# Favicon #}
{% if config.extra.favicon %}
<link rel="icon"
type="image/png"
href="{{ get_url(path=config.extra.favicon) }}" />
{% endif %}
{# Font from cdn or disk #}
{% if config.extra.use_cdn | default(value=false) %}
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/jetbrains-mono@1.0.6/css/jetbrains-mono.min.css" />
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@fontsource/space-grotesk@4.5.8/index.min.css" />
{% else %}
<link href={{ get_url(path="fonts.css") }} rel="stylesheet" />
{% endif %}
{# Syntax theme #}
{% if config.markdown.highlight_code and config.markdown.highlight_theme == "css" %}
<link rel="stylesheet"
type="text/css"
href="/syntax-theme-dark.css"
media="(prefers-color-scheme: dark)" />
<link rel="stylesheet"
type="text/css"
href="/syntax-theme-light.css"
media="(prefers-color-scheme: light)" />
{% endif %}
{# Analytics #}
{% if config.extra.analytics.enabled %}
{% if config.extra.analytics.umami.website_id %}
{% set website_id = config.extra.analytics.umami.website_id %}
{% set host_url = config.extra.analytics.umami.host_url | default(value="https://api-gateway.umami.dev/") %}
<script defer
src="/js/imamu.js"
data-website-id="{{ website_id }}"
data-host-url="{{ host_url }}"></script>
{% endif %}
{% if config.extra.analytics.goatcounter.user %}
{% set user = config.extra.analytics.goatcounter.user %}
{% set host = config.extra.analytics.goatcounter.host | default(value="goatcounter.com") %}
<script data-goatcounter="https://{{ user }}.{{ host }}/count"
async
src="{{ get_url(path="js/count.js") }}"></script>
<noscript>
{# EasyList blocks '.com/count?', so we have to use '.com//count' #}
<img src="https://{{ user }}.{{ host }}//count?p={{ current_path }}&t={{ page.title | default(value=config.title) }}" />
</noscript>
{% endif %}
{% if config.extra.analytics.google.tracking_id %}
{% set tracking_id = config.extra.analytics.google.tracking_id %}
<!-- Google tag (gtag.js) -->
<script async
src="https://www.googletagmanager.com/gtag/js?id={{ tracking_id }}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', '{{ tracking_id }}');
</script>
{% endif %}
{% endif %}
{# Fancy Codeblock #}
{% if config.extra.fancy_code %}
<script defer src={{ get_url(path="js/codeblock.js") }}></script>
{% endif %}
{# Table of contents #}
{% if config.extra.toc | default(value=false) %}
<script defer src={{ get_url(path="js/toc.js") }}></script>
{% endif %}
{# Dynamic Note #}
{% if config.extra.dynamic_note | default(value=false) %}
<script defer src={{ get_url(path="js/note.js") }}></script>
{% endif %}
{% if page.extra.mathjax | default(value=config.extra.mathjax | default(value=false)) %}
{% if config.extra.mathjax_dollar_inline_enable | default(value=false) %}
<script>
MathJax = {
tex: {
inlineMath: [
['$', '$'],
['\\(', '\\)']
]
}
};
</script>
{% endif %}
<script type="text/javascript" id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js">
</script>
{% endif %}
{# RSS #}
{% if config.generate_feeds %}
{% for feed_filenames in config.feed_filenames %}
{% if feed_filenames == "atom.xml" %}
<link rel="alternate"
type="application/atom+xml"
title="{{ config.title }}"
href="{{ get_url(path='atom.xml', trailing_slash=false) }}" />
{% elif feed_filenames == "rss.xml" %}
<link rel="alternate"
type="application/rss+xml"
title="{{ config.title }}"
href="{{ get_url(path='rss.xml', trailing_slash=false) }}" />
{% endif %}
{% endfor %}
{% endif %}
{% set theme = config.extra.theme | default(value="toggle") %}
{% if theme == "dark" %}
<link rel="stylesheet"
type="text/css"
href="{{ get_url(path='theme/dark.css') }}" />
{% elif theme == "light" %}
<link rel="stylesheet"
type="text/css"
href="{{ get_url(path='theme/light.css') }}" />
{% elif theme == "auto" %}
<link rel="stylesheet"
type="text/css"
href="{{ get_url(path='theme/light.css') }}" />
<link rel="stylesheet"
type="text/css"
href="{{ get_url(path='theme/dark.css') }}"
media="(prefers-color-scheme: dark)" />
{% elif theme == "toggle" %}
<link rel="stylesheet"
type="text/css"
href="{{ get_url(path='theme/light.css') }}" />
<link id="darkModeStyle"
rel="stylesheet"
type="text/css"
href="{{ get_url(path='theme/dark.css') }}" />
{% endif %}
<!-- Set the correct theme in the script -->
{% if theme == "auto" or theme == "toggle" %}
<script src={{ get_url(path="js/themetoggle.js") }}></script>
{% if theme == "auto" %}
<script>
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
setTheme("dark");
} else {
setTheme("light");
}
</script>
{% else %}
<script>
setTheme(getSavedTheme());
</script>
{% endif %}
{% endif %}
<link rel="stylesheet"
type="text/css"
media="screen"
href="{{ get_url(path='main.css') }}" />
{% if config.extra.stylesheets %}
{% for stylesheet in config.extra.stylesheets %}
<link rel="stylesheet" href="{{ get_url(path=stylesheet) }}" />
{% endfor %}
{% endif %}
{# Search #}
{%- if config.build_search_index -%}
{%- if config.search.index_format -%}
{%- set search_index_format = config.search.index_format -%}
{%- elif config.extra.index_format -%}
{# Necessary to support Zola 0.17.X, as it doesn't have access to config.search.index_format #}
{# See: https://github.com/getzola/zola/issues/2165 #}
{%- set search_index_format = config.extra.index_format -%}
{%- else -%}
{%- set search_index_format = "elasticlunr_json" -%}
{%- endif -%}
{%- if search_index_format == "elasticlunr_javascript" -%}
<script defer
src="{{ get_url(path='search_index.' ~ lang ~ '.js', cachebust=true) | safe }}"></script>
{%- endif -%}
{# Main search script #}
<script defer
src="{{ get_url(path='js/searchElasticlunr.min.js', cachebust=true) | safe }}"></script>
{%- endif -%}
</head>

View file

@ -0,0 +1,97 @@
<nav>
<div class="left-nav">
{% if config.extra.logo %}
<a href={{ config.base_url }} class="logo">
<img alt="{{ config.title }}" src="{{ get_url(path=config.extra.logo) }}" />
</a>
{% else %}
<a href={{ config.base_url }}>{{ config.title }}</a>
{% endif %}
<div class="socials">
{% if config.markdown.external_links_target_blank %}
{% set link_attrs = "rel='me noopener' target='_blank'" %}
{% else %}
{% set link_attrs = "rel='me'" %}
{% endif %}
{% for social in config.extra.socials %}
<a {{ link_attrs | safe }} href="{{ social.url }}" class="social">
<img alt="{{ social.name }}"
src="{{ get_url(path='icons/social/' ~ social.icon ~ '.svg') }}" />
</a>
{% endfor %}
</div>
</div>
<div class="right-nav">
{% for menu in config.extra.menu %}
<a href={{ get_url(path=menu.url) }} style="margin-right: 0.5em">{{ menu.name }}</a>
{% endfor %}
{% if config.build_search_index %}
<button id="search-button"
class="search-button"
title="$SHORTCUT to open search">
<img src="{{ get_url(path='icons/search.svg') }}"
alt="Search"
class="search-icon" />
</button>
<div id="searchModal"
class="search-modal js"
role="dialog"
aria-labelledby="modalTitle">
<div id="modal-content">
<h2 id="modalTitle" class="page-header">
Search
</h2>
<div id="searchBar">
<input id="searchInput"
role="combobox"
autocomplete="off"
spellcheck="false"
aria-expanded="false"
aria-controls="results-container"
placeholder="Search..." />
<button id="clear-search" class="clear-button" title="Clear search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960">
<path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z" />
</svg>
</button>
</div>
<div id="results-container">
<div id="results-info">
<span id="zero_results" style="display: none;">No results</span>
<span id="one_result" style="display: none;">1 result</span>
<span id="many_results" style="display: none;">$NUMBER results</span>
</div>
<div id="results" role="listbox">
</div>
</div>
</div>
</div>
{% endif %}
{% if config.extra.theme == "toggle" %}
<a id="dark-mode-toggle"
onclick="toggleTheme(); event.preventDefault();"
href="#">
<img src="{{ get_url(path='icons/sun.svg') }}" id="sun-icon" alt="Light" />
<img src="{{ get_url(path='icons/moon.svg') }}"
id="moon-icon"
style="filter: invert(1)"
alt="Dark" />
<img src="{{ get_url(path='icons/auto.svg') }}"
id="auto-icon"
style="filter: invert(1)"
alt="Auto" />
</a>
<!-- Initialize the theme toggle icons -->
<script>
updateItemToggleTheme()
</script>
{% endif %}
</div>
</nav>

14
templates/sitemap.xml Normal file
View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{%- for sitemap_entry in entries %}
{#- Fuera: notas sueltas (se leen en la portada) y redirecciones de paginación #}
{%- if sitemap_entry.permalink is not containing("/notas/") and sitemap_entry.permalink is not ending_with("/page/1/") %}
<url>
<loc>{{ sitemap_entry.permalink | escape_xml | safe }}</loc>
{%- if sitemap_entry.updated %}
<lastmod>{{ sitemap_entry.updated }}</lastmod>
{%- endif %}
</url>
{%- endif %}
{%- endfor %}
</urlset>

View file

@ -0,0 +1,30 @@
{% extends "base.html" %}
{% block main_content %}
<div>
{% block title %}
{{ post_macros::page_header(title="Etiquetas") }}
{% endblock title %}
<main class="tag-list">
{% set sort_by = config.extra.taxonomies.sort_by | default(value="name") %}
{% set terms = terms | default(value=[]) | sort(attribute=sort_by) %}
{% if config.extra.taxonomies.reverse | default(value=false) %}
{% set terms = terms | reverse %}
{% endif %}
<ul>
{%- for term in terms %}
<div class="post-header">
<h2 class="title">
<a href={{ term.permalink }}>{{ term.name }}</a>
</h2>
<small class="meta">{{ term.page_count }} entrada{{ term.page_count | pluralize }}</small>
</div>
{% endfor -%}
</ul>
</main>
</div>
{% endblock main_content %}

View file

@ -0,0 +1,9 @@
{% extends "index.html" %}
{% block main_content %}
<h1 class="page-header">Entradas sobre «{{ term.name }}»</h1>
<main class="post-list">
{{ post_macros::list_posts(pages=term.pages) }}
</main>
{% endblock main_content %}