# Hugo + PaperMod Customization Guide This guide covers how to customize your Hugo portfolio site using the PaperMod theme. --- ## Table of Contents 1. [Project Structure](#project-structure) 2. [Development Workflow](#development-workflow) 3. [Configuration (hugo.yaml)](#configuration-hugoyaml) 4. [Adding Content](#adding-content) 5. [Creating New Sections](#creating-new-sections) 6. [Customizing Styles (CSS)](#customizing-styles-css) 7. [Overriding Layouts](#overriding-layouts) 8. [Common Customizations](#common-customizations) 9. [Deployment](#deployment) --- ## Project Structure ``` MyPortfolio/ ├── archetypes/ # Templates for new content ├── assets/ │ └── css/ │ └── extended/ │ └── custom.css # YOUR custom CSS (this gets merged) ├── content/ │ ├── posts/ # Long-form articles │ │ └── _index.md # Section config │ ├── notes/ # Short notes/TILs │ │ └── _index.md # Section config │ └── search.md # Search page ├── layouts/ │ ├── partials/ # Override theme partials │ │ └── footer.html # Custom footer │ └── notes/ # Section-specific layouts │ └── list.html # Custom notes list ├── static/ │ └── files/ # Static files (CV.pdf, images, etc.) ├── themes/ │ └── PaperMod/ # Theme (git submodule - DON'T edit) ├── hugo.yaml # Main configuration └── .gitignore # Ignores public/, etc. ``` ### Key Principle: Never Edit the Theme Directly The `themes/PaperMod/` folder is a git submodule. To customize: - **CSS**: Add to `assets/css/extended/custom.css` - **Layouts**: Copy the file from `themes/PaperMod/layouts/` to `layouts/` and modify - **Config**: Everything in `hugo.yaml` --- ## Development Workflow ### Local Development ```bash # Start dev server (watches for changes) hugo serve # With drafts visible hugo serve -D # Build for production (outputs to public/) hugo build --minify ``` ### Why `public/` is Gitignored - `public/` is generated output - it's rebuilt fresh every time - Your CI/CD (GitHub Actions) builds it during deployment - Never commit build artifacts to git ### Git Workflow ```bash # After making changes git add . git commit -m "Add new post about X" git push origin master # Triggers deployment ``` --- ## Configuration (hugo.yaml) ### Key Sections #### Site Basics ```yaml baseURL: "/" # Use "/" for relative URLs (works on localhost + prod) relativeURLs: true # Makes all URLs relative title: Saurav Dhakal languageCode: en-us theme: ["PaperMod"] ``` #### Menu (Header Navigation) ```yaml menu: main: - identifier: posts # Internal ID name: Posts # Display name url: /posts/ # URL path weight: 10 # Order (lower = first) - identifier: notes name: Notes url: /notes/ weight: 20 - identifier: tags name: Tags url: /tags/ weight: 30 - identifier: search name: Search url: /search/ weight: 40 ``` #### Home Page Info ```yaml params: homeInfoParams: Title: "Hi there 👋, I'm Saurav!" Content: > I'm a software engineer... ``` #### Social Icons ```yaml params: socialIcons: - name: github url: "https://github.com/yourusername" - name: linkedin url: "https://linkedin.com/in/yourusername" - name: x url: "https://x.com/yourusername" ``` Available icons: github, linkedin, x, twitter, email, rss, youtube, instagram, facebook, stackoverflow, etc. --- ## Adding Content ### New Post ```bash hugo new posts/my-new-post.md ``` Or manually create `content/posts/my-new-post.md`: ```markdown --- title: "My New Post" date: 2025-08-20 summary: "A brief description for the list view" tags: ["tag1", "tag2"] categories: ["Category"] draft: false ShowToc: true TocOpen: false --- Your content here... ``` ### New Note Create `content/notes/my-note.md`: ```markdown --- title: "Quick TIL" date: 2025-08-20 summary: "Today I learned about X" tags: ["til"] --- Short content here... ``` ### Front Matter Options | Field | Description | |-------|-------------| | `title` | Post title | | `date` | Publication date (YYYY-MM-DD) | | `summary` | Short description for list views | | `tags` | Array of tags | | `categories` | Array of categories | | `draft` | If `true`, won't be published | | `ShowToc` | Show table of contents | | `TocOpen` | TOC expanded by default | | `ShowReadingTime` | Override global setting | | `ShowWordCount` | Override global setting | | `cover.image` | Cover image path | --- ## Creating New Sections ### Example: Adding a "Projects" Section 1. **Add to menu** in `hugo.yaml`: ```yaml menu: main: # ... existing items - identifier: projects name: Projects url: /projects/ weight: 25 ``` 2. **Create content directory**: ```bash mkdir -p content/projects ``` 3. **Create section index** `content/projects/_index.md`: ```markdown --- title: "Projects" description: "Things I've built" --- ``` 4. **Add project pages** `content/projects/my-project.md`: ```markdown --- title: "My Cool Project" date: 2025-01-15 summary: "A brief description" tags: ["golang", "cli"] --- Project description... ``` 5. **(Optional) Custom layout** - If you want projects to look different, create `layouts/projects/list.html` --- ## Customizing Styles (CSS) ### Where to Add CSS **Only edit**: `assets/css/extended/custom.css` PaperMod automatically includes this file. You don't need to import it anywhere. ### CSS Variables (Theme Colors) PaperMod uses CSS variables. Override them in your custom.css: ```css :root { /* Light mode */ --primary: #282828; /* Main text */ --secondary: #3c3836; /* Secondary text */ --tertiary: rgb(214, 214, 214); /* Borders, etc */ --theme: rgb(255, 255, 255); /* Background */ --entry: rgb(255, 255, 255); /* Card background */ } :root[data-theme="dark"] { /* Dark mode */ --primary: #fbf1c7; --secondary: #ebdbb2; --theme: #181818; --entry: rgb(46, 46, 51); } ``` ### Common CSS Customizations #### Change fonts ```css @import url("https://fonts.googleapis.com/css2?family=Your+Font&display=swap"); body { font-family: "Your Font", sans-serif; } ``` #### Style links ```css main a { text-decoration: underline; text-decoration-color: var(--green); } main a:hover { background-color: var(--green); color: white; } ``` #### Customize post cards ```css main .post-entry { border: 2px solid #383838; background-color: var(--entry); border-radius: 8px; } ``` --- ## Overriding Layouts ### How Layout Override Works Hugo looks for templates in this order: 1. `layouts/` (your overrides) 2. `themes/PaperMod/layouts/` (theme defaults) ### To Override a Template 1. Find the file in `themes/PaperMod/layouts/` 2. Copy it to the same path in `layouts/` 3. Modify your copy ### Common Files to Override | File | Purpose | |------|---------| | `layouts/partials/header.html` | Site header/nav | | `layouts/partials/footer.html` | Site footer | | `layouts/partials/post_meta.html` | Post metadata (date, read time) | | `layouts/_default/list.html` | List pages (posts, tags) | | `layouts/_default/single.html` | Individual post/page | ### Example: Simpler Footer Your `layouts/partials/footer.html` already overrides the theme's footer. ### Section-Specific Layouts Create `layouts/SECTION_NAME/list.html` for a custom list layout for that section. Example: `layouts/notes/list.html` - custom compact layout for notes. --- ## Common Customizations ### Disable Reading Time for a Section In the section's `_index.md`: ```markdown --- title: "Notes" ShowReadingTime: false --- ``` Or per-post in front matter. ### Add a Static Page (About, Contact) Create `content/about.md`: ```markdown --- title: "About" layout: "single" url: "/about/" --- About me content... ``` Add to menu: ```yaml menu: main: - identifier: about name: About url: /about/ weight: 50 ``` ### Add Favicon 1. Place favicon files in `static/`: - `static/favicon.ico` - `static/favicon-16x16.png` - `static/favicon-32x32.png` - `static/apple-touch-icon.png` 2. Update `hugo.yaml`: ```yaml params: assets: favicon: "/favicon.ico" favicon16x16: "/favicon-16x16.png" favicon32x32: "/favicon-32x32.png" apple_touch_icon: "/apple-touch-icon.png" ``` ### Enable Comments (Disqus) ```yaml params: comments: true disqusShortname: "your-disqus-shortname" ``` ### Add Google Analytics Already configured in your `hugo.yaml`: ```yaml googleAnalytics: "G-XXXXXXXXXX" ``` --- ## Deployment ### Current Setup (GitHub Actions → SSH/SCP) Your `.github/workflows/deploy.yml`: 1. Triggers on push to `master` 2. Builds site with `hugo build --minify` 3. Deploys `public/` via SCP to your server ### Required GitHub Secrets Set these in your repo's Settings → Secrets: - `SSH_HOST` - Your server hostname/IP - `SSH_USER` - SSH username - `SSH_KEY` - Private SSH key - `SSH_PORT` - SSH port (usually 22) ### Alternative: GitHub Pages If you want to use GitHub Pages instead: 1. Change workflow to use `peaceiris/actions-gh-pages` 2. Set `baseURL` to `https://yourusername.github.io/repo-name/` ### Alternative: Netlify/Vercel These platforms auto-detect Hugo and build for you: 1. Connect your GitHub repo 2. Set build command: `hugo --minify` 3. Set publish directory: `public` --- ## Quick Reference ### Useful Commands ```bash # Local dev hugo serve -D # Serve with drafts # Create content hugo new posts/title.md # New post hugo new notes/title.md # New note # Build hugo build --minify # Production build # Debug hugo config # Show full config hugo list all # List all content ``` ### File Locations | What | Where | |------|-------| | Config | `hugo.yaml` | | Custom CSS | `assets/css/extended/custom.css` | | Posts | `content/posts/` | | Notes | `content/notes/` | | Static files | `static/` | | Layout overrides | `layouts/` | --- ## Troubleshooting ### Links Go to Production URL in Dev Fixed by setting: ```yaml baseURL: "/" relativeURLs: true ``` ### Changes Not Showing 1. Hard refresh browser (Ctrl+Shift+R) 2. Clear `public/` folder: `rm -rf public/` 3. Restart hugo serve ### Theme Not Loading ```bash git submodule update --init --recursive ``` ### Search Not Working Ensure `hugo.yaml` has: ```yaml outputs: home: - HTML - RSS - JSON # Required for search ``` --- ## Resources - [Hugo Documentation](https://gohugo.io/documentation/) - [PaperMod Wiki](https://github.com/adityatelange/hugo-PaperMod/wiki) - [PaperMod Demo](https://adityatelange.github.io/hugo-PaperMod/)