Files
cv-site/QUICK-START-IMPROVEMENTS.md
T

517 lines
14 KiB
Markdown
Raw Normal View History

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: 2rem;
right: 2rem;
background: #fee2e2;
color: #dc2626;
padding: 1rem 1.5rem;
border-radius: 8px;
border-left: 4px solid #dc2626;
box-shadow: var(--shadow-lg);
display: flex;
align-items: center;
gap: 1rem;
max-width: 400px;
z-index: 1000;
animation: slideIn 0.2s 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: #dc2626;
cursor: pointer;
padding: 0;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
transition: opacity 0.2s;
}
.error-toast button:hover {
opacity: 0.7;
}
/* Smooth transitions */
.cv-paper {
transition: opacity 200ms;
}
.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
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
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:**
- 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`