Building Static Site with Hugo Clarity Theme
Static site generators like Hugo allow you to build fast, secure, and highly customizable websites without the overhead of server-side logic. Whether you're creating a personal blog, research notebook, or engineering portfolio, this guide provides a complete, automation-focused setup for a professional web presence.
Environment Setup (Installation Scripts)
The development environment requires several tools: Go (the required runtime for building Hugo from source and for certain features), Hugo(the core static site generator, including Sass/SCSS and other advanced features), and essential development tools (git, gh, pre-commit utilities).
Installing go
We begin with a Bash script to install Go programming language. The script performs architecture detection, checks for existing versions, downloads the Go archive, installs it to a local directory, and ensures your shell environment is updated to include Go in the system path.
1
2# Golang Installation
3install_go() {
4 # Determine OS and architecture
5 local os arch
6 case "$(uname -s)" in
7 Linux*) os="linux" ;;
8 Darwin*) os="darwin" ;;
9 *) echo "Unsupported OS"; return 1 ;;
10 esac
11 case "$(uname -m)" in
12 x86_64*) arch="amd64" ;;
13 arm64*) arch="arm64" ;;
14 *) echo "Unsupported architecture"; return 1 ;;
15 esac
16
17 # Get the latest Go version
18 local version
19 version=$(curl -fsS https://go.dev/VERSION?m=text | head -n1) || {
20 echo "Failed to get Go version"
21 return 1
22 }
23
24 # Validate version string
25 if [[ -z "$version" || ! "$version" =~ ^go[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then
26 echo "Invalid Go version string: '$version'"
27 return 1
28 fi
29
30 # Check if Go is already installed with the same version
31 if command -v go &> /dev/null && [[ "$(go version)" =~ $version ]]; then
32 echo "Go $version is already installed"
33 return 0
34 fi
35
36 # Set archive path and download URL
37 local filename="${version}.${os}-${arch}.tar.gz"
38 local archive_path="$HOME/Downloads/$filename"
39 local url="https://go.dev/dl/$filename"
40
41 # Download archive if not already cached
42 if [[ -f "$archive_path" ]]; then
43 echo "Using cached archive: $archive_path"
44 else
45 echo "Downloading from: $url"
46 wget --secure-protocol TLSv1_3 -O "$archive_path" "$url" || {
47 echo "Download failed"
48 return 1
49 }
50 fi
51
52 # Remove existing Go and extract new version
53 rm -rf "$HOME/.local/go"
54 tar -C "$HOME/.local" -xzf "$archive_path" || {
55 echo "Extraction failed"
56 return 1
57 }
58
59 # Update PATH in shell config
60 local shell_config
61 shell_config=$(find "$HOME" -maxdepth 1 \( -name ".bash_aliases" -o -name ".zshrc" \) -print -quit)
62 if [[ -z "$shell_config" ]]; then
63 echo "No shell configuration file found"
64 return 1
65 fi
66
67 if ! grep -q 'export PATH=.*go/bin' "$shell_config"; then
68 echo 'export PATH="$HOME/.local/go/bin:$PATH"' >> "$shell_config"
69 echo "Updated PATH in $shell_config"
70 fi
71
72 export PATH="$HOME/.local/go/bin:$PATH"
73 echo "Go $version installed in $HOME/.local/go"
74 echo "Run 'source $shell_config' or restart your shell to use Go"
75 go version || echo "Go not found in PATH"
76}
77install_go
Installing hugo
Then we'll install Hugo (v0.150.0) using the official .deb files from the Hugo GitHub repository. The Bash function defines the Hugo version, architecture, and proceeds to downloading the standard and extended Hugo .deb files if they don't already exist locally and installs them using the Debian package manager.
1# Hugo Installation
2install_hugo() {
3 local Version="0.150.0"
4 local Arch="amd64"
5 local OS="linux"
6 local base_url="https://github.com/gohugoio/hugo/releases/download/v${Version}"
7
8 local standard_deb="hugo_${Version}_${OS}-${Arch}.deb"
9 local extended_deb="hugo_extended_${Version}_${OS}-${Arch}.deb"
10
11 if [ ! -f "${standard_deb}" ] || [ ! -f "${extended_deb}" ]; then
12 echo "Downloading Hugo v${Version}..."
13 wget --secure-protocol TLSv1_3 "${base_url}/${standard_deb}"
14 wget --secure-protocol TLSv1_3 "${base_url}/${extended_deb}"
15 else
16 echo "Hugo v${Version} DEBs already downloaded."
17 fi
18
19 echo "Installing Hugo..."
20 sudo apt install -y -f ./"${standard_deb}" ./"${extended_deb}"
21}
22install_hugo
Installing imagemagick, git, gh, and pre-commit
Finally, we'll use the Debian package manager (apt) to install essential development tools. ImageMagick(magick
) provides powerful utilities for image conversion and favicon/logo generation, which we’ll use later when customizing the Hugo site. Git(git
) manages version control and theme integration, while the GitHub CLI (gh
) streamlines repository setup and deployment. We’ll also install Python utilities (python3-pip, python3-venv, pipx) for virtual environment management and installing tools like pre-commit
, which enforces code quality and security checks before commits.
1# enter sudo user password when prompted
2install_tools_via_package_manager() {
3 local pkg_manager
4 local install_cmd
5 local tools="imagemagick git gh python3-pip python3-venv pipx"
6
7 if [ -f /etc/os-release ]; then
8 source /etc/os-release
9 case $ID in
10 debian | ubuntu)
11 pkg_manager="apt"
12 install_cmd="sudo apt -y install"
13 sudo apt update
14 ;;
15 centos | rhel | fedora)
16 pkg_manager="dnf"
17 install_cmd="sudo dnf -y install"
18 ;;
19 opensuse)
20 pkg_manager="zypper"
21 install_cmd="sudo zypper -y install"
22 ;;
23 *)
24 echo "Unsupported Linux distribution ($ID)."
25 return 1
26 ;;
27 esac
28 else
29 echo "Could not detect OS."
30 return 1
31 fi
32
33 echo "Installing $tools using $pkg_manager..."
34 if ! $install_cmd $tools; then
35 echo "Failed to install tools."
36 return 1
37 fi
38
39 # Ensure pipx environment is ready
40 if ! command -v pipx &> /dev/null; then
41 echo "pipx not found. Aborting pre-commit installation."
42 return 1
43 fi
44
45 echo "Installing pre-commit using pipx..."
46 if ! pipx install pre-commit; then
47 echo "Failed to install pre-commit."
48 return 1
49 fi
50}
51install_tools_via_package_manager
Project Initialization & Configuration
This section, uses the installed gh, git and hugo tools to set up the repository, site structure, and initial configuration.
Define Core Variables
Start by defining the necessary environment and project variables. This centralizes all configurations for the repository, Hugo project, and custom domain. These variables will be used throughout the process.
1# Git variables
2GIT_USER_NAME="th3b1rdm2n"
3GIT_USER_HOMEPAGE="https://github.com/$GIT_USER_NAME"
4GIT_REPO_NAME="$GIT_USER_NAME.github.io"
5GIT_REPO_URL="https://github.com/$GIT_USER_NAME/$GIT_REPO_NAME.git"
6GIT_REPO_DESCRIPTION="A Digital Nest for Learning, Living and Leading with Intent."
7GIT_MAIN_BRANCH="production"
8GIT_DEV_BRANCH="development"
9GIT_REMOTE_NAME="origin"
10
11# Hugo variables
12HUGO_SITENAME="$GIT_REPO_NAME"
13HUGO_PROJECT_ROOT="$HOME/$HUGO_SITENAME"
14HUGO_THEME_REPO_URL="https://github.com/chipzoller/hugo-clarity"
15HUGO_THEME_DIR="themes/$(basename "$HUGO_THEME_REPO_URL")"
16
17# Custom variables
18CUSTOM_DOMAIN="th3b1rdm2n.site"
Initialize the Project
Next, we'll authenticate to GitHub using the gh
command-line tool and create a new public repository.
1# Authenticate to Github and create remote repository
2gh auth status # Check authentication status
3gh auth login -h github.com -p https -w # Authenticate to GitHub
4gh auth setup-git # Configure Git to use GitHub CLI as credential helper
5gh repo create "$GIT_REPO_NAME" --public --homepage "$GIT_USER_HOMEPAGE" --description "$GIT_REPO_DESCRIPTION" # Create remote repo
We'll then initialize the local Git repository, create a new Hugo site, add the Clarity theme as a submodule, and push to remote repository.
1
2# Set project root and clone the theme repository
3cd $(dirname "$HUGO_PROJECT_ROOT")
4hugo new site "$HUGO_SITENAME" --format yaml
5cd "$HUGO_SITENAME"
6hugo mod init "$HUGO_SITENAME"
7
8# Initialize Git
9git init . # Initialize local Git repo
10git config --local user.name "$GIT_USER_NAME" # Set local Git username
11git config --local user.email "" # Set local Git email
12git branch -M "$GIT_MAIN_BRANCH" # Rename default branch to production
13git submodule add $HUGO_THEME_REPO_URL "$HUGO_THEME_DIR" # add a hugo theme as submodule
14git commit --allow-empty -m "chore: initial commit - $(date +%F_%H:%M)" # initial empty commit
15git remote add "$GIT_REMOTE_NAME" "$GIT_REPO_URL" # Add remote origin
16git push -u "$GIT_REMOTE_NAME" "$GIT_MAIN_BRANCH" # Push production branch to remote
Customize the Project
We'll then configure pre-commit hooks to ensure code quality, add a .gitignore file to exclude unnecessary files, add a CNAME file for gh-pages
to use custom domain, copy the theme's exampleSite and icons folder into the project root and static folders respectively, update the images of the icons folder, and modification to various files as shown in the walkthrough video.
1
2# Create pre-commit config file
3cat > "$HUGO_PROJECT_ROOT/.pre-commit-config.yaml" <<EOF
4repos:
5- repo: https://github.com/pre-commit/pre-commit-hooks
6 rev: v5.0.0
7 hooks:
8 - id: check-added-large-files
9 args: ["--maxkb=102400", "--enforce-all"]
10- repo: https://github.com/zricethezav/gitleaks
11 rev: v8.18.0
12 hooks:
13 - id: gitleaks
14EOF
15pre-commit install # Install pre-commit hooks
16pre-commit run --all-files # Run pre-commit hooks on all files
17
18# add .gitignore file
19cat > "$HUGO_PROJECT_ROOT/.gitignore" <<EOF
20# IDE
21**/.code-workspace
22**/.idea
23**/.vscode/
24!**/.vscode/extensions.json
25!**/.vscode/settings.json
26
27# Go
28**/*.out
29**/*.test
30**/vendor/
31
32# Hugo
33**/resources
34**/public
35
36# JavaScript
37**/.angular/
38**/.next/
39**/.node_repl_history
40**/.npm
41**/.nuxt/
42**/.parcel-cache/
43**/.temp
44**/.vue-cli-service/
45**/*.js.map
46**/*.min.js
47**/*.ts.map
48**/*.tsbuildinfo
49**/coverage/
50**/dist/
51**/e2e/
52**/node_modules/
53**/npm-debug.log
54**/package-lock.json
55**/public/
56**/yarn-debug.log
57**/yarn-error.log
58
59# Miscellaneous
60**/*.bak
61**/*.log
62**/*.swp
63**/.env
64EOF
65
66# Add CNAME file at the project root for custom domain mapping
67echo -e "$CUSTOM_DOMAIN\nwww.$CUSTOM_DOMAIN" > "$HUGO_PROJECT_ROOT/CNAME"
68
69# Use the exampleSite in developing and modifying yours
70cp -a "$HUGO_THEME_DIR"/exampleSite/* $HUGO_PROJECT_ROOT/ # Copy theme's exampleSite content into the root of your Hugo project
71cp -a "$HUGO_THEME_DIR"/static/icons $HUGO_PROJECT_ROOT/static/ # Copy theme's static icons to your project's static directory
72
73# Create a logo - to see list run: `convert -list font | less`
74generate_ssg_image() {
75 local input_image="" output_dir="" logo_text=""
76
77 # Parse CLI arguments
78 while [[ "$#" -gt 0 ]]; do
79 case "$1" in
80 -i|--input) input_image="$2"; shift 2 ;;
81 -o|--output) output_dir="$2"; shift 2 ;;
82 -l|--logo) logo_text="$2"; shift 2 ;;
83 -*|*) echo "Usage: $FUNCNAME -i <input_image> -o <output_dir>"; return 1 ;;
84 esac
85 done
86
87 # Validate input
88 [[ -z "$input_image" || -z "$output_dir" ]] && {
89 echo "Usage: $FUNCNAME -i <input_image> -o <output_dir>"; return 1; }
90 [[ ! -f "$input_image" ]] && { echo "File not found: $input_image"; return 1; }
91
92 mkdir -p "$output_dir"
93
94 local transparent_image="$output_dir/th3b1rdm2n.png"
95 convert "$input_image" -fuzz 10% -transparent black "$transparent_image"
96
97 declare -A output_names=(
98 [16]="favicon-16x16.png"
99 [32]="favicon-32x32.png"
100 [150]="mstile-150x150.png"
101 [180]="apple-touch-icon.png"
102 [192]="android-chrome-192x192.png"
103 [256]="android-chrome-256x256.png"
104 [512]="android-chrome-512x512.png"
105 )
106
107 echo "Generating icons from $transparent_image..."
108 for size in "${!output_names[@]}"; do
109 convert "$transparent_image" -resize "${size}x${size}" "$output_dir/${output_names[$size]}"
110 echo "Generated: ${output_names[$size]}"
111 done
112
113 convert "$output_dir/favicon-16x16.png" "$output_dir/favicon.ico"
114
115 # Create a logo - to see list run: `convert -list font | less`
116 convert -size 140x38 xc:none -gravity center -stroke white -strokewidth 2 -fill black -pointsize 24 -font Liberation-Sans -annotate +0+0 "$logo_text" "$(dirname "$output_dir")/logos/logo.png"
117 echo "Done: All icons saved to $output_dir"
118}
119generate_ssg_image -i "$HOME/Downloads/th3b1rdm2n.png" -o "$HUGO_PROJECT_ROOT/static/icons" -l "bírd màn"
Git Workflow and Deployment
Branching and Initial Deployment
A structured Git workflow ensures that development work is isolated from the live production code. This step creates a development branch for ongoing changes and then merges to the production branch, triggering the configured GitHub Actions workflow to build and deploy your site. After the initial push, navigate to your repository settings to enable GitHub Actions as the deployment source for GitHub Pages.
1git checkout -b "$GIT_DEV_BRANCH" # Create and switch to main branch
2git add . # add the modified and populated files
3git commit -m "chore: modified and populated site files - $(date +%F_%H:%M)"
4git push -u "$GIT_REMOTE_NAME" "$GIT_DEV_BRANCH"
5
6# Navigate to $GIT_USER_HOMEPAGE/$GIT_REPO_NAME/settings/pages. Switch the Source to `GitHub Actions`. Then run:
7git switch "$GIT_MAIN_BRANCH"
8git merge "$GIT_DEV_BRANCH"
9git push -u "$GIT_REMOTE_NAME" "$GIT_MAIN_BRANCH" # Push main branch to remote
10git switch "$GIT_DEV_BRANCH" # Switch back to development branch
Configuring DNS Records
To make your site accessible via your custom domain, you must update your DNS settings. At your domain registrar (e.g., Namecheap), buy a domain of your choice, navigate to Advanced DNS and add the following records to point your domain to the correct GitHub Pages IP addresses and repository URL.
1open "https://ap.www.namecheap.com/Domains/DomainControlPanel/$CUSTOM_DOMAIN/advancedns"
Click on Add New Record
and add A and CNAME records:
Type | Host | Value | TTL |
---|---|---|---|
A Record | @ | 185.199.108.153 | Automatic |
A Record | @ | 185.199.109.153 | Automatic |
A Record | @ | 185.199.110.153 | Automatic |
A Record | @ | 185.199.111.153 | Automatic |
CNAME Record | @ | <your $GIT_REPO_NAME value> | Automatic |
1open $GIT_USER_HOMEPAGE/$GIT_REPO_NAME/settings/pages
With DNS records set, finalize the connection in GitHub. Enter <your $CUSTOM_DOMAIN value> in the Settings > Pages > Custom domain input field. GitHub will provision the necessary SSL certificate. Your Hugo site is now live and accessible via your custom domain.