refactor: reorganize skills sidebar layout (7 left, 6 right)

- Update splitSkills logic to put 7 categories on left sidebar, 6 on right
- Reorder skills: Frontend Technologies now at position 5, Legacy Enterprise Technologies at position 6 (last on left)
- Apply changes to both English and Spanish CV data
- Clean up unused enhanced template and CSS files

Left sidebar (Page 1):
1. AI-Assisted Development
2. SAP Technologies
3. Programming Languages
4. Go Ecosystem
5. JavaScript Ecosystem
6. Frontend Technologies
7. Legacy Enterprise Technologies

Right sidebar (Page 2):
8. Backend Technologies
9. Databases
10. Infrastructure & Servers
11. DevOps & CI/CD
12. Team Management
13. Design Tools
This commit is contained in:
juanatsap
2025-11-08 14:33:55 +00:00
parent 435ab7ae70
commit 4761145ad8
5 changed files with 243 additions and 1256 deletions
+102 -86
View File
@@ -310,125 +310,138 @@
"skills": {
"technical": [
{
"category": "AI & Modern Development",
"category": "AI-Assisted Development",
"proficiency": 5,
"items": [
"AI-Assisted Development (Claude Code, Copilot, GPT-4)",
"Prompt Engineering & AI Workflows",
"HTMX (Hypermedia Applications)",
"Go (Golang)",
"AI Development Workflows (Claude Code, Copilot, GPT-4)",
"Agent-Based & Spec-Driven Development",
"Prompt Engineering & AI Integration",
"Automated Code Generation & Documentation",
"OpenAI & Anthropic APIs"
]
},
{
"category": "SAP Technologies",
"proficiency": 5,
"items": [
"SAP Customer Data Cloud (CDC)",
"SAP Cloud Platform",
"SAP S/4HANA",
"GDPR Compliance & Data Protection"
]
},
{
"category": "Programming Languages",
"proficiency": 4,
"items": [
"JAVA/Groovy",
"JavaScript (ES6+)",
"Go",
"TypeScript",
"Node.js",
"Python",
"Shell Scripting (Bash/Unix)",
"PHP",
"Scala",
"XML/XSLT",
"Action Script",
"Shell Scripts (Unix)",
"C/C++"
"Java",
"Groovy",
"SQL",
"Assembler"
]
},
{
"category": "Web Development",
"category": "Go Ecosystem",
"proficiency": 5,
"items": [
"JSP/PHP",
"HTML(5)/XHTML/Handlebars/Moustache/Velocity/Freemarker",
"CSS/Less/Sass/Javascript/jQuery/mooTools",
"DOM/Ajax/SEO/WebServices",
"REST API Design & Development",
"Responsive & Mobile-First Design"
"Hono - High-Performance Web Framework",
"Gin - Web Framework",
"Fyne - Cross-Platform GUI Framework",
"Bubble Tea - TUI Framework",
"Charm - Terminal UI Libraries (Lipgloss, Bubbles)",
"Goroutines & Concurrency Patterns",
"Standard Library (net/http, html/template)"
]
},
{
"category": "JavaScript Frameworks",
"category": "JavaScript Ecosystem",
"proficiency": 5,
"items": [
"Node & ReactJS",
"Node.js & Express",
"React & React Ecosystem",
"Redux/Flux",
"Webpack2/Express",
"Gulp/Grunt"
"Modern Build Tools (Webpack, Vite, ESBuild)",
"Package Management (npm, pnpm, bun)",
"Testing Frameworks (Jest, Playwright)"
]
},
{
"category": "PHP Frameworks",
"proficiency": 4,
"category": "Frontend Technologies",
"proficiency": 5,
"items": [
"Yii Framework",
"Zend Framework",
"Wordpress API",
"Joomla API"
"HTMX - Hypermedia-Driven Applications",
"HTML5 & Semantic Web",
"CSS3, Tailwind CSS, SASS/LESS",
"JavaScript - DOM Manipulation & AJAX",
"jQuery",
"Progressive Enhancement & Accessibility",
"Responsive & Mobile-First Design",
"Template Engines (Handlebars, Panini, Mustache)"
]
},
{
"category": "Java Frameworks",
"proficiency": 4,
"category": "Legacy Enterprise Technologies",
"proficiency": 3,
"items": [
"Play! Framework",
"Struts",
"Spring",
"Hibernate",
"Ibatis",
"Magnolia CMS",
"XWiki",
"TESEO Framework"
]
},
{
"category": "Application Servers",
"proficiency": 4,
"items": [
"Apache/WAMP/MAMP",
"Tomcat/JBoss/Resin/Jetty/Websphere/Weblogic"
"Java & J2EE",
"Spring Framework, Struts, Hibernate",
"PHP & WordPress",
"Yii Framework, Zend Framework",
"Enterprise Application Servers (Tomcat, JBoss, WebLogic)"
]
},
{
"category": "Backend Technologies",
"proficiency": 4,
"proficiency": 5,
"items": [
"Node.js (Express, Modern frameworks)",
"Go (Golang)",
"Java & J2EE",
"Spring Framework, Struts, Hibernate",
"PHP"
"Go - Current Primary Stack",
"Hono Framework - High-Performance Web Server",
"Node.js (Express, Hono-compatible frameworks)",
"RESTful API Design & Implementation",
"Authentication & Authorization Systems",
"Database Design & Optimization"
]
},
{
"category": "Databases",
"proficiency": 4,
"items": [
"PostgreSQL",
"MySQL",
"SQLite",
"Oracle",
"MySql",
"Postgresql",
"Hypersonic",
"SQL Knowledge"
"MongoDB (NoSQL)",
"SQL Knowledge",
"Database Design & Optimization"
]
},
{
"category": "CMS's and Web Production Environments",
"proficiency": 4,
"category": "Infrastructure & Servers",
"proficiency": 5,
"items": [
"Joomla",
"Wordpress",
"RapidWeaver",
"Servoy",
"WebRatio",
"Play! Framework"
"Linux Server Administration",
"VPS Deployment & Configuration",
"Nginx & Apache Configuration",
"Docker & Containers",
"Reverse Proxy & Load Balancing"
]
},
{
"category": "Design Tools",
"proficiency": 3,
"category": "DevOps & CI/CD",
"proficiency": 5,
"items": [
"Corel Draw",
"Adobe PhotoShop",
"Adobe Illustrator",
"GIMP"
"CI/CD Pipeline Design & Implementation",
"Custom Deployment Solutions",
"Monitoring & Alerting Dashboards",
"Git & Version Control",
"Process Automation & Scripting"
]
},
{
@@ -443,23 +456,13 @@
]
},
{
"category": "SAP Technologies",
"proficiency": 5,
"category": "Design Tools",
"proficiency": 3,
"items": [
"SAP Customer Data Cloud (CDC)",
"SAP Cloud Platform",
"GDPR Compliance & Data Protection"
]
},
{
"category": "DevOps & Tools",
"proficiency": 4,
"items": [
"Git (Version Control)",
"CI/CD Pipelines",
"Docker",
"Automated Testing",
"Agile Methodologies"
"Corel Draw",
"Adobe PhotoShop",
"Adobe Illustrator",
"GIMP"
]
}
],
@@ -700,6 +703,19 @@
"Participated in practical development workshops",
"Networked with regional technology sector professionals"
]
},
{
"title": "Web Application Development: Apache, PHP and MySQL",
"institution": "University of Extremadura",
"location": "Cáceres",
"date": "2002",
"duration": "40 hours",
"shortDescription": "Web application development course covering Apache, PHP, and MySQL technologies.",
"responsibilities": [
"Learned Apache web server configuration and administration",
"Developed dynamic web applications using PHP",
"Designed and implemented MySQL databases for web applications"
]
}
],
"projects": [
+133 -144
View File
@@ -310,172 +310,138 @@
"skills": {
"technical": [
{
"category": "IA y Desarrollo Moderno",
"category": "Desarrollo Asistido por IA",
"proficiency": 5,
"items": [
"Desarrollo Asistido por IA (Claude Code, Copilot, GPT-4)",
"Ingeniería de Prompts y Flujos de Trabajo con IA",
"HTMX (Aplicaciones Hipermedia)",
"Go (Golang)",
"Flujos de Desarrollo con IA (Claude Code, Copilot, GPT-4)",
"Desarrollo Basado en Agentes y Especificaciones",
"Ingeniería de Prompts e Integración de IA",
"Generación Automática de Código y Documentación",
"APIs OpenAI y Anthropic"
]
},
{
"category": "Lenguajes de Programación",
"proficiency": 4,
"items": [
"JavaScript (ES6+)",
"Go (Golang)",
"Java",
"Groovy",
"PHP",
"XML",
"XSLT",
"Action Script",
"Shell Scripts (Unix)",
"C",
"C++"
]
},
{
"category": "Ecosistema JavaScript",
"proficiency": 5,
"items": [
"JavaScript Avanzado (ES6+)",
"React y Ecosistema React",
"Redux",
"Flux",
"Node.js y Express",
"Webpack, Vite",
"Gulp",
"Grunt"
]
},
{
"category": "Desarrollo Web",
"proficiency": 5,
"items": [
"HTML5, CSS3, Web Semántica",
"Diseño y Desarrollo de APIs REST",
"LESS, SASS, Preprocesadores CSS",
"Diseño Responsive y Mobile-First",
"JSP",
"PHP",
"Handlebars",
"Moustache",
"Velocity",
"Freemarker",
"jQuery",
"mooTools",
"DOM",
"Ajax",
"SEO",
"WebServices"
]
},
{
"category": "Frameworks PHP",
"proficiency": 4,
"items": [
"Yii Framework",
"Zend Framework",
"API de Wordpress",
"API de Joomla"
]
},
{
"category": "Frameworks Java",
"proficiency": 4,
"items": [
"Play! Framework",
"Struts",
"Spring Framework",
"Hibernate",
"Ibatis",
"Magnolia CMS",
"XWiki",
"Framework TESEO"
]
},
{
"category": "Tecnologías Backend",
"proficiency": 4,
"items": [
"Node.js (Express, frameworks modernos)",
"Go (Golang)",
"Java y J2EE",
"Spring Framework, Struts, Hibernate",
"PHP"
]
},
{
"category": "Servidores de Aplicaciones",
"proficiency": 4,
"items": [
"Apache",
"WAMP",
"MAMP",
"Tomcat",
"JBoss",
"Resin",
"Jetty",
"Websphere",
"Weblogic"
]
},
{
"category": "CMS y Entornos de Producción Web",
"proficiency": 4,
"items": [
"Joomla",
"Wordpress",
"RapidWeaver",
"Servoy",
"WebRatio",
"Magnolia CMS"
]
},
{
"category": "Bases de Datos",
"proficiency": 4,
"items": [
"PostgreSQL",
"MySQL",
"Oracle",
"MongoDB (NoSQL)",
"Hypersonic",
"Dominio de SQL",
"Diseño y Optimización de Bases de Datos"
]
},
{
"category": "Tecnologías SAP",
"proficiency": 5,
"items": [
"SAP Customer Data Cloud (CDC)",
"SAP Cloud Platform",
"SAP S/4HANA",
"Cumplimiento GDPR y Protección de Datos"
]
},
{
"category": "DevOps y Herramientas",
"category": "Lenguajes de Programación",
"proficiency": 4,
"items": [
"Git (Control de Versiones)",
"Pipelines CI/CD",
"Docker",
"Testing Automatizado",
"Metodologías Ágiles"
"JavaScript (ES6+)",
"Go",
"TypeScript",
"Node.js",
"Python",
"Shell Scripting (Bash/Unix)",
"PHP",
"Java",
"Groovy",
"SQL",
"Assembler"
]
},
{
"category": "Herramientas de Diseño",
"category": "Ecosistema Go",
"proficiency": 5,
"items": [
"Hono - Framework Web de Alto Rendimiento",
"Gin - Framework Web",
"Fyne - Framework GUI Multiplataforma",
"Bubble Tea - Framework TUI",
"Charm - Bibliotecas de UI Terminal (Lipgloss, Bubbles)",
"Goroutines y Patrones de Concurrencia",
"Biblioteca Estándar (net/http, html/template)"
]
},
{
"category": "Ecosistema JavaScript",
"proficiency": 5,
"items": [
"Node.js y Express",
"React y Ecosistema React",
"Redux/Flux",
"Herramientas de Build Modernas (Webpack, Vite, ESBuild)",
"Gestión de Paquetes (npm, pnpm, bun)",
"Frameworks de Testing (Jest, Playwright)"
]
},
{
"category": "Tecnologías Frontend",
"proficiency": 5,
"items": [
"HTMX - Aplicaciones Basadas en Hipermedia",
"HTML5 y Web Semántica",
"CSS3, Tailwind CSS, SASS/LESS",
"JavaScript - Manipulación DOM y AJAX",
"jQuery",
"Mejora Progresiva y Accesibilidad",
"Diseño Responsive y Mobile-First",
"Motores de Plantillas (Handlebars, Panini, Mustache)"
]
},
{
"category": "Tecnologías Enterprise Anteriores",
"proficiency": 3,
"items": [
"Corel Draw",
"Adobe PhotoShop",
"Adobe Illustrator",
"GIMP"
"Java y J2EE",
"Spring Framework, Struts, Hibernate",
"PHP y WordPress",
"Yii Framework, Zend Framework",
"Servidores de Aplicaciones Enterprise (Tomcat, JBoss, WebLogic)"
]
},
{
"category": "Tecnologías Backend",
"proficiency": 5,
"items": [
"Go - Stack Principal Actual",
"Hono Framework - Servidor Web de Alto Rendimiento",
"Node.js (Express, frameworks compatibles con Hono)",
"Diseño e Implementación de APIs RESTful",
"Sistemas de Autenticación y Autorización",
"Diseño y Optimización de Bases de Datos"
]
},
{
"category": "Bases de Datos",
"proficiency": 4,
"items": [
"PostgreSQL",
"MySQL",
"SQLite",
"Oracle",
"MongoDB (NoSQL)",
"Dominio de SQL",
"Diseño y Optimización de Bases de Datos"
]
},
{
"category": "Infraestructura y Servidores",
"proficiency": 5,
"items": [
"Administración de Servidores Linux",
"Despliegue y Configuración de VPS",
"Configuración Nginx y Apache",
"Docker y Contenedores",
"Reverse Proxy y Balanceo de Carga"
]
},
{
"category": "DevOps y CI/CD",
"proficiency": 5,
"items": [
"Diseño e Implementación de Pipelines CI/CD",
"Soluciones de Despliegue Personalizadas",
"Dashboards de Monitoreo y Alertas",
"Git y Control de Versiones",
"Automatización de Procesos y Scripting"
]
},
{
@@ -488,6 +454,16 @@
"Gestión de tareas",
"Reportes mensuales"
]
},
{
"category": "Herramientas de Diseño",
"proficiency": 3,
"items": [
"Corel Draw",
"Adobe PhotoShop",
"Adobe Illustrator",
"GIMP"
]
}
],
"soft_skills": [
@@ -732,6 +708,19 @@
"Participé en talleres prácticos de desarrollo",
"Hice networking con profesionales del sector tecnológico regional"
]
},
{
"title": "Desarrollo de aplicaciones Web: Apache, PHP y MySQL",
"institution": "Universidad de Extremadura",
"location": "Cáceres",
"date": "2002",
"duration": "40 horas",
"shortDescription": "Curso de desarrollo de aplicaciones web con tecnologías Apache, PHP y MySQL.",
"responsibilities": [
"Aprendí configuración y administración del servidor web Apache",
"Desarrollé aplicaciones web dinámicas usando PHP",
"Diseñé e implementé bases de datos MySQL para aplicaciones web"
]
}
],
"projects": [
+8 -6
View File
@@ -199,18 +199,20 @@ func (h *CVHandler) ExportPDF(w http.ResponseWriter, r *http.Request) {
}
// splitSkills splits skill categories between left (page 1) and right (page 2) sidebars
// The split is done at the midpoint to evenly distribute skills
// Left sidebar shows first 7 categories, right sidebar shows remaining categories
func splitSkills(skills []models.SkillCategory) (left, right []models.SkillCategory) {
if len(skills) == 0 {
return nil, nil
}
// Calculate midpoint
mid := len(skills) / 2
// Split at index 7 (first 7 items on left)
splitIndex := 7
if len(skills) < splitIndex {
return skills, nil
}
// Split at midpoint
left = skills[:mid]
right = skills[mid:]
left = skills[:splitIndex]
right = skills[splitIndex:]
return left, right
}
-849
View File
@@ -1,849 +0,0 @@
/* Root Variables */
:root {
--bg-gray: #525659;
--paper-white: #ffffff;
--text-dark: #1a1a1a;
--text-gray: #4a4a4a;
--text-light: #6a6a6a;
--accent-blue: #2563eb;
--accent-blue-hover: #1d4ed8;
--border-gray: #e5e5e5;
--error-red: #dc2626;
--error-bg: #fee2e2;
--success-green: #10b981;
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
/* Transition timings */
--transition-fast: 150ms;
--transition-base: 200ms;
--transition-slow: 300ms;
}
/* Reset & Base Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background-color: var(--bg-gray);
color: var(--text-dark);
line-height: 1.6;
min-height: 100vh;
}
/* Screen reader only text */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
a {
color: var(--accent-blue);
text-decoration: none;
transition: color var(--transition-base);
}
a:hover {
color: var(--accent-blue-hover);
text-decoration: underline;
}
a:focus-visible {
outline: 2px solid var(--accent-blue);
outline-offset: 2px;
border-radius: 2px;
}
/* Action Bar */
.action-bar {
background: rgba(255, 255, 255, 0.95);
border-bottom: 1px solid var(--border-gray);
position: sticky;
top: 0;
z-index: 100;
backdrop-filter: blur(10px);
box-shadow: var(--shadow);
}
.action-bar-content {
max-width: 1200px;
margin: 0 auto;
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
gap: 2rem;
}
.language-toggle {
display: flex;
gap: 0.5rem;
}
.lang-btn {
padding: 0.5rem 1rem;
border: 2px solid var(--border-gray);
background: white;
border-radius: 6px;
cursor: pointer;
font-size: 0.9rem;
font-weight: 500;
transition: all var(--transition-base);
position: relative;
}
.lang-btn:hover:not(.active) {
border-color: var(--accent-blue);
background: #f0f9ff;
transform: translateY(-1px);
}
.lang-btn:focus-visible {
outline: 2px solid var(--accent-blue);
outline-offset: 2px;
}
.lang-btn.active {
border-color: var(--accent-blue);
background: var(--accent-blue);
color: white;
}
/* Loading state for buttons */
.lang-btn[aria-busy="true"] {
opacity: 0.7;
cursor: wait;
pointer-events: none;
}
.export-actions {
display: flex;
gap: 0.5rem;
}
.export-btn {
padding: 0.5rem 1.5rem;
background: var(--accent-blue);
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 0.9rem;
font-weight: 600;
transition: all var(--transition-base);
box-shadow: var(--shadow);
}
.export-btn:hover {
background: var(--accent-blue-hover);
transform: translateY(-1px);
box-shadow: var(--shadow-lg);
}
.export-btn:focus-visible {
outline: 2px solid white;
outline-offset: 2px;
}
/* HTMX Indicator */
.htmx-indicator {
display: none;
align-items: center;
gap: 0.5rem;
}
.htmx-indicator.htmx-request {
display: flex;
}
.loader {
border: 3px solid #f3f3f3;
border-top: 3px solid var(--accent-blue);
border-radius: 50%;
width: 24px;
height: 24px;
animation: spin 1s linear infinite;
display: inline-block;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* CV Container - Paper Effect */
.cv-container {
max-width: 900px;
margin: 2rem auto;
padding: 0 1rem;
}
.cv-paper {
background: var(--paper-white);
padding: 3rem;
box-shadow: var(--shadow-lg);
border-radius: 8px;
min-height: 11in; /* A4 height */
/* HTMX swap transitions */
transition: opacity var(--transition-base);
}
/* HTMX swap animation */
.cv-paper.htmx-swapping {
opacity: 0;
transition: opacity var(--transition-fast);
}
.cv-paper.htmx-settling {
opacity: 1;
transition: opacity var(--transition-base);
}
/* Error Toast */
.error-toast {
position: fixed;
bottom: 2rem;
right: 2rem;
background: var(--error-bg);
color: var(--error-red);
padding: 1rem 1.5rem;
border-radius: 8px;
border-left: 4px solid var(--error-red);
box-shadow: var(--shadow-lg);
display: flex;
align-items: center;
gap: 1rem;
max-width: 400px;
z-index: 1000;
animation: slideIn var(--transition-base) ease-out;
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.error-toast button {
background: none;
border: none;
font-size: 1.5rem;
color: var(--error-red);
cursor: pointer;
padding: 0;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
transition: opacity var(--transition-base);
}
.error-toast button:hover {
opacity: 0.7;
}
/* CV Header */
.cv-header {
border-bottom: 3px solid var(--accent-blue);
padding-bottom: 2rem;
margin-bottom: 2rem;
}
.cv-header-main {
margin-bottom: 1.5rem;
}
.cv-name {
font-size: 2.5rem;
font-weight: 700;
color: var(--text-dark);
margin-bottom: 0.5rem;
}
.cv-title {
font-size: 1.5rem;
font-weight: 400;
color: var(--text-gray);
}
.cv-contact {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 0.75rem;
font-size: 0.9rem;
}
.contact-item {
color: var(--text-gray);
}
/* Sections */
.cv-section {
margin-bottom: 2.5rem;
}
.section-title {
font-size: 1.5rem;
font-weight: 700;
color: var(--text-dark);
margin-bottom: 1.5rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid var(--border-gray);
}
.summary-text {
font-size: 1rem;
line-height: 1.8;
color: var(--text-gray);
text-align: justify;
}
/* AI Development Section */
.ai-section {
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
padding: 2rem;
border-radius: 8px;
border-left: 4px solid var(--accent-blue);
}
.ai-period {
font-size: 0.9rem;
color: var(--text-gray);
margin-bottom: 1rem;
font-style: italic;
}
.ai-description {
margin-bottom: 1.5rem;
line-height: 1.8;
}
.ai-skills {
display: grid;
gap: 1.5rem;
}
.ai-skill-category {
background: white;
padding: 1.5rem;
border-radius: 6px;
box-shadow: var(--shadow);
}
.ai-skill-title {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 1rem;
color: var(--text-dark);
}
.proficiency-badge {
background: var(--accent-blue);
color: white;
padding: 0.25rem 0.75rem;
border-radius: 12px;
font-size: 0.75rem;
font-weight: 500;
text-transform: uppercase;
margin-left: 0.5rem;
}
.ai-skill-list {
list-style: none;
display: grid;
gap: 0.5rem;
}
.ai-skill-list li:before {
content: "✨ ";
margin-right: 0.5rem;
}
.ai-achievements {
margin-top: 1.5rem;
background: white;
padding: 1.5rem;
border-radius: 6px;
}
.ai-achievements h4 {
margin-bottom: 1rem;
font-size: 1.1rem;
}
.achievement-list {
list-style: none;
}
.achievement-list li {
padding: 0.5rem 0;
border-bottom: 1px solid var(--border-gray);
}
.achievement-list li:last-child {
border-bottom: none;
}
.achievement-list li:before {
content: "🏆 ";
margin-right: 0.5rem;
}
/* Experience Items */
.experience-item {
margin-bottom: 2rem;
padding-bottom: 2rem;
border-bottom: 1px solid var(--border-gray);
}
.experience-item:last-child {
border-bottom: none;
}
.experience-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 1rem;
gap: 1rem;
}
.experience-title {
flex: 1;
}
.position {
font-size: 1.2rem;
font-weight: 600;
color: var(--text-dark);
margin-bottom: 0.25rem;
}
.company {
font-size: 0.95rem;
color: var(--text-gray);
font-weight: 500;
}
.experience-period {
font-size: 0.9rem;
color: var(--text-light);
white-space: nowrap;
font-style: italic;
}
.responsibilities {
list-style: none;
margin-bottom: 1rem;
}
.responsibilities li {
padding-left: 1.5rem;
margin-bottom: 0.5rem;
position: relative;
}
.responsibilities li:before {
content: "▸";
position: absolute;
left: 0;
color: var(--accent-blue);
font-weight: bold;
}
.technologies {
font-size: 0.9rem;
color: var(--text-gray);
margin-top: 1rem;
}
.highlights {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 1rem;
}
.highlight-badge {
background: #fef3c7;
color: #92400e;
padding: 0.25rem 0.75rem;
border-radius: 12px;
font-size: 0.85rem;
font-weight: 500;
}
/* Skills Grid */
.skills-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.skill-category {
background: #f9fafb;
padding: 1.5rem;
border-radius: 6px;
border-left: 3px solid var(--accent-blue);
}
.skill-category-title {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
.proficiency-stars {
color: #fbbf24;
margin-bottom: 1rem;
font-size: 1.1rem;
}
.skill-items {
list-style: none;
}
.skill-items li {
padding: 0.25rem 0;
padding-left: 1rem;
position: relative;
}
.skill-items li:before {
content: "•";
position: absolute;
left: 0;
color: var(--accent-blue);
}
.soft-skills {
margin-top: 2rem;
}
.soft-skills h4 {
margin-bottom: 1rem;
font-size: 1.1rem;
}
.soft-skills-list {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.soft-skill-tag {
background: #e0e7ff;
color: #3730a3;
padding: 0.5rem 1rem;
border-radius: 20px;
font-size: 0.85rem;
font-weight: 500;
}
/* Projects */
.project-item {
margin-bottom: 2rem;
padding-bottom: 2rem;
border-bottom: 1px solid var(--border-gray);
}
.project-item:last-child {
border-bottom: none;
}
.project-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 0.5rem;
}
.project-name {
font-size: 1.2rem;
font-weight: 600;
color: var(--text-dark);
}
.project-period {
font-size: 0.9rem;
color: var(--text-light);
font-style: italic;
}
.project-role {
font-size: 0.9rem;
color: var(--text-gray);
margin-bottom: 0.5rem;
font-weight: 500;
}
.project-url {
font-size: 0.85rem;
margin-bottom: 0.75rem;
}
.project-description {
margin-bottom: 1rem;
line-height: 1.7;
}
.project-highlights {
list-style: none;
margin-top: 1rem;
}
.project-highlights li {
padding-left: 1.5rem;
margin-bottom: 0.25rem;
position: relative;
}
.project-highlights li:before {
content: "✓";
position: absolute;
left: 0;
color: #10b981;
font-weight: bold;
}
/* Education */
.education-item {
margin-bottom: 1.5rem;
}
.education-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 0.5rem;
}
.degree {
font-size: 1.1rem;
font-weight: 600;
color: var(--text-dark);
}
.education-period {
font-size: 0.9rem;
color: var(--text-light);
font-style: italic;
}
.institution {
font-size: 0.95rem;
color: var(--text-gray);
margin-bottom: 0.25rem;
}
.field {
font-size: 0.9rem;
color: var(--text-light);
}
/* Certifications */
.certifications-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
}
.certification-item {
background: #f9fafb;
padding: 1.25rem;
border-radius: 6px;
border-left: 3px solid #10b981;
}
.certification-name {
font-size: 1rem;
font-weight: 600;
margin-bottom: 0.5rem;
color: var(--text-dark);
}
.certification-issuer {
font-size: 0.9rem;
color: var(--text-gray);
margin-bottom: 0.25rem;
}
.certification-date {
font-size: 0.85rem;
color: var(--text-light);
}
/* Awards */
.award-item {
margin-bottom: 1.5rem;
padding-bottom: 1.5rem;
border-bottom: 1px solid var(--border-gray);
}
.award-item:last-child {
border-bottom: none;
}
.award-title {
font-size: 1.1rem;
font-weight: 600;
color: var(--text-dark);
margin-bottom: 0.5rem;
}
.award-issuer {
font-size: 0.9rem;
color: var(--text-gray);
margin-bottom: 0.5rem;
}
.award-description {
font-size: 0.95rem;
color: var(--text-gray);
}
/* Languages */
.languages-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1.5rem;
}
.language-item {
text-align: center;
padding: 1.5rem;
background: #f9fafb;
border-radius: 6px;
}
.language-name {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
.language-proficiency {
font-size: 0.9rem;
color: var(--text-gray);
margin-bottom: 0.5rem;
}
/* Footer */
footer {
text-align: center;
padding: 2rem;
color: rgba(255, 255, 255, 0.8);
font-size: 0.9rem;
}
/* Responsive */
@media (max-width: 768px) {
.cv-paper {
padding: 1.5rem;
}
.cv-name {
font-size: 2rem;
}
.cv-title {
font-size: 1.2rem;
}
.action-bar-content {
flex-direction: column;
padding: 1rem;
gap: 1rem;
}
.language-toggle {
width: 100%;
flex-wrap: wrap;
}
.lang-btn {
flex: 1;
min-width: 120px;
}
.export-actions {
width: 100%;
}
.export-btn {
width: 100%;
}
.error-toast {
bottom: 1rem;
right: 1rem;
left: 1rem;
max-width: none;
}
.experience-header,
.project-header,
.education-header {
flex-direction: column;
gap: 0.5rem;
}
.experience-period,
.project-period,
.education-period {
align-self: flex-start;
}
.skills-grid,
.certifications-grid,
.languages-grid {
grid-template-columns: 1fr;
}
}
/* Reduced motion support */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Print-specific hiding */
.no-print {
/* Will be hidden in print.css */
}
/* High contrast mode support */
@media (prefers-contrast: high) {
.lang-btn {
border-width: 3px;
}
.lang-btn:focus-visible,
.export-btn:focus-visible,
a:focus-visible {
outline-width: 3px;
}
}
-171
View File
@@ -1,171 +0,0 @@
<!DOCTYPE html>
<html lang="{{if eq .Lang "es"}}es{{else}}en{{end}}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="{{.CV.Personal.Name}} - {{.CV.Personal.Title}}">
<meta name="keywords" content="CV, Resume, {{.CV.Personal.Name}}, Developer, SAP, AI">
<meta name="author" content="{{.CV.Personal.Name}}">
<meta name="robots" content="index, follow">
<!-- Open Graph Meta Tags -->
<meta property="og:title" content="{{.CV.Personal.Name}} - Curriculum Vitae">
<meta property="og:description" content="{{.CV.Personal.Title}}">
<meta property="og:type" content="profile">
<meta property="og:url" content="{{.CV.Personal.Website}}">
<title>{{.CV.Personal.Name}} - Curriculum Vitae</title>
<!-- HTMX with Integrity Check -->
<script src="https://unpkg.com/htmx.org@1.9.10"
integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC"
crossorigin="anonymous"></script>
<!-- CSS -->
<link rel="stylesheet" href="/static/css/main.css">
<link rel="stylesheet" href="/static/css/print.css" media="print">
<!-- Fonts with Preload -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<!-- Favicon -->
<link rel="icon" type="image/png" href="/static/favicon.png">
<!-- HTMX Configuration -->
<meta name="htmx-config" content='{"timeout":5000,"defaultSwapStyle":"innerHTML","defaultSwapDelay":0,"defaultSettleDelay":20}'>
</head>
<body>
<!-- Language & Export Bar (hidden in print) -->
<div class="action-bar no-print" role="navigation" aria-label="Language and export controls">
<div class="action-bar-content">
<div class="language-toggle" role="group" aria-label="Language selection">
<button
class="lang-btn {{if eq .Lang "en"}}active{{end}}"
hx-get="/cv?lang=en"
hx-target="#cv-content"
hx-swap="innerHTML swap:200ms settle:200ms"
hx-indicator="#loading"
hx-push-url="/?lang=en"
hx-on::before-request="this.setAttribute('aria-busy', 'true')"
hx-on::after-request="this.setAttribute('aria-busy', 'false')"
aria-label="Switch to English"
aria-pressed="{{if eq .Lang "en"}}true{{else}}false{{end}}">
🇬🇧 English
</button>
<button
class="lang-btn {{if eq .Lang "es"}}active{{end}}"
hx-get="/cv?lang=es"
hx-target="#cv-content"
hx-swap="innerHTML swap:200ms settle:200ms"
hx-indicator="#loading"
hx-push-url="/?lang=es"
hx-on::before-request="this.setAttribute('aria-busy', 'true')"
hx-on::after-request="this.setAttribute('aria-busy', 'false')"
aria-label="Switch to Spanish"
aria-pressed="{{if eq .Lang "es"}}true{{else}}false{{end}}">
🇪🇸 Español
</button>
</div>
<div class="export-actions">
<button
class="export-btn"
onclick="window.print()"
aria-label="{{if eq .Lang "es"}}Descargar PDF del CV{{else}}Download CV as PDF{{end}}">
📄 {{if eq .Lang "es"}}Descargar PDF{{else}}Download PDF{{end}}
</button>
</div>
<span id="loading" class="htmx-indicator" role="status" aria-live="polite" aria-label="Loading">
<span class="loader" aria-hidden="true"></span>
<span class="sr-only">Loading...</span>
</span>
</div>
</div>
<!-- CV Content Container -->
<div class="cv-container">
<main id="cv-content"
class="cv-paper"
role="main"
aria-live="polite"
aria-atomic="false">
{{template "cv-content.html" .}}
</main>
</div>
<!-- Error Toast (hidden by default) -->
<div id="error-toast" class="error-toast no-print" role="alert" aria-live="assertive" style="display: none;">
<span id="error-message"></span>
<button onclick="this.parentElement.style.display='none'" aria-label="Close error message">×</button>
</div>
<!-- Footer (hidden in print) -->
<footer class="no-print" role="contentinfo">
<p>&copy; {{.CV.Meta.LastUpdated}} {{.CV.Personal.Name}} |
{{if eq .Lang "es"}}Última actualización{{else}}Last updated{{end}}: {{.CV.Meta.LastUpdated}}</p>
</footer>
<!-- HTMX Event Handlers -->
<script>
// Global error handler for HTMX requests
document.body.addEventListener('htmx:responseError', function(evt) {
const errorToast = document.getElementById('error-toast');
const errorMessage = document.getElementById('error-message');
errorMessage.textContent = '{{if eq .Lang "es"}}Error al cargar el contenido. Por favor, inténtelo de nuevo.{{else}}Failed to load content. Please try again.{{end}}';
errorToast.style.display = 'flex';
// Auto-hide after 5 seconds
setTimeout(() => {
errorToast.style.display = 'none';
}, 5000);
});
// Smooth scroll to top on language change
document.body.addEventListener('htmx:afterSwap', function(evt) {
if (evt.detail.target.id === 'cv-content') {
window.scrollTo({ top: 0, behavior: 'smooth' });
}
});
// Save language preference
document.body.addEventListener('htmx:afterRequest', function(evt) {
const url = new URL(evt.detail.xhr.responseURL);
const lang = url.searchParams.get('lang');
if (lang) {
localStorage.setItem('cv-lang', lang);
}
});
// Load saved language preference on page load
window.addEventListener('DOMContentLoaded', function() {
const savedLang = localStorage.getItem('cv-lang');
const currentLang = '{{.Lang}}';
if (savedLang && savedLang !== currentLang) {
document.querySelector(`[hx-get="/cv?lang=${savedLang}"]`)?.click();
}
});
// Keyboard shortcuts
document.addEventListener('keydown', function(evt) {
// Ctrl/Cmd + P for print
if ((evt.ctrlKey || evt.metaKey) && evt.key === 'p') {
evt.preventDefault();
window.print();
}
// Ctrl/Cmd + E for English, Ctrl/Cmd + S for Spanish
if (evt.ctrlKey || evt.metaKey) {
if (evt.key === 'e') {
evt.preventDefault();
document.querySelector('[hx-get="/cv?lang=en"]')?.click();
} else if (evt.key === 's' && evt.shiftKey) {
evt.preventDefault();
document.querySelector('[hx-get="/cv?lang=es"]')?.click();
}
}
});
</script>
</body>
</html>