2025-10-20 08:54:21 +01:00
# Quick Start: Critical Improvements
This guide shows you the fastest path to production-ready status (85% → 95% in ~2 hours).
## 🚀 30-Minute Priority Fixes
### 1. Browser History & Transitions (5 minutes)
**File: ** `/Users/txeo/Git/yo/cv/templates/index.html`
**Find lines 27-42 (language buttons) and update: **
``` html
< 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-push-url = "/?lang=en"
hx-indicator = "#loading" >
🇬🇧 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-push-url = "/?lang=es"
hx-indicator = "#loading" >
🇪🇸 Español
< / button >
```
**Changes: **
- Added `hx-swap="innerHTML swap:200ms settle:200ms"` (smooth transitions)
- Added `hx-push-url="/?lang=XX"` (browser history)
---
### 2. ARIA Attributes (10 minutes)
**File: ** `/Users/txeo/Git/yo/cv/templates/index.html`
**Update the action bar section (lines 24-57): **
``` html
< 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-push-url = "/?lang=en"
hx-indicator = "#loading"
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-push-url = "/?lang=es"
hx-indicator = "#loading"
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" > < / span >
< / span >
< / div >
< / div >
```
**Update CV content container (lines 60-64): **
``` html
< div class = "cv-container" >
< main id = "cv-content"
class = "cv-paper"
role = "main"
aria-live = "polite" >
{{template "cv-content.html" .}}
< / main >
< / div >
```
---
### 3. Error Handling (10 minutes)
**File: ** `/Users/txeo/Git/yo/cv/templates/index.html`
**Add before closing `</body>` tag (after footer): **
``` html
<!-- Error Toast -->
< div id = "error-toast" class = "error-toast no-print" role = "alert" style = "display: none;" >
< span id = "error-message" > < / span >
< button onclick = "this.parentElement.style.display='none'" aria-label = "Close error message" > × < / button >
< / div >
<!-- HTMX Error Handler -->
< script >
// Global error handler
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' ;
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' } ) ;
}
} ) ;
< / script >
```
**File: ** `/Users/txeo/Git/yo/cv/static/css/main.css`
**Add at the end of the file: **
``` css
/* Error Toast */
. error-toast {
position : fixed ;
bottom : 2 rem ;
right : 2 rem ;
background : #fee2e2 ;
color : #dc2626 ;
padding : 1 rem 1.5 rem ;
border-radius : 8 px ;
border-left : 4 px solid #dc2626 ;
box-shadow : var ( - - shadow - lg ) ;
display : flex ;
align-items : center ;
gap : 1 rem ;
max-width : 400 px ;
z-index : 1000 ;
animation : slideIn 0.2 s 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.5 rem ;
color : #dc2626 ;
cursor : pointer ;
padding : 0 ;
width : 24 px ;
height : 24 px ;
display : flex ;
align-items : center ;
justify-content : center ;
transition : opacity 0.2 s ;
}
. error-toast button : hover {
opacity : 0.7 ;
}
/* Smooth transitions */
. cv-paper {
transition : opacity 200 ms ;
}
. cv-paper . htmx-swapping {
opacity : 0 ;
}
```
---
### 4. HTMX Configuration (5 minutes)
**File: ** `/Users/txeo/Git/yo/cv/templates/index.html`
**Add in `<head>` section after meta viewport: **
``` html
<!-- HTMX Configuration -->
< meta name = "htmx-config" content = '{"timeout":5000,"defaultSwapStyle":"innerHTML","defaultSwapDelay":0,"defaultSettleDelay":20}' >
```
---
## ⏱️ 1-Hour Enhancement: SEO & Meta Tags
**File: ** `/Users/txeo/Git/yo/cv/templates/index.html`
**Replace entire `<head>` section: **
``` html
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
<!-- SEO Meta Tags -->
< meta name = "description" content = "{{.CV.Personal.Name}} - {{.CV.Personal.Title}}" >
< meta name = "keywords" content = "CV, Resume, {{.CV.Personal.Name}}, Developer, SAP, AI, HTMX, Go, FullStack" >
< 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}}" >
<!-- Twitter Card -->
< meta name = "twitter:card" content = "summary" >
< meta name = "twitter:title" content = "{{.CV.Personal.Name}}" >
< meta name = "twitter:description" content = "{{.CV.Personal.Title}}" >
< title > {{.CV.Personal.Name}} - Curriculum Vitae< / title >
<!-- HTMX Configuration -->
< meta name = "htmx-config" content = '{"timeout":5000,"defaultSwapStyle":"innerHTML","defaultSwapDelay":0,"defaultSettleDelay":20}' >
<!-- HTMX with SRI -->
< 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" >
<!-- 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}}" ,
"sameAs" : [
"{{.CV.Personal.LinkedIn}}" ,
"{{.CV.Personal.GitHub}}"
] ,
"address" : {
"@type" : "PostalAddress" ,
"addressLocality" : "{{.CV.Personal.Location}}"
} ,
"email" : "{{.CV.Personal.Email}}" ,
"telephone" : "{{.CV.Personal.Phone}}"
}
< / script >
< / head >
```
---
## 🔒 2-Hour Enhancement: Security Headers
**Create file: ** `/Users/txeo/Git/yo/cv/middleware/security.go`
``` go
package middleware
import (
"net/http"
"os"
)
// SecurityHeaders adds security headers to all responses
func SecurityHeaders ( next http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
// Prevent clickjacking
w . Header ( ) . Set ( "X-Frame-Options" , "DENY" )
w . Header ( ) . Set ( "X-Content-Type-Options" , "nosniff" )
w . Header ( ) . Set ( "X-XSS-Protection" , "1; mode=block" )
// Content Security Policy
csp := "default-src 'self'; " +
"script-src 'self' 'unsafe-inline' https://unpkg.com; " +
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; " +
"font-src 'self' https://fonts.gstatic.com; " +
"img-src 'self' data:; " +
"connect-src 'self'"
w . Header ( ) . Set ( "Content-Security-Policy" , csp )
// Referrer Policy
w . Header ( ) . Set ( "Referrer-Policy" , "strict-origin-when-cross-origin" )
// Permissions Policy
w . Header ( ) . Set ( "Permissions-Policy" , "geolocation=(), microphone=(), camera=()" )
// HTTPS-only in production
if os . Getenv ( "GO_ENV" ) == "production" {
w . Header ( ) . Set ( "Strict-Transport-Security" , "max-age=31536000; includeSubDomains" )
}
next . ServeHTTP ( w , r )
} )
}
// CORS allows cross-origin requests (if needed)
func CORS ( next http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
origin := os . Getenv ( "ALLOWED_ORIGIN" )
if origin == "" {
origin = "*" // Development only
}
w . Header ( ) . Set ( "Access-Control-Allow-Origin" , origin )
w . Header ( ) . Set ( "Access-Control-Allow-Methods" , "GET, OPTIONS" )
w . Header ( ) . Set ( "Access-Control-Allow-Headers" , "Content-Type" )
if r . Method == "OPTIONS" {
w . WriteHeader ( http . StatusOK )
return
}
next . ServeHTTP ( w , r )
} )
}
```
**Update file: ** `/Users/txeo/Git/yo/cv/main.go`
**Add imports: **
``` go
import (
// ... existing imports
"yourproject/middleware" // Update with your module path
)
```
**Update main() function to use middleware: **
``` go
func main ( ) {
// ... existing setup code
// Apply middleware
http . Handle ( "/" , middleware . SecurityHeaders ( http . HandlerFunc ( handleHome ) ) )
http . Handle ( "/cv" , middleware . SecurityHeaders ( http . HandlerFunc ( handleCV ) ) )
http . Handle ( "/export/pdf" , middleware . SecurityHeaders ( http . HandlerFunc ( handlePDFExport ) ) )
// ... rest of main()
}
```
**Or create a middleware chain: **
``` go
func main ( ) {
// ... existing setup code
// Create base handler
mux := http . NewServeMux ( )
mux . HandleFunc ( "/" , handleHome )
mux . HandleFunc ( "/cv" , handleCV )
mux . HandleFunc ( "/export/pdf" , handlePDFExport )
// Static files
mux . Handle ( "/static/" , http . StripPrefix ( "/static/" , http . FileServer ( http . Dir ( "static" ) ) ) )
// Apply middleware chain
handler := middleware . SecurityHeaders (
middleware . CORS ( mux ) ,
)
// ... start server with handler
2025-10-29 14:04:24 +00:00
log . Fatal ( http . ListenAndServe ( ":1999" , handler ) )
2025-10-20 08:54:21 +01:00
}
```
---
## ✅ Testing Your Improvements
### 1. Test Browser History
``` bash
# Start server
go run main.go
# Open browser, click language buttons
# Press browser back button - should work!
```
### 2. Test Error Handling
``` bash
# Stop the server
# In browser, click language button
# Should see error toast!
```
### 3. Test Accessibility
``` bash
# Use keyboard only:
# Tab to language buttons
# Press Enter to activate
# Tab to export button
# Press Enter to print
```
### 4. Test Security Headers
``` bash
2025-10-29 14:04:24 +00:00
curl -I http://localhost:1999/
2025-10-20 08:54:21 +01:00
# Should see security headers in response
```
---
## 📊 Before vs After
### Before (Current)
- ❌ No browser history on language change
- ❌ No error handling
- ❌ Limited accessibility
- ⚠️ Missing SEO meta tags
- ⚠️ No security headers
- ✅ Excellent performance
### After (30 minutes)
- ✅ Browser history works
- ✅ Error handling with toast
- ✅ ARIA attributes for accessibility
- ✅ Smooth transitions
- ✅ HTMX timeout configured
- ✅ Still excellent performance
### After (2 hours)
- ✅ All of the above PLUS:
- ✅ Complete SEO meta tags
- ✅ Structured data (JSON-LD)
- ✅ Security headers
- ✅ SRI for external scripts
- ✅ Production-ready!
---
## 🎯 Next Steps
1. **Apply 30-minute fixes ** ← Start here!
2. **Test in browser **
3. **Apply 1-hour SEO enhancements **
4. **Apply 2-hour security enhancements **
5. **Run Lighthouse audit **
6. **Deploy to production! **
---
## 💡 Pro Tips
1. **Backup first: **
```bash
cp templates/index.html templates/index.html.backup
cp static/css/main.css static/css/main.css.backup
` ``
2. **Test incrementally:**
- Apply one fix at a time
- Test in browser
- Commit to git
- Move to next fix
3. **Use the enhanced templates:**
` ``bash
# I've already created fully enhanced versions:
mv templates/index-improved.html templates/index.html
mv static/css/main-enhanced.css static/css/main.css
` ``
4. **Validate with tools:**
2025-10-29 14:04:24 +00:00
- Lighthouse: ` lighthouse http://localhost:1999`
2025-10-20 08:54:21 +01:00
- WAVE: Install browser extension
- axe DevTools: Install browser extension
---
## 🚀 Ready to Go!
These quick fixes will take you from **85% → 95% production-ready** in just 30 minutes!
For the complete guide, see: ` HTMX-PRODUCTION-RECOMMENDATIONS.md`