Files
cv-site/templates/index.html
T

198 lines
9.5 KiB
HTML
Raw Normal View History

2025-10-20 08:54:21 +01:00
<!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">
2025-10-31 11:06:38 +00:00
<!-- Primary Meta Tags -->
<title>{{.CV.Personal.Name}} - {{if eq .Lang "es"}}Curriculum Vitae{{else}}Curriculum Vitae{{end}}</title>
<meta name="title" content="{{.CV.Personal.Name}} - {{if eq .Lang "es"}}CV Profesional{{else}}Professional CV{{end}}">
<meta name="description" content="{{.CV.Personal.Title}} | {{if eq .Lang "es"}}18 años de experiencia en desarrollo web, SAP CDC, React, Node.js, Go, HTMX y desarrollo asistido por IA{{else}}18 years of experience in web development, SAP CDC, React, Node.js, Go, HTMX and AI-assisted development{{end}}">
<meta name="keywords" content="{{if eq .Lang "es"}}CV, Curriculum Vitae, {{.CV.Personal.Name}}, Desarrollador FullStack, SAP CDC, React, Node.js, Go, HTMX, IA, Desarrollo Web, Consultor Técnico{{else}}CV, Resume, {{.CV.Personal.Name}}, FullStack Developer, SAP CDC, React, Node.js, Go, HTMX, AI, Web Development, Technical Consultant{{end}}">
<meta name="author" content="{{.CV.Personal.Name}}">
<meta name="robots" content="index, follow">
<link rel="canonical" href="{{.CanonicalURL}}">
<!-- Hreflang tags for international SEO -->
<link rel="alternate" hreflang="en" href="{{.AlternateEN}}">
<link rel="alternate" hreflang="es" href="{{.AlternateES}}">
<link rel="alternate" hreflang="x-default" href="https://juan.andres.morenorub.io/?lang=en">
2025-10-31 11:06:38 +00:00
<!-- Open Graph / Facebook -->
<meta property="og:type" content="profile">
<meta property="og:url" content="{{.CV.Personal.Website}}">
<meta property="og:title" content="{{.CV.Personal.Name}} - {{if eq .Lang "es"}}CV Profesional{{else}}Professional CV{{end}}">
<meta property="og:description" content="{{.CV.Personal.Title}} | {{if eq .Lang "es"}}Consultor Técnico Senior con 18 años de experiencia{{else}}Senior Technical Consultant with 18 years of experience{{end}}">
<meta property="og:image" content="{{.CV.Personal.Website}}/static/images/profile.jpg">
<meta property="og:locale" content="{{if eq .Lang "es"}}es_ES{{else}}en_US{{end}}">
<meta property="og:site_name" content="{{.CV.Personal.Name}}">
<meta property="profile:first_name" content="Juan Andrés">
<meta property="profile:last_name" content="Moreno Rubio">
<meta property="profile:username" content="txeo">
<!-- Social Media Card (Generic) -->
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="{{.CV.Personal.Name}} - {{if eq .Lang "es"}}CV Profesional{{else}}Professional CV{{end}}">
<meta name="twitter:description" content="{{.CV.Personal.Title}}">
<meta name="twitter:image" content="{{.CV.Personal.Website}}/static/images/profile.jpg">
<!-- HTMX Configuration -->
<meta name="htmx-config" content='{"timeout":5000,"defaultSwapStyle":"innerHTML","defaultSwapDelay":0,"defaultSettleDelay":20}'>
<!-- FOUC Prevention: Apply color theme before page render -->
<script>
(function() {
const theme = localStorage.getItem('color-theme-mode') || 'auto';
document.documentElement.setAttribute('data-color-theme', theme);
})();
</script>
2025-10-31 11:06:38 +00:00
<!-- HTMX with SRI (Subresource Integrity) -->
<script src="https://unpkg.com/htmx.org@1.9.10"
integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC"
crossorigin="anonymous"></script>
2025-10-20 08:54:21 +01:00
<!-- CV Core Functions - Regular JavaScript (global reusable functions) -->
<script src="/static/js/cv-functions.js"></script>
2025-11-14 21:38:09 +00:00
<!-- Hyperscript Functions - Must load BEFORE hyperscript library -->
<!-- ✅ NO def limit with latest hyperscript - organized by category -->
<script type="text/hyperscript" src="/static/hyperscript/utils._hs"></script>
<script type="text/hyperscript" src="/static/hyperscript/toggles._hs"></script>
<script type="text/hyperscript" src="/static/hyperscript/hover-sync._hs"></script>
<script type="text/hyperscript" src="/static/hyperscript/keyboard._hs"></script>
2025-11-14 21:38:09 +00:00
<!-- Color Theme System (JavaScript - hyperscript had parsing issues with colons in strings) -->
<script src="/static/js/color-theme.js"></script>
2025-11-12 22:54:46 +00:00
<!-- Hyperscript - Declarative event handling for enhanced interactivity -->
<script src="https://unpkg.com/hyperscript.org@0.9.14"></script>
2025-11-12 22:54:46 +00:00
<!-- Iconify - Load synchronously for immediate rendering -->
<!-- Using unpkg CDN (more reliable than code.iconify.design) -->
<script src="https://cdn.jsdelivr.net/npm/iconify-icon@2.1.0/dist/iconify-icon.min.js"></script>
<!-- CSS - Modular structure with native @import -->
2025-10-20 08:54:21 +01:00
<link rel="stylesheet" href="/static/css/main.css">
2025-10-31 11:06:38 +00:00
<!-- Fonts with Preload -->
2025-10-20 08:54:21 +01:00
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
2025-10-31 11:06:38 +00:00
<link rel="dns-prefetch" href="https://fonts.googleapis.com">
2025-10-20 08:54:21 +01:00
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
2025-10-31 11:06:38 +00:00
<!-- Structured Data (JSON-LD) -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Person",
"name": "{{.CV.Personal.Name}}",
"jobTitle": "{{.CV.Personal.Title}}",
"url": "{{.CV.Personal.Website}}",
"email": "{{.CV.Personal.Email}}",
"telephone": "{{.CV.Personal.Phone}}",
"address": {
"@type": "PostalAddress",
"addressLocality": "{{.CV.Personal.Location}}"
},
"sameAs": [
"{{.CV.Personal.LinkedIn}}",
"{{.CV.Personal.GitHub}}",
"{{.CV.Personal.Domestika}}"
2025-10-31 11:06:38 +00:00
],
"alumniOf": {
"@type": "EducationalOrganization",
"name": "Universidad de Extremadura"
},
"knowsAbout": [
"Web Development",
"SAP Customer Data Cloud",
"React",
"Node.js",
"Go",
"HTMX",
"AI-Assisted Development",
"Full Stack Development"
],
"worksFor": {
"@type": "Organization",
"name": "Olympic Broadcasting Services"
}
}
</script>
2025-10-20 08:54:21 +01:00
</head>
2025-11-12 23:07:44 +00:00
<body {{if .ThemeClean}}class="theme-clean"{{end}}
2025-11-15 15:59:54 +00:00
_="on load call initScrollBehavior()
on scroll from window call handleScroll()
on keydown
2025-11-17 08:34:50 +00:00
set tagName to event.target.tagName
set isInputField to (tagName is 'INPUT' or tagName is 'TEXTAREA')
if event.key is '?' and not event.ctrlKey and not event.metaKey and not event.altKey and not isInputField
halt the event then set modal to #shortcuts-modal then if modal then call modal.showModal() end
2025-11-17 08:34:50 +00:00
end
if (event.key is 'l' or event.key is 'L') and not event.ctrlKey and not event.metaKey and not event.altKey and not isInputField
halt the event then set lengthToggle to (#lengthToggle or #lengthToggleMenu)
then if lengthToggle then set lengthToggle's checked to (not lengthToggle's checked) then send change to lengthToggle end
2025-11-17 08:34:50 +00:00
end
if (event.key is 'i' or event.key is 'I') and not event.ctrlKey and not event.metaKey and not event.altKey and not isInputField
halt the event then set iconToggle to (#iconToggle or #iconToggleMenu)
then if iconToggle then set iconToggle's checked to (not iconToggle's checked) then send change to iconToggle end
2025-11-17 08:34:50 +00:00
end
if (event.key is 'v' or event.key is 'V') and not event.ctrlKey and not event.metaKey and not event.altKey and not isInputField
halt the event then set themeToggle to (#themeToggle or #themeToggleMenu)
then if themeToggle then set themeToggle's checked to (not themeToggle's checked) then send change to themeToggle end
2025-11-15 15:59:54 +00:00
end
end">
2025-11-12 19:54:56 +00:00
<!-- Top anchor for back-to-top link -->
<div id="top"></div>
2025-11-12 18:26:18 +00:00
{{template "action-bar" .}}
{{template "hamburger-menu" .}}
{{template "color-theme-switcher" .}}
<!-- Zoom Wrapper (for zoom functionality) -->
<div id="zoom-wrapper" class="zoom-wrapper">
<!-- CV Content Container -->
<div class="cv-container">
2025-11-12 18:55:06 +00:00
{{template "cv-content.html" .}}
</div>
2025-11-12 18:26:18 +00:00
</div> <!-- End zoom-wrapper -->
{{template "page-footer" .}}
2025-11-12 18:26:18 +00:00
{{template "error-toast" .}}
{{template "pdf-toast" .}}
2025-11-12 18:26:18 +00:00
{{template "back-to-top" .}}
2025-11-17 08:34:50 +00:00
{{template "info-button" .}}
{{template "download-button" .}}
{{template "print-friendly-button" .}}
{{template "zoom-toggle-button" .}}
{{template "shortcuts-button" .}}
2025-11-12 18:26:18 +00:00
{{template "info-modal" .}}
2025-11-15 15:59:54 +00:00
{{template "shortcuts-modal" .}}
2025-11-12 18:26:18 +00:00
{{template "pdf-modal" .}}
{{template "zoom-control" .}}
<!-- External JavaScript - CSP Compliant -->
<script src="/static/js/main.js"></script>
2025-11-09 15:02:31 +00:00
<!-- Matomo Analytics - First-party subdomain to bypass ad blockers -->
<script>
2025-11-09 15:02:31 +00:00
var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="https://matomo.morenorub.io/";
2025-11-09 15:02:31 +00:00
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '4']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true;
g.src=u+'matomo.js';
s.parentNode.insertBefore(g,s);
2025-11-09 15:02:31 +00:00
})();
</script>
2025-11-09 15:02:31 +00:00
<!-- End Matomo Code -->
2025-10-20 08:54:21 +01:00
</body>
</html>