Overview
The plantilla.html file serves as a reusable template that’s filled with dynamically generated content. It includes a responsive design, sidebar navigation, and a clean card-based layout.
Template Structure
The template uses a simple variable substitution system with placeholder tags:
<! DOCTYPE html >
< html lang = "es" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title > Historia Diaria Automatizada </ title >
< style >
/* Embedded CSS styles */
</ style >
</ head >
< body >
< button id = "menu-btn" onclick = " toggleMenu ()" > ☰ </ button >
< nav id = "sidebar" >
< button id = "close-btn" onclick = " toggleMenu ()" > ✖ </ button >
< h2 > Historial </ h2 >
{{MENU}}
</ nav >
< div class = "container" >
< h1 > {{TITULO}} </ h1 >
< img src = "{{IMAGEN_URL}}" alt = "Imagen del día" class = "imagen-dia" >
< div class = "historia-texto" >
< p > {{HISTORIA}} </ p >
</ div >
</ div >
< script >
function toggleMenu () {
const sidebar = document . getElementById ( 'sidebar' );
sidebar . classList . toggle ( 'abierto' );
}
</ script >
</ body >
</ html >
Variable Placeholders
The template uses four dynamic placeholders that are replaced by the Python script:
Location: <h1>{{TITULO}}</h1>Replaced with the AI-generated story title extracted from the <TITULO> tag in the API response. contenido_html = contenido_html.replace( ' {{ TITULO }} ' , nuevo_titulo)
Location: <p>{{HISTORIA}}</p>Replaced with the story content extracted from the <HISTORIA> tag. Supports multi-paragraph text. contenido_html = contenido_html.replace( ' {{ HISTORIA }} ' , nueva_historia)
Location: <img src="{{IMAGEN_URL}}" ...>Replaced with the Picsum Photos URL generated using a random UUID seed. contenido_html = contenido_html.replace( ' {{ IMAGEN_URL }} ' , url_imagen)
Replacement Process
The Python script performs simple string replacement:
# 7. Load template
with open ( 'plantilla.html' , 'r' , encoding = 'utf-8' ) as archivo:
contenido_html = archivo.read()
# Replace all placeholders
contenido_html = contenido_html.replace( ' {{ TITULO }} ' , nuevo_titulo)
contenido_html = contenido_html.replace( ' {{ HISTORIA }} ' , nueva_historia)
contenido_html = contenido_html.replace( ' {{ IMAGEN_URL }} ' , url_imagen)
contenido_html = contenido_html.replace( ' {{ MENU }} ' , menu_html)
# 8. Save final files
with open ( 'index.html' , 'w' , encoding = 'utf-8' ) as archivo:
archivo.write(contenido_html)
with open (nombre_archivo_hoy, 'w' , encoding = 'utf-8' ) as archivo:
archivo.write(contenido_html)
This approach is simple and doesn’t require a templating engine like Jinja2. It works perfectly for this use case since the placeholders are unique and don’t conflict with HTML syntax.
Styling System
The template includes embedded CSS with a modern, clean design:
Color Palette
body {
background-color : #f0f2f5 ; /* Light gray background */
}
.container {
background-color : #ffffff ; /* White card */
}
h1 {
color : #2d3748 ; /* Dark gray headings */
}
.historia-texto {
color : #4a5568 ; /* Medium gray text */
}
Layout Components
Main Container
Story Image
Story Text
.container {
background-color : #ffffff ;
padding : 40 px ;
border-radius : 16 px ;
box-shadow : 0 10 px 25 px rgba ( 0 , 0 , 0 , 0.1 );
max-width : 600 px ;
width : 100 % ;
text-align : center ;
margin-top : 40 px ;
}
Creates a centered card with rounded corners and subtle shadow. .imagen-dia {
max-width : 100 % ;
height : auto ;
border-radius : 12 px ;
margin-bottom : 30 px ;
}
Responsive image that scales to container width while maintaining aspect ratio. .historia-texto {
font-size : 1.15 rem ;
line-height : 1.7 ;
color : #4a5568 ;
text-align : left ;
margin-bottom : 40 px ;
}
Comfortable reading typography with generous line spacing.
The sidebar navigation is dynamically built from historial.json:
# 6. Generate sidebar menu HTML
menu_html = ""
for mes, historias in historial.items():
menu_html += f '<h3 class="mes-titulo"> { mes } </h3>'
menu_html += '<ul class="lista-historias">'
for item in historias:
menu_html += f '<li><a href=" { item[ "archivo" ] } "> { item[ "titulo" ] } </a></li>'
menu_html += '</ul>'
Based on the actual historial.json data:
< h3 class = "mes-titulo" > Marzo 2026 </ h3 >
< ul class = "lista-historias" >
< li >< a href = "historia-2026-03-04.html" > El Guardián del Faro de las Sombras </ a ></ li >
< li >< a href = "historia-2026-03-03.html" > El Guardián del Laberinto Cuántico </ a ></ li >
< li >< a href = "historia-2026-03-02.html" > El Código de las Estrellas </ a ></ li >
</ ul >
< h3 class = "mes-titulo" > Febrero 2026 </ h3 >
< ul class = "lista-historias" >
< li >< a href = "historia-2026-02-26.html" > El Eco de los Días Perdidos </ a ></ li >
< li >< a href = "historia-2026-02-25.html" > El Eco del Ayer </ a ></ li >
</ ul >
#sidebar {
position : fixed ;
top : 0 ;
left : -300 px ; /* Hidden by default */
width : 250 px ;
height : 100 % ;
background-color : #ffffff ;
box-shadow : 2 px 0 15 px rgba ( 0 , 0 , 0 , 0.2 );
transition : left 0.3 s ease ; /* Smooth slide animation */
padding : 20 px ;
overflow-y : auto ;
z-index : 1001 ;
}
#sidebar.abierto {
left : 0 ; /* Slides into view */
}
The sidebar uses a translate animation instead of display toggling, providing a smooth slide-in effect.
< button id = "menu-btn" onclick = " toggleMenu ()" > ☰ </ button >
#menu-btn {
position : fixed ;
top : 20 px ;
left : 20 px ;
font-size : 24 px ;
background : #ffffff ;
border : none ;
padding : 10 px 15 px ;
border-radius : 8 px ;
cursor : pointer ;
box-shadow : 0 4 px 6 px rgba ( 0 , 0 , 0 , 0.1 );
z-index : 1000 ;
}
Responsive Design
The template is mobile-friendly:
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
Ensures proper scaling on mobile devices.
Flexible Layout
body {
display : flex ;
justify-content : center ;
align-items : center ;
min-height : 90 vh ;
}
.container {
max-width : 600 px ;
width : 100 % ; /* Scales down on smaller screens */
}
.imagen-dia {
max-width : 100 % ; /* Never exceeds container */
height : auto ; /* Maintains aspect ratio */
}
The card layout automatically adapts to screen width, maintaining readability on phones, tablets, and desktops.
JavaScript Functionality
The template includes minimal JavaScript for sidebar toggling:
function toggleMenu () {
const sidebar = document . getElementById ( 'sidebar' );
sidebar . classList . toggle ( 'abierto' );
}
How It Works
User clicks the hamburger button (☰)
toggleMenu() is called
The abierto class is toggled on the sidebar
CSS transition animates the sidebar sliding in/out
Close button (✖) uses the same function to hide the menu
Month Grouping Styles
Month headers and story lists have distinct styling:
.mes-titulo {
color : #2d3748 ;
border-bottom : 2 px solid #e2e8f0 ;
padding-bottom : 5 px ;
margin-top : 20 px ;
}
.lista-historias {
list-style-type : none ;
padding : 0 ;
}
.lista-historias li {
margin-bottom : 10 px ;
}
.lista-historias a {
text-decoration : none ;
color : #4a5568 ;
font-size : 0.95 rem ;
}
.lista-historias a :hover {
color : #3182ce ;
text-decoration : underline ;
}
This creates clear visual separation between months and provides hover feedback on links.
Complete Rendering Example
Before (Template)
After (Rendered)
< div class = "container" >
< h1 > {{TITULO}} </ h1 >
< img src = "{{IMAGEN_URL}}" alt = "Imagen del día" class = "imagen-dia" >
< div class = "historia-texto" >
< p > {{HISTORIA}} </ p >
</ div >
</ div >
Template Advantages
No Build Step Pure HTML/CSS/JS means instant page loads without bundlers or frameworks
Simple Maintenance Easy to update styles or layout without complex tooling
GitHub Pages Ready Static files deploy directly without server-side rendering
Offline Capable All assets embedded means pages work without internet (except images)
Customization Guide
Changing Colors
Edit the color values in the embedded <style> block:
body {
background-color : #yourcolor; /* Change background */
}
h1 {
color : #yourcolor; /* Change title color */
}
Adjusting Layout Width
.container {
max-width : 800 px ; /* Wider cards */
}
Modifying Image Dimensions
Change the Picsum URL dimensions in the Python script:
url_imagen = f "https://picsum.photos/seed/ { codigo_unico } /800/450" # Wider aspect ratio
Next Steps
How It Works Understand the complete system architecture
Customization Learn how to customize colors, fonts, and layouts