2025-11-09 13:54:31 +00:00
# Customization Guide
**Note ** : This is my personal CV website. While the code is open-source (MIT license), this guide documents my own customizations. The site is subject to modifications without notice, and I don't intend for others to use this as a template - it's publicly available code, but it's designed for my personal use.
## Table of Contents
- [Introduction ](#introduction )
- [Prerequisites ](#prerequisites )
- [Quick Customization ](#quick-customization )
- [Content Customization ](#content-customization )
- [Personal Information ](#personal-information )
- [JSON Schema Explained ](#json-schema-explained )
- [Adding/Removing Sections ](#addingremoving-sections )
- [Visual Customization ](#visual-customization )
- [Colors & Fonts ](#colors--fonts )
- [Layout Changes ](#layout-changes )
- [Branding ](#branding )
- [Template Customization ](#template-customization )
2025-11-09 19:33:20 +00:00
- [Analytics Configuration ](#analytics-configuration )
2025-11-12 16:04:43 +00:00
- [Option 1: Configure Your Own Analytics ](#option-1-configure-your-own-analytics )
- [Option 2: Remove Analytics Entirely ](#option-2-remove-analytics-entirely )
- [Option 3: Use Alternative Analytics Service ](#option-3-use-alternative-analytics-service )
2025-11-09 19:33:20 +00:00
- [Option 3: Use Alternative Analytics ](#option-3-use-google-analytics-or-other-service )
2025-11-09 13:54:31 +00:00
- [Advanced Customization ](#advanced-customization )
- [Testing Your Changes ](#testing-your-changes )
- [Examples ](#examples )
---
## Introduction
This CV/Resume application is designed to be easily customizable. You can adapt it for your own CV by modifying JSON files, templates, and styles without deep Go programming knowledge.
**Architecture Overview ** :
- **Data**: JSON files (`data/cv-en.json` , `data/cv-es.json` )
- **Models**: Go structs (`internal/models/cv.go` ) define data structure
- **Templates**: Go HTML templates (`templates/*.html` ) render the CV
- **Styles**: CSS (`static/css/main.css` ) controls appearance
- **Assets**: Images, fonts (`static/` directory)
**Customization Levels ** :
1. **Basic ** : Edit JSON files only (name, experience, skills)
2. **Intermediate ** : Modify CSS styles and add images
3. **Advanced ** : Change templates and Go models
---
## Prerequisites
### Knowledge Requirements
- **Basic**: JSON syntax, file editing
- **Intermediate**: HTML/CSS, command-line basics
- **Advanced**: Go templates, basic Go programming
### Tools Needed
- **Text editor**: VS Code, Sublime Text, or any editor
2025-11-18 19:32:28 +00:00
- **Go 1.25.1+**: For building and testing (see [8-DEPLOYMENT.md ](8-DEPLOYMENT.md ))
2025-11-09 13:54:31 +00:00
- **Git**: For version control (optional but recommended)
- **Browser**: For testing (Chrome/Firefox recommended)
### Optional Tools
- **JSON validator**: [JSONLint ](https://jsonlint.com/ )
- **Image editor**: For logo/photo preparation
- **Make**: For using Makefile commands
---
## Quick Customization
**Get started in 5 minutes ** :
``` bash
# 1. Clone or download the project
git clone https://github.com/juanatsap/cv-site.git my-cv
cd my-cv
# 2. Edit your information
nano data/cv-en.json # or use your favorite editor
# 3. Replace your photo
cp ~/my-photo.jpg static/images/profile/dni.jpeg
# 4. Test locally
make dev
# 5. Open browser
open http://localhost:1999
```
**Minimal changes to make it yours ** :
1. Replace `personal` section in `data/cv-en.json`
2. Replace `summary` section
3. Replace `experience` section with your jobs
4. Replace `education` section
5. Update `skills` section
6. Replace profile photo
2025-11-12 16:04:43 +00:00
7. **Configure or remove analytics ** (see [Analytics Configuration ](#analytics-configuration ) below)
2025-11-09 13:54:31 +00:00
---
## Content Customization
### Personal Information
Edit `data/cv-en.json` (and `data/cv-es.json` for Spanish):
**Location ** : Top of JSON file, `personal` object
``` json
{
"personal" : {
"name" : "Your Full Name" ,
"title" : "Your Professional Title" ,
"location" : "Your City, Country" ,
"email" : "your.email@example.com" ,
"phone" : "+1 234 567 8900" ,
"dateOfBirth" : "1990-01-01" ,
"placeOfBirth" : "Your Birthplace" ,
"citizenship" : "Your Nationality" ,
"linkedin" : "https://www.linkedin.com/in/your-profile" ,
"github" : "https://github.com/yourusername" ,
"domestika" : "https://www.domestika.org/en/yourusername" ,
"website" : "https://yourwebsite.com" ,
"photo" : "/static/images/profile.jpg"
}
}
```
**Field Descriptions ** :
- `name` : Full name (displayed prominently)
- `title` : Job title or professional tagline
- `location` : Current location
- `email` : Contact email (clickable in CV)
- `phone` : Phone number with country code
- `dateOfBirth` : Birth date (YYYY-MM-DD format)
- `placeOfBirth` : Birthplace
- `citizenship` : Nationality/citizenship
- `linkedin` , `github` , `domestika` , `website` : Social/professional links
- `photo` : Path to profile photo (relative to project root)
**Tips ** :
- Use **consistent formatting ** across English and Spanish versions
- Keep URLs **absolute ** (include `https://` )
- Use **international phone format ** (+XX XXX XXX XXXX)
- Photo should be **square ** (400x400px minimum) for best results
---
### JSON Schema Explained
The CV data follows a structured schema. Each section has specific fields.
#### Summary Section
``` json
{
"summary" : "Your professional summary. 2-3 sentences highlighting your expertise, experience, and career goals. This appears prominently at the top of your CV."
}
```
**Tips ** :
- Keep it **concise ** (100-150 words)
- Highlight **key achievements ** and expertise
- Tailor to your **target audience **
---
#### Experience Section
**Structure ** :
``` json
{
"experience" : [
{
"position" : "Job Title" ,
"company" : "Company Name" ,
"companyURL" : "https://company.com" , // Optional
"companyLogo" : "company-logo.png" , // Optional, in static/images/companies/
"location" : "City, Country" ,
"startDate" : "2020-01" , // YYYY-MM format
"endDate" : "2023-06" , // Or "present"
"current" : false , // true if still working here
"expired" : false , // Optional, true if company closed
"shortDescription" : "Brief one-line summary for compact view" ,
"responsibilities" : [
"Responsibility or achievement 1" ,
"Responsibility or achievement 2" ,
"Use bullet points for clarity"
] ,
"technologies" : [
"Technology 1" ,
"Technology 2" ,
"List relevant tech stack"
] ,
"highlights" : [ // Optional
"Major achievement 1" ,
"Major achievement 2"
]
}
]
}
```
**Field Details ** :
- `position` : Job title
- `company` : Company name
- `companyURL` : Company website (optional, makes company name clickable)
- `companyLogo` : Logo filename (place in `static/images/companies/` )
- `location` : Office location
- `startDate` : Start date (YYYY-MM format)
- `endDate` : End date or `"present"` for current job
- `current` : Boolean, `true` if still employed
- `expired` : Boolean, `true` if company no longer exists (grays out logo)
- `shortDescription` : One-liner for short CV version (HTML allowed)
- `responsibilities` : Array of bullet points (HTML allowed)
- `technologies` : Array of technologies used
- `highlights` : Optional array of major achievements
**HTML in Descriptions ** :
You can use HTML tags:
``` json
"shortDescription" : "Led development of <a href='https://example.com' target='_blank' rel='noopener noreferrer'>major platform</a> serving 1M+ users."
```
**Adding Company Logos ** :
1. Place logo in `static/images/companies/`
2. Reference filename in `companyLogo` field
3. Recommended size: 100x100px, PNG with transparency
4. Fallback icon appears if logo missing
---
#### Education Section
``` json
{
"education" : [
{
"degree" : "Bachelor's Degree in Computer Science" ,
"institution" : "University Name" ,
"location" : "City, Country" ,
"startDate" : "2015-09" ,
"endDate" : "2019-06" ,
"field" : "Computer Science and Engineering"
}
]
}
```
**Tips ** :
- List **highest degree first **
- Include **relevant coursework ** in degree name if needed
- Use `field` for specialization
---
#### Skills Section
**Two types ** : Technical skills (with sidebar placement) and soft skills
``` json
{
"skills" : {
"technical" : [
{
"category" : "Programming Languages" ,
"proficiency" : 5 , // 1-5 scale (not displayed, for internal use)
"sidebar" : "left" , // "left" or "right" or omit for main content
"items" : [
"JavaScript (ES6+)" ,
"Python" ,
"Go"
]
} ,
{
"category" : "Frontend Technologies" ,
"proficiency" : 5 ,
"sidebar" : "left" ,
"items" : [
"React" ,
"HTMX" ,
"CSS3"
]
}
] ,
"soft_skills" : [
"Leadership & Team Management" ,
"Problem-Solving" ,
"Communication"
]
}
}
```
**Sidebar Layout ** :
- **Left sidebar**: Skills displayed on page 1 left side
- **Right sidebar**: Skills displayed on page 2 right side
- **No sidebar**: Skills displayed in main content area
**Organizing Skills ** :
1. Group by **category ** (e.g., "Programming Languages", "Databases")
2. Order by **importance ** (most important categories first)
3. Use **specific names ** (e.g., "PostgreSQL" not just "SQL")
4. Balance **left/right sidebars ** for visual symmetry
---
#### Projects Section
``` json
{
"projects" : [
{
"title" : "Full Project Title" , // Used if no projectName
"projectName" : "Project Name" , // Optional: linkable part
"projectDesc" : "Project Description" , // Optional: non-linkable part
"url" : "https://project.com" , // Optional
"projectLogo" : "project-logo.png" , // Optional, in static/images/projects/
"gitRepoUrl" : "/path/to/local/repo" , // Optional: for dynamic dates
"location" : "City or 'Online'" ,
"startDate" : "2023" , // Optional: YYYY or YYYY-MM
"current" : true , // true if ongoing
"maintainedBy" : "Company Name" , // Optional: if transferred
"technologies" : [
"Tech 1" ,
"Tech 2"
] ,
"shortDescription" : "One-line project summary" ,
"responsibilities" : [
"What you built or contributed" ,
"Use bullet points"
]
}
]
}
```
**Dynamic Dates ** (Advanced):
- Use `gitRepoUrl` to point to a local git repository
- Application will extract first commit date as start date
- Useful for open-source projects where you want automatic dating
**Project Logos ** :
- Place in `static/images/projects/`
- Recommended: 80x80px, PNG with transparency
---
#### Languages Section
``` json
{
"languages" : [
{
"language" : "English" ,
"proficiency" : "Native" ,
"level" : 5 , // 1-5 scale (not displayed)
"detail" : "Oral (Advanced) Written (Advanced)" // Optional
} ,
{
"language" : "Spanish" ,
"proficiency" : "Professional Working Proficiency" ,
"level" : 4
}
]
}
```
**Proficiency Levels ** (suggested):
- Native
- Bilingual/Fluent
- Professional Working Proficiency
- Limited Working Proficiency
- Elementary/Comprehension
---
#### Courses/Certifications Sections
**Courses ** :
``` json
{
"courses" : [
{
"title" : "Course Title" ,
"institution" : "Platform or Institution" ,
"courseLogo" : "platform-logo.png" , // Optional, in static/images/courses/
"location" : "Online or City" ,
"date" : "2024-03" , // YYYY-MM or range
"duration" : "40 hours" ,
"shortDescription" : "Brief course overview" ,
"responsibilities" : [ // Optional: detailed course content
"Topic 1 covered" ,
"Topic 2 covered"
]
}
]
}
```
**Certifications ** :
``` json
{
"certifications" : [
{
"name" : "Certification Name" ,
"issuer" : "Issuing Organization" ,
"date" : "2024-01" ,
"description" : "What this certification covers"
}
]
}
```
**Course Logos ** :
- Place in `static/images/courses/`
- Examples: Codecademy, LinkedIn Learning, Coursera logos
---
#### Awards Section
``` json
{
"awards" : [
{
"title" : "Award Title" ,
"issuer" : "Issuing Organization" ,
"date" : "09 2023" , // MM YYYY format
"shortDescription" : "Brief description of award" ,
"responsibilities" : [ // Optional: what you did to earn it
"Achievement 1" ,
"Achievement 2"
] ,
"awardLogo" : "award-logo.png" // Optional, in static/images/companies/
}
]
}
```
---
#### References Section
``` json
{
"references" : [
{
"title" : "Full reference text" ,
"url" : "https://example.com" ,
"type" : "recommendation" , // recommendation, portfolio, profile, cv, presentation
"textBefore" : "Text before link" , // Optional
"linkText" : "Clickable text" , // Optional: bold linked text
"textAfter" : "text after link" // Optional
}
]
}
```
**Types ** :
- `recommendation` : Reference letters
- `portfolio` : Online portfolio
- `profile` : LinkedIn, GitHub, etc.
- `cv` : Other CV versions
- `presentation` : Presentation letters
**Example rendering ** :
```
Text before link Clickable text text after link
```
---
#### Other Section
``` json
{
"other" : {
"driverLicense" : "<strong>Type B</strong>" // HTML allowed
}
}
```
Add any miscellaneous information here.
---
#### Meta Section
``` json
{
"meta" : {
"version" : "2025-11-09" , // Your CV version
"lastUpdated" : "2025-11-08" , // Last update date
"format" : "JSON Resume Extended" ,
"language" : "en" // "en" or "es"
}
}
```
Update `lastUpdated` when you make changes.
---
### Adding/Removing Sections
#### Removing Sections
**Option 1 ** : Empty the array/object
``` json
{
"awards" : [ ] ,
"courses" : [ ]
}
```
Templates automatically hide empty sections.
**Option 2 ** : Remove from template
Edit `templates/cv-content.html` and delete the section:
``` html
<!-- Remove this entire block -->
{{if .CV.Awards}}
< section id = "awards" class = "cv-section" >
...
< / section >
{{end}}
```
#### Adding New Sections
**Step 1 ** : Add to JSON
``` json
{
"volunteering" : [
{
"organization" : "Organization Name" ,
"role" : "Volunteer Role" ,
"startDate" : "2020-01" ,
"endDate" : "present" ,
"description" : "What you did"
}
]
}
```
**Step 2 ** : Add to Go model (`internal/models/cv.go` )
``` go
type CV struct {
// ... existing fields ...
Volunteering [ ] Volunteering ` json:"volunteering" `
}
type Volunteering struct {
Organization string ` json:"organization" `
Role string ` json:"role" `
StartDate string ` json:"startDate" `
EndDate string ` json:"endDate" `
Description string ` json:"description" `
}
```
**Step 3 ** : Add to template (`templates/cv-content.html` )
``` html
<!-- Add wherever you want it to appear -->
{{if .CV.Volunteering}}
< section id = "volunteering" class = "cv-section" >
< details open >
< summary >
< h3 class = "section-title" >
< iconify-icon icon = "mdi:hand-heart" width = "24" height = "24" class = "section-icon" > < / iconify-icon >
{{if eq .Lang "es"}}Voluntariado{{else}}Volunteering{{end}}
< / h3 >
< / summary >
{{range .CV.Volunteering}}
< div class = "volunteering-item" >
< strong > {{.Role}} - {{.Organization}}< / strong > < br >
< small > {{.StartDate}} / {{.EndDate}}< / small >
< p > {{.Description}}< / p >
< / div >
{{end}}
< / details >
< / section >
{{end}}
```
**Step 4 ** : Rebuild and test
``` bash
make build
make dev
```
---
## Visual Customization
### Colors & Fonts
**Location ** : `static/css/main.css`
#### Color Scheme
**CSS Variables ** (lines 6-15):
``` css
: root {
--bg-gray : rgb ( 82 , 86 , 89 ) ; /* Page background */
--sidebar-gray : #d1d4d2 ; /* Sidebar background */
--black-bar : #2b2b2b ; /* Top action bar */
--paper-white : #ffffff ; /* CV paper background */
--text-dark : rgb ( 0 , 0 , 0 ) ; /* Main text */
--text-gray : rgb ( 51 , 51 , 51 ) ; /* Secondary text */
--accent-blue : #0066cc ; /* Links and accents */
--border-gray : #dddddd ; /* Borders */
}
```
**Changing Colors ** :
``` css
/* Example: Blue theme */
: root {
--bg-gray : #1a365d ; /* Dark blue background */
--sidebar-gray : #e3f2fd ; /* Light blue sidebar */
--accent-blue : #2196f3 ; /* Bright blue links */
--black-bar : #0d47a1 ; /* Deep blue bar */
}
/* Example: Dark theme */
: root {
--bg-gray : #1e1e1e ; /* Dark background */
--sidebar-gray : #2d2d2d ; /* Dark sidebar */
--paper-white : #252526 ; /* Dark paper */
--text-dark : #d4d4d4 ; /* Light text */
--text-gray : #9d9d9d ; /* Gray text */
--accent-blue : #569cd6 ; /* Blue accent */
}
```
#### Fonts
**Current fonts ** (line 4):
``` css
@ import url ( 'https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500;600;700&family=Source+Sans+Pro:wght@400;600&family=Inter:wght@400;500;600;700&display=swap' ) ;
```
**Changing fonts ** :
1. **Choose fonts ** from [Google Fonts ](https://fonts.google.com/ )
2. **Update import ** :
``` css
@ import url ( 'https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&family=Open+Sans:wght@400;600&display=swap' ) ;
```
3. **Update font family ** (line 24):
``` css
body {
font-family : 'Roboto' , 'Open Sans' , - apple-system , system-ui , sans-serif ;
}
```
4. **Customize headings ** (add after body):
``` css
h1 , h2 , h3 {
font-family : 'Playfair Display' , serif ;
}
```
**Font sizes ** :
``` css
/* Increase base font size */
body {
font-size : 18 px ; /* Default: 16px */
}
/* Adjust specific elements */
. cv-name {
font-size : 2.5 rem ; /* Larger name */
}
. section-title {
font-size : 1.3 rem ; /* Larger section titles */
}
```
---
### Layout Changes
#### Page Width
**Current ** : A4 paper size (210mm x 297mm)
**Wider layout ** :
``` css
. cv-page {
max-width : 250 mm ; /* Wider (default: fit to A4) */
min-height : 330 mm ; /* Taller */
}
```
**US Letter ** :
``` css
. cv-page {
max-width : 8.5 in ; /* Letter width */
min-height : 11 in ; /* Letter height */
}
```
#### Grid Adjustments
**Sidebar widths ** (find `.page-content` section):
``` css
. page-content {
display : grid ;
grid-template-columns : 200 px 1 fr ; /* Left sidebar | Main */
}
/* Page 2: Main | Right sidebar */
. page-2 . page-content {
grid-template-columns : 1 fr 200 px ; /* Make sidebar wider */
}
```
**Adjust gap ** :
``` css
. page-content {
gap : 25 px ; /* Space between sidebar and main (default: 20px) */
}
```
#### Responsive Breakpoints
**Mobile view ** (add to end of CSS):
``` css
@ media screen and ( max-width : 768px ) {
. page-content {
grid-template-columns : 1 fr ; /* Stack vertically */
}
. cv-sidebar {
order : 2 ; /* Sidebars after main content */
}
. cv-main {
order : 1 ; /* Main content first */
}
}
```
#### Print Styles
**Current print styles ** handle page breaks. Customize:
``` css
@ media print {
body {
background : white ; /* No background texture when printing */
}
. cv-page {
box-shadow : none ; /* Remove shadow for printing */
margin : 0 ;
page-break-after : always ;
}
/* Hide elements when printing */
. action-bar {
display : none ;
}
/* Prevent page breaks inside elements */
. experience-item ,
. project-item {
page-break-inside : avoid ;
}
}
```
---
### Branding
#### Adding Your Logo
**Step 1 ** : Prepare logo
- Format: PNG with transparency preferred
- Size: 200x80px (width x height)
- Location: `static/images/logo.png`
**Step 2 ** : Add to template (`templates/cv-content.html` )
Add after header:
``` html
< div class = "cv-header" >
< div class = "cv-logo" >
< img src = "/static/images/logo.png" alt = "Your Brand Logo" >
< / div >
<!-- Existing header content -->
< / div >
```
**Step 3 ** : Style logo (`static/css/main.css` ):
``` css
. cv-logo {
text-align : center ;
margin-bottom : 20 px ;
}
. cv-logo img {
max-width : 200 px ;
height : auto ;
}
```
#### Favicon Replacement
**Current favicon ** : Browser tab icon
**Replace ** :
1. Create favicon (16x16, 32x32, 48x48 px)
2. Use [Favicon Generator ](https://realfavicongenerator.net/ )
3. Place in `static/images/favicon/`
4. Update in `templates/index.html` :
``` html
< head >
< link rel = "icon" type = "image/png" sizes = "32x32" href = "/static/images/favicon/favicon-32x32.png" >
< link rel = "icon" type = "image/png" sizes = "16x16" href = "/static/images/favicon/favicon-16x16.png" >
< link rel = "apple-touch-icon" sizes = "180x180" href = "/static/images/favicon/apple-touch-icon.png" >
< / head >
```
#### Custom Icons
**Current ** : Using [Iconify ](https://iconify.design/ )
**Change icons ** in templates:
``` html
<!-- Old -->
< iconify-icon icon = "mdi:office-building" width = "24" height = "24" > < / iconify-icon >
<!-- New (browse icons at iconify.design) -->
< iconify-icon icon = "fa6-solid:building" width = "24" height = "24" > < / iconify-icon >
< iconify-icon icon = "carbon:user-avatar" width = "24" height = "24" > < / iconify-icon >
```
**Use custom icons ** :
Replace with `<img>` tags:
``` html
< img src = "/static/images/icons/custom-icon.svg" alt = "Icon" class = "section-icon" >
```
---
## Template Customization
Templates use **Go template syntax ** . Basic knowledge helps but isn't required for simple changes.
### Go Template Syntax Basics
**Variables ** :
``` html
{{.CV.Personal.Name}} <!-- Output variable -->
{{.CV.Personal.Email}}
```
**Conditionals ** :
``` html
{{if .CV.Awards}}
<!-- Only show if awards exist -->
< section > Awards here< / section >
{{end}}
{{if eq .Lang "es"}}
Español
{{else}}
English
{{end}}
```
**Loops ** :
``` html
{{range .CV.Experience}}
< div > {{.Position}} at {{.Company}}< / div >
{{end}}
```
**Safe HTML ** (allow HTML in content):
``` html
{{.Description | safeHTML}}
```
### Modifying cv-content.html
**Location ** : `templates/cv-content.html`
#### Example: Change Section Order
Move "Education" before "Experience":
1. Find Education section (around line 50)
2. Cut the entire `<section id="education">...</section>` block
3. Paste it before the Experience section (line 86)
#### Example: Change Header Layout
**Current ** (lines 35-46):
``` html
< div class = "cv-header" >
< div class = "cv-header-content" >
< div class = "cv-header-left" >
< h1 class = "cv-name" > Moreno Rubio, Juan Andrés< / h1 >
< p class = "years-experience" > {{.YearsOfExperience}} years< / p >
< div class = "intro-text" > {{.CV.Summary}}< / div >
< / div >
< div class = "cv-photo" >
< img src = "/static/images/profile/dni.jpeg" alt = "{{.CV.Personal.Name}}" >
< / div >
< / div >
< / div >
```
**Customize ** : Change name format
``` html
< h1 class = "cv-name" > {{.CV.Personal.Name}}< / h1 >
<!-- Instead of hardcoded "Moreno Rubio, Juan Andrés" -->
```
**Add contact info to header ** :
``` html
< div class = "cv-header-left" >
< h1 class = "cv-name" > {{.CV.Personal.Name}}< / h1 >
< p class = "cv-contact" >
{{.CV.Personal.Email}} | {{.CV.Personal.Phone}}
< / p >
< div class = "intro-text" > {{.CV.Summary}}< / div >
< / div >
```
#### Example: Custom Section
Add "Hobbies" section:
``` html
< section id = "hobbies" class = "cv-section" >
< h3 class = "section-title" >
< iconify-icon icon = "mdi:heart" width = "24" height = "24" class = "section-icon" > < / iconify-icon >
{{if eq .Lang "es"}}Aficiones{{else}}Hobbies{{end}}
< / h3 >
< ul >
{{range .CV.Hobbies}}
< li > {{.}}< / li >
{{end}}
< / ul >
< / section >
```
Don't forget to add to JSON and Go model!
### Adding Custom Template Functions
**Location ** : `main.go` (where templates are loaded)
**Example ** : Add function to format dates
``` go
// In main.go, find template loading section and add:
funcMap := template . FuncMap {
"safeHTML" : func ( s string ) template . HTML {
return template . HTML ( s )
} ,
"formatDate" : func ( dateStr string ) string {
// Parse YYYY-MM format and return "Month Year"
parts := strings . Split ( dateStr , "-" )
if len ( parts ) != 2 {
return dateStr
}
months := [ ] string { "" , "January" , "February" , "March" , "April" , "May" , "June" ,
"July" , "August" , "September" , "October" , "November" , "December" }
monthNum , _ := strconv . Atoi ( parts [ 1 ] )
return months [ monthNum ] + " " + parts [ 0 ]
} ,
}
tmpl := template . New ( "" ) . Funcs ( funcMap )
```
**Use in template ** :
``` html
< small > {{formatDate .StartDate}} / {{formatDate .EndDate}}< / small >
```
### Conditional Rendering
**Show section only in long version ** :
``` html
< div class = "long-only" >
<!-- Only visible when user toggles to long CV -->
{{range .Responsibilities}}
< li > {{. | safeHTML}}< / li >
{{end}}
< / div >
```
**Show section only in short version ** :
``` html
< div class = "short-desc" >
{{.ShortDescription | safeHTML}}
< / div >
```
**Language-specific content ** :
``` html
{{if eq .Lang "es"}}
< p > Contenido en español< / p >
{{else}}
< p > Content in English< / p >
{{end}}
```
---
2025-11-09 19:33:20 +00:00
## Analytics Configuration
2025-11-12 16:04:43 +00:00
This template includes a self-hosted analytics implementation as a learning example. You have three options:
2025-11-09 19:33:20 +00:00
2025-11-12 16:04:43 +00:00
### Option 1: Configure Your Own Analytics
2025-11-09 19:33:20 +00:00
2025-11-12 16:04:43 +00:00
If you want to use self-hosted analytics:
2025-11-09 19:33:20 +00:00
2025-11-12 16:04:43 +00:00
1. **Set up your analytics server ** (Matomo, Plausible, or similar)
2025-11-09 19:33:20 +00:00
2025-11-12 16:04:43 +00:00
2. **Update tracking code ** in `templates/index.html` :
```javascript
// Find this section near the end of the file
var _paq = window._paq = window._paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
2025-11-09 19:33:20 +00:00
2025-11-12 16:04:43 +00:00
var u="https://YOUR-ANALYTICS-SERVER.COM/"; // Replace this
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', 'YOUR-SITE-ID']); // Replace this
` ``
2025-11-09 19:33:20 +00:00
2025-11-12 16:04:43 +00:00
3. **Update CSP headers** in ` internal/middleware/security.go`:
` ``go
// Find the CSP policy and update these directives:
"script-src 'self' 'unsafe-inline' https://YOUR-ANALYTICS-SERVER.COM; " +
"connect-src 'self' https://YOUR-ANALYTICS-SERVER.COM; "
` ``
2025-11-09 19:33:20 +00:00
2025-11-12 16:04:43 +00:00
4. **Update PRIVACY.md** with your own privacy policy details
2025-11-09 19:33:20 +00:00
2025-11-12 16:04:43 +00:00
### Option 2: Remove Analytics Entirely
2025-11-09 19:33:20 +00:00
2025-11-12 16:04:43 +00:00
If you don't want analytics:
2025-11-09 19:33:20 +00:00
2025-11-12 16:04:43 +00:00
1. **Remove tracking code** from ` templates/index.html`:
- Delete the entire ` <!-- Matomo Analytics -->` section (usually at the end of ` <body>`)
2025-11-09 19:33:20 +00:00
2025-11-12 16:04:43 +00:00
2. **Simplify CSP headers** in ` internal/middleware/security.go`:
` ``go
// Remove analytics domains from:
"script-src 'self' 'unsafe-inline'; " + // Remove analytics domain
"connect-src 'self'; " // Remove analytics domain
` ``
2025-11-09 19:33:20 +00:00
2025-11-12 16:04:43 +00:00
3. **Update PRIVACY.md**:
- Simplify to state no tracking is used
- Or remove the file if not needed
2025-11-09 19:33:20 +00:00
2025-11-12 16:04:43 +00:00
### Option 3: Use Alternative Analytics Service
2025-11-09 19:33:20 +00:00
2025-11-12 16:04:43 +00:00
If you prefer Google Analytics, Plausible Cloud, or another service:
2025-11-09 19:33:20 +00:00
2025-11-12 16:04:43 +00:00
1. **Replace tracking code** in ` templates/index.html` with your provider's script
2. **Update CSP headers** with your provider's domains
3. **Update PRIVACY.md** according to your provider's data handling
4. **Note**: External services may require additional privacy disclosures (GDPR, CCPA)
2025-11-09 19:33:20 +00:00
### Testing Analytics
After configuration:
` ``bash
2025-11-12 16:04:43 +00:00
# Start development server
make dev
2025-11-09 19:33:20 +00:00
2025-11-12 16:04:43 +00:00
# Visit site in browser
2025-11-09 19:33:20 +00:00
open http://localhost:1999
2025-11-12 16:04:43 +00:00
# Check browser console for errors
# Verify analytics requests in Network tab
2025-11-09 19:33:20 +00:00
` ``
2025-11-12 16:04:43 +00:00
**Security Note:** Always use HTTPS in production to protect analytics data in transit.
2025-11-09 19:33:20 +00:00
### Privacy Compliance
**Important legal considerations:**
2025-11-12 16:04:43 +00:00
- ✅ Add cookie consent banner if required in your jurisdiction
2025-11-09 19:33:20 +00:00
- ✅ Create privacy policy explaining data collection
- ✅ Provide opt-out mechanism
- ✅ Comply with GDPR, CCPA, or local privacy laws
- ✅ Update privacy policy when changing analytics providers
2025-11-18 19:32:28 +00:00
**See [10-PRIVACY.md](10-PRIVACY.md) for privacy policy template.**
2025-11-09 19:33:20 +00:00
---
2025-11-09 13:54:31 +00:00
## Advanced Customization
### Adding New Languages (Beyond en/es)
**Step 1**: Create JSON file
` ``bash
cp data/cv-en.json data/cv-fr.json
# Edit cv-fr.json with French content
` ``
**Step 2**: Update Go model validation (` internal/models/cv.go`)
` ``go
func LoadCV(lang string) (*CV, error) {
// Validate language
if lang != "en" && lang != "es" && lang != "fr" {
return nil, fmt.Errorf("unsupported language: %s", lang)
}
// ... rest of function
}
` ``
**Step 3**: Add language selector to template
` ``html
<div class="language-selector">
<a href="/?lang=en">English</a> |
<a href="/?lang=es">Español</a> |
<a href="/?lang=fr">Français</a>
</div>
` ``
**Step 4**: Update all ` {{if eq .Lang "es"}}` conditions:
` ``html
{{if eq .Lang "es"}}
Español
{{else if eq .Lang "fr"}}
Français
{{else}}
English
{{end}}
` ``
2025-11-19 10:56:01 +00:00
### PDF Export Customization
2025-11-09 13:54:31 +00:00
2025-11-19 10:56:01 +00:00
The CV site includes a powerful PDF export feature with multiple customization options. Users can download their CV as a PDF with different configurations.
#### How PDF Export Works
When a user clicks "Download PDF", the server:
1. Takes a snapshot of the CV webpage using headless Chrome
2. Applies special print-optimized CSS styles (` @media print`)
3. Generates a professional PDF document
4. Downloads it with a descriptive filename
#### PDF Export Parameters
The PDF export endpoint accepts 4 parameters that let users customize their PDF:
**1. Language (` lang`)**
- ` en` - English version
- ` es` - Spanish version
- Default: ` en`
- Example: ` /export/pdf?lang=es`
**2. Length (` length`)**
- ` short` - Concise version with summaries only
- ` long` - Detailed version with full descriptions and bullet points
- Default: ` short`
- Example: ` /export/pdf?length=long`
**3. Icons (` icons`)**
- ` show` - Display all company logos, project icons, and visual elements
- ` hide` - Text-only version without icons (more formal)
- Default: ` show`
- Example: ` /export/pdf?icons=hide`
**4. Version (` version`)**
- ` extended` - Full styling with colors and visual elements
- ` clean` - Minimal, print-optimized design
- Default: ` extended`
- Example: ` /export/pdf?version=clean`
#### Example URLs
` ``bash
# Short, clean English CV (most compact)
http://localhost:1999/export/pdf?lang=en&length=short&version=clean
# Long, detailed Spanish CV with all icons
http://localhost:1999/export/pdf?lang=es&length=long&icons=show&version=extended
# Use defaults (English, short, with icons, extended)
http://localhost:1999/export/pdf
` ``
#### Filename Convention
PDFs are automatically named following this pattern:
` ``
CV-{Your-Name}-{lang}-{length}-{version}.pdf
` ``
Examples:
- ` CV-Juan-Andrés-Moreno-Rubio-en-short-clean.pdf`
- ` CV-Jane-Smith-es-long-extended.pdf`
#### Customizing PDF Appearance
PDF appearance is controlled by ` @media print` CSS rules. The print styles are already optimized for A4 paper, but you can customize them:
**Location**: ` static/css/08-contexts/_print.css` (or your main CSS file)
**Common customizations**:
2025-11-09 13:54:31 +00:00
` ``css
@media print {
2025-11-19 10:56:01 +00:00
/* Change paper size */
@page {
size: Letter portrait; /* US Letter instead of A4 */
margin: 10mm; /* Adjust margins */
2025-11-09 13:54:31 +00:00
}
2025-11-19 10:56:01 +00:00
/* Customize colors */
.section-title {
color: #000000 !important; /* Force black text */
2025-11-09 13:54:31 +00:00
}
2025-11-19 10:56:01 +00:00
/* Hide specific elements */
.cv-footer {
display: none !important;
2025-11-09 13:54:31 +00:00
}
2025-11-19 10:56:01 +00:00
/* Adjust font sizes */
.cv-name {
font-size: 18pt !important;
2025-11-09 13:54:31 +00:00
}
}
` ``
2025-11-19 10:56:01 +00:00
#### Frontend Integration
Add a download button to your templates:
` ``html
<!-- Simple link -->
<a href="/export/pdf?lang=en&length=short&version=clean" download>
Download PDF
</a>
<!-- HTMX button (recommended) -->
<button
hx-get="/export/pdf?lang=en&length=short&version=clean"
hx-trigger="click">
📥 Download Clean PDF
</button>
<!-- Dynamic button with user's current language -->
<button
hx-get="/export/pdf?lang={{.Lang}}&length=short&version=clean"
hx-trigger="click">
Download PDF
</button>
` ``
#### Tips for Best PDF Results
1. **Keep it concise**: Use ` length=short` for 1-2 page CVs
2. **Professional look**: Use ` version=clean&icons=hide` for formal applications
3. **Colorful**: Use ` version=extended&icons=show` for creative industries
4. **Test before sharing**: Always preview the PDF before sending to employers
5. **File size**: Short versions ~1.5-2MB, Long versions ~2-2.5MB
#### Troubleshooting PDF Export
**Problem**: PDF generation times out
- **Solution**: Reduce content in your JSON files, or increase timeout in ` internal/pdf/generator.go`
**Problem**: Fonts look wrong in PDF
- **Solution**: Ensure fonts are loaded correctly in your HTML head section
**Problem**: Images don't appear in PDF
- **Solution**: Check that image paths are absolute or relative to server root
**Problem**: Colors are washed out
- **Solution**: Add ` -webkit-print-color-adjust: exact !important;` to your print CSS
#### Advanced: Customizing PDF Generation
If you need more control over PDF generation, you can modify:
**File**: ` internal/handlers/cv.go` (ExportPDF function)
- Add new parameters
- Change cookie mappings
- Modify filename pattern
**File**: ` internal/pdf/generator.go`
- Adjust PDF settings (margins, paper size)
- Change timeout duration
- Modify chromedp options
See [3-API.md](3-API.md) for complete API documentation.
2025-11-09 13:54:31 +00:00
### Additional Export Formats
#### Adding Word Export
**Step 1**: Install library
` ``bash
go get github.com/nguyenthenguyen/docx
` ``
**Step 2**: Create export handler (new file ` internal/export/docx.go`)
` ``go
package export
import (
"github.com/nguyenthenguyen/docx"
"your-cv/internal/models"
)
func GenerateDOCX(cv *models.CV, filename string) error {
doc := docx.NewFile()
// Add content
doc.AddHeading(cv.Personal.Name, 1)
doc.AddParagraph(cv.Summary)
// Add experience
for _, exp := range cv.Experience {
doc.AddHeading(exp.Position + " - " + exp.Company, 2)
for _, resp := range exp.Responsibilities {
doc.AddListItem(resp)
}
}
return doc.Save(filename)
}
` ``
**Step 3**: Add route in ` main.go`
` ``go
http.HandleFunc("/download/docx", func(w http.ResponseWriter, r *http.Request) {
lang := r.URL.Query().Get("lang")
cv, _ := models.LoadCV(lang)
filename := "/tmp/cv.docx"
export.GenerateDOCX(cv, filename)
w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
w.Header().Set("Content-Disposition", "attachment; filename=cv.docx")
http.ServeFile(w, r, filename)
})
` ``
### API Endpoints
**Add JSON API** for CV data:
**In ` main.go`**:
` ``go
// JSON API endpoint
http.HandleFunc("/api/cv", func(w http.ResponseWriter, r *http.Request) {
lang := r.URL.Query().Get("lang")
if lang == "" {
lang = "en"
}
cv, err := models.LoadCV(lang)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*") // CORS
json.NewEncoder(w).Encode(cv)
})
` ``
**Usage**:
` ``bash
curl http://localhost:1999/api/cv?lang=en | jq .
` ``
### Database Integration (Replacing JSON)
**For dynamic CVs** that update frequently:
**Step 1**: Choose database (PostgreSQL example)
` ``bash
go get github.com/lib/pq
` ``
**Step 2**: Create database schema
` ``sql
CREATE TABLE cv_data (
id SERIAL PRIMARY KEY,
language VARCHAR(2) NOT NULL,
content JSONB NOT NULL,
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_language ON cv_data(language);
` ``
**Step 3**: Update LoadCV function
` ``go
func LoadCVFromDB(lang string, db *sql.DB) (*CV, error) {
var content []byte
err := db.QueryRow(
"SELECT content FROM cv_data WHERE language = $1 ORDER BY updated_at DESC LIMIT 1",
lang,
).Scan(&content)
if err != nil {
return nil, err
}
var cv CV
if err := json.Unmarshal(content, &cv); err != nil {
return nil, err
}
return &cv, nil
}
` ``
**Step 4**: Add admin panel to update CV through web interface
---
## Testing Your Changes
### Local Testing Workflow
**1. Make changes**
` ``bash
# Edit JSON
nano data/cv-en.json
# Edit CSS
nano static/css/main.css
# Edit templates
nano templates/cv-content.html
` ``
**2. Test immediately** (with hot-reload):
` ``bash
# Start development server
make dev
# Or manually
GO_ENV=development TEMPLATE_HOT_RELOAD=true go run main.go
` ``
**3. Open browser**
` ``
http://localhost:1999
http://localhost:1999?lang=es
` ``
**4. Check for errors**
` ``bash
# Watch terminal for errors
# Check browser console (F12)
` ``
### Validating JSON
**Online validators**:
- [JSONLint](https://jsonlint.com/)
- [JSON Formatter](https://jsonformatter.org/)
**Command-line validation**:
` ``bash
# Using Python
python3 -m json.tool data/cv-en.json
# Using jq
jq . data/cv-en.json
# Should output formatted JSON without errors
` ``
**Common JSON errors**:
- Missing comma: ` "name": "John" "title": "Dev"` ← Need comma
- Trailing comma: ` ["item1", "item2",]` ← Remove last comma
- Unescaped quotes: ` "He said "hello""` ← Use ` "He said \"hello\""`
- Wrong brackets: ` {...]` ← Mismatched brackets
### Browser Testing Checklist
**Visual checks**:
- [ ] Profile photo displays correctly
- [ ] Company logos appear (or fallback icons)
- [ ] All sections render
- [ ] No overlapping text
- [ ] Colors look correct
- [ ] Links are clickable and work
- [ ] Icons display (Iconify loaded)
**Functionality checks**:
- [ ] Language toggle works (` ?lang=en` vs ` ?lang=es`)
- [ ] Print view looks good (Cmd/Ctrl+P)
- [ ] PDF download works
- [ ] Long/short CV toggle works (if implemented)
- [ ] Responsive on mobile (if implemented)
**Testing commands**:
` ``bash
# Test health endpoint
curl http://localhost:1999/health
# Test English version
curl http://localhost:1999/?lang=en | head -100
# Test Spanish version
curl http://localhost:1999/?lang=es | head -100
# Test PDF generation
curl http://localhost:1999/download/pdf?lang=en --output test.pdf
open test.pdf
` ``
### PDF Export Verification
**Check PDF quality**:
` ``bash
# Generate PDF
curl http://localhost:1999/download/pdf?lang=en --output cv.pdf
# Open and verify
open cv.pdf # macOS
xdg-open cv.pdf # Linux
start cv.pdf # Windows
# Check file size
ls -lh cv.pdf
# Should be ~500KB - 2MB depending on images
` ``
**PDF checklist**:
- [ ] All content visible (not cut off)
- [ ] Page breaks in correct places
- [ ] Images/logos render correctly
- [ ] Links are clickable (if viewing digitally)
- [ ] Text is selectable (not rasterized)
- [ ] Colors accurate
- [ ] No weird formatting issues
**Debug PDF issues**:
` ``bash
# If PDF generation fails, check:
# 1. Chromium installed
chromium-browser --version
# 2. Chromium path
which chromium-browser
# 3. Set environment variable if needed
export CHROME_BIN=/usr/bin/chromium-browser
` ``
---
## Examples
### Example 1: Complete Personal Rebrand
**Goal**: Replace all content with your own
**Steps**:
` ``bash
# 1. Backup original
cp data/cv-en.json data/cv-en.json.backup
# 2. Edit personal info
nano data/cv-en.json
# Update: personal, summary, experience, education, skills, projects
# 3. Replace photo
cp ~/my-headshot.jpg static/images/profile/dni.jpeg
# 4. Update colors to match your brand
nano static/css/main.css
# Change: --accent-blue to your brand color
# 5. Test
make dev
open http://localhost:1999
# 6. Generate PDF
curl http://localhost:1999/download/pdf?lang=en --output my-cv.pdf
` ``
### Example 2: Academic CV Style
**Goal**: Convert to academic CV format
**Changes needed**:
1. **Reorder sections**: Education first, then Publications, then Experience
2. **Add Publications section** (follow pattern from "Adding New Sections")
3. **Remove** "Projects" and "Awards" sections
4. **Change styling** to more conservative colors
**CSS changes**:
` ``css
:root {
2025-11-19 10:56:01 +00:00
--accent-blue: #013c77; /* Conservative dark blue */
2025-11-09 13:54:31 +00:00
--bg-gray: #f4f4f4; /* Light background */
}
body {
font-family: 'Times New Roman', serif; /* Traditional font */
}
` ``
### Example 3: Portfolio Website Integration
**Goal**: Use CV data to populate portfolio website
**Create new template** ` templates/portfolio.html`:
` ``html
<!DOCTYPE html>
<html>
<head>
<title>{{.CV.Personal.Name}} - Portfolio</title>
<link rel="stylesheet" href="/static/css/portfolio.css">
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/projects">Projects</a>
<a href="/cv">CV</a>
</nav>
<header>
<h1>{{.CV.Personal.Name}}</h1>
<p>{{.CV.Personal.Title}}</p>
</header>
<section class="projects">
{{range .CV.Projects}}
<article>
<h2>{{.Title}}</h2>
<p>{{.ShortDescription}}</p>
<a href="{{.URL}}">View Project</a>
</article>
{{end}}
</section>
</body>
</html>
` ``
**Add route in ` main.go`**:
` ``go
http.HandleFunc("/portfolio", func(w http.ResponseWriter, r *http.Request) {
// Render portfolio template using same CV data
})
` ``
### Example 4: Multi-Language Support (Adding French)
**Complete implementation**:
**1. Create French JSON**:
` ``bash
cp data/cv-en.json data/cv-fr.json
# Translate all content to French
` ``
**2. Update model validation** (` internal/models/cv.go`):
` ``go
func LoadCV(lang string) (*CV, error) {
if lang != "en" && lang != "es" && lang != "fr" {
return nil, fmt.Errorf("unsupported language: %s", lang)
}
// ...
}
` ``
**3. Update template conditionals** (all instances):
` ``html
{{if eq .Lang "fr"}}
Français text
{{else if eq .Lang "es"}}
Texto en español
{{else}}
English text
{{end}}
` ``
**4. Add language selector**:
` ``html
<div class="lang-selector">
<a href="/?lang=en" {{if eq .Lang "en"}}class="active"{{end}}>EN</a>
<a href="/?lang=es" {{if eq .Lang "es"}}class="active"{{end}}>ES</a>
<a href="/?lang=fr" {{if eq .Lang "fr"}}class="active"{{end}}>FR</a>
</div>
` ``
---
## Common Customization Patterns
### Pattern 1: Responsive Sidebar
Make sidebars collapse on mobile:
` ``css
@media screen and (max-width: 768px) {
.page-content {
grid-template-columns: 1fr;
}
.cv-sidebar {
display: none; /* Hide sidebars on mobile */
}
/* Or show as accordion */
.cv-sidebar details {
margin-bottom: 15px;
}
}
` ``
### Pattern 2: Dark Mode Toggle
Add dark mode switch:
**CSS**:
` ``css
/* Dark mode variables */
[data-theme="dark"] {
--bg-gray: #1e1e1e;
--sidebar-gray: #2d2d2d;
--paper-white: #252526;
--text-dark: #d4d4d4;
--text-gray: #9d9d9d;
}
` ``
**JavaScript** (add to template):
` ``html
<script>
function toggleDarkMode() {
const current = document.documentElement.getAttribute('data-theme');
const next = current === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', next);
localStorage.setItem('theme', next);
}
// Load saved preference
const saved = localStorage.getItem('theme');
if (saved) {
document.documentElement.setAttribute('data-theme', saved);
}
</script>
` ``
### Pattern 3: Skills Progress Bars
Add visual skill levels:
**Template** (in skills section):
` ``html
{{range $category.Items}}
<div class="skill-item">
<span class="skill-name">{{.}}</span>
<div class="skill-bar">
<div class="skill-level" style="width: {{$category.Proficiency}}0%;"></div>
</div>
</div>
{{end}}
` ``
**CSS**:
` ``css
.skill-bar {
background: #e0e0e0;
height: 8px;
border-radius: 4px;
overflow: hidden;
margin-top: 4px;
}
.skill-level {
background: var(--accent-blue);
height: 100%;
transition: width 0.3s ease;
}
` ``
---
## Troubleshooting Customization
### Issue: Changes Not Appearing
**Solutions**:
` ``bash
# 1. Hard refresh browser
# Press Cmd+Shift+R (Mac) or Ctrl+Shift+R (Windows/Linux)
# 2. Clear browser cache
# Or use private/incognito window
# 3. Restart server if Go code changed
pkill cv-server
make dev
# 4. Check for errors
# Look in terminal and browser console (F12)
` ``
### Issue: JSON Parse Error
**Solutions**:
` ``bash
# Validate JSON syntax
python3 -m json.tool data/cv-en.json
# Common fixes:
# - Add missing commas between items
# - Remove trailing commas in arrays/objects
# - Escape quotes in strings: \" instead of "
# - Check matching brackets: { } [ ]
` ``
### Issue: Template Rendering Error
**Solutions**:
` ``bash
# Check error message in terminal
# Common issues:
# - Undefined variable: Check spelling, case-sensitivity
# - Wrong field name: Verify against models/cv.go
# - Missing | safeHTML for HTML content
` ``
### Issue: Styling Not Applied
**Solutions**:
` ``bash
# 1. Verify CSS file loaded
# Check browser Network tab (F12) for main.css
# 2. Check CSS syntax
# Use browser DevTools to inspect elements
# 3. Check specificity
# Use !important to test: color: red !important;
# 4. Verify class names match
# Template: class="cv-name"
# CSS: .cv-name { ... }
` ``
---
## Next Steps
After customization:
1. **Test thoroughly ** with checklist above
2. **Generate PDF ** and verify quality
2025-11-18 19:32:28 +00:00
3. **Deploy ** using [8-DEPLOYMENT.md ](8-DEPLOYMENT.md ) guide
2025-11-09 13:54:31 +00:00
4. **Set up CI/CD ** for automatic deployments
5. **Share ** your customized CV!
**Further Resources ** :
- [Go Templates Documentation ](https://pkg.go.dev/text/template )
- [CSS Grid Guide ](https://css-tricks.com/snippets/css/complete-guide-grid/ )
- [Iconify Icon Sets ](https://icon-sets.iconify.design/ )
- [Google Fonts ](https://fonts.google.com/ )
**Need Help? **
- Check existing issues on GitHub
- Open new issue with details
- Include error messages and screenshots
Happy customizing! 🎨