533 lines
11 KiB
Markdown
533 lines
11 KiB
Markdown
# 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/)
|