自定义配置
本指南介绍如何自定义 Hugo 网站的外观、行为和功能,从简单样式调整到高级布局修改。
概览
网站支持多层次自定义:
- 配置:站点设置、参数与行为
- 样式:颜色、字体、间距与视觉设计
- 布局:页面结构与模板修改
- 内容:自定义内容类型与组织方式
- 功能:JavaScript 特性与交互
站点配置
基础配置
主配置文件位于 config/_default/
:
config/
├── _default/
│ ├── config.toml # Hugo 核心设置
│ ├── params.toml # 主题参数
│ ├── menus.en.toml # 英文导航
│ └── menus.zh.toml # 中文导航
核心设置
编辑 config/_default/config.toml
:
baseURL = "https://your-domain.com"
title = "你的站点标题"
description = "你的站点描述"
# 语言设置
DefaultContentLanguage = "zh"
hasCJKLanguage = true
# 启用功能
enableGitInfo = true
pygmentsUseClasses = true
[params]
author = "你的名字"
email = "[email protected]"
主题参数
在 config/_default/params.toml
自定义主题行为:
# 站点品牌
[params]
header_title = "你的站点"
description = "你的描述"
logo = "images/logo.svg"
brand_icon = "fas fa-terminal"
# 功能
enable_comment = true
anchored = true
top_header = true
top_header_content = "欢迎信息"
# 分析
google_analytics_id = "UA-XXXXXXXX-X"
# 搜索
[search]
enable = true
provider = "wowchemy"
导航菜单
在 config/_default/menus.*.toml
配置导航:
# 中文菜单 (menus.zh.toml)
[[main]]
name = "首页"
url = "/"
weight = 1
[[main]]
name = "博客"
url = "/blog/"
weight = 2
[[main]]
name = "书籍"
url = "/book/"
weight = 3
hasChildren = true
[[main]]
parent = "书籍"
name = "Kubernetes 手册"
url = "/book/kubernetes-handbook/"
weight = 1
视觉自定义
颜色方案
在 assets/scss/_variables.scss
自定义颜色:
// 主色
$primary-color: #007bff;
$secondary-color: #6c757d;
$success-color: #28a745;
$warning-color: #ffc107;
$danger-color: #dc3545;
// 背景色
$body-bg: #ffffff;
$card-bg: #f8f9fa;
$navbar-bg: #ffffff;
// 文字色
$text-color: #333333;
$text-muted: #6c757d;
$link-color: #007bff;
$link-hover-color: #0056b3;
// 暗色模式
$dark-bg: #1a1a1a;
$dark-card-bg: #2d2d2d;
$dark-text: #ffffff;
$dark-text-muted: #cccccc;
排版
配置字体与排版:
// 字体族
$font-family-sans-serif: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
$font-family-monospace: "JetBrains Mono", "Fira Code", Consolas, monospace;
$font-family-serif: "Merriweather", Georgia, serif;
// 字号
$font-size-base: 1rem;
$font-size-lg: 1.125rem;
$font-size-sm: 0.875rem;
// 标题
$h1-font-size: 2.5rem;
$h2-font-size: 2rem;
$h3-font-size: 1.75rem;
$h4-font-size: 1.5rem;
$h5-font-size: 1.25rem;
$h6-font-size: 1rem;
// 行高
$line-height-base: 1.6;
$line-height-sm: 1.4;
$line-height-lg: 1.8;
自定义 CSS
在 assets/scss/_custom.scss
添加自定义样式:
// 自定义组件样式
.custom-hero {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 4rem 0;
h1 {
font-size: 3rem;
font-weight: 700;
margin-bottom: 1rem;
}
p {
font-size: 1.25rem;
opacity: 0.9;
}
}
// 自定义按钮样式
.btn-custom {
background: linear-gradient(45deg, #ff6b6b, #ee5a24);
border: none;
color: white;
padding: 0.75rem 2rem;
border-radius: 50px;
font-weight: 600;
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
}
}
// 自定义卡片样式
.card-custom {
border: none;
border-radius: 15px;
box-shadow: 0 5px 15px rgba(0,0,0,0.08);
transition: all 0.3s ease;
&:hover {
transform: translateY(-5px);
box-shadow: 0 15px 35px rgba(0,0,0,0.1);
}
}
暗色模式
配置暗色模式样式:
// 暗色模式变量
:root {
--bg-color: #{$body-bg};
--text-color: #{$text-color};
--card-bg: #{$card-bg};
--border-color: #{$border-color};
}
[data-theme="dark"] {
--bg-color: #{$dark-bg};
--text-color: #{$dark-text};
--card-bg: #{$dark-card-bg};
--border-color: #{$dark-border-color};
}
// 组件中使用变量
.card {
background-color: var(--card-bg);
color: var(--text-color);
border-color: var(--border-color);
}
布局自定义
页面模板
在 layouts/
目录下覆盖默认模板:
layouts/
├── _default/
│ ├── baseof.html # 基础模板
│ ├── single.html # 单页模板
│ └── list.html # 列表页模板
├── blog/
│ └── single.html # 博客文章模板
├── book/
│ ├── single.html # 书籍页面模板
│ └── list.html # 书籍列表模板
└── partials/
├── header.html # 头部
├── footer.html # 底部
└── sidebar.html # 侧边栏组件
自定义页面模板
为特定内容创建自定义模板:
<!-- layouts/blog/single.html -->
{{ define "main" }}
<article class="blog-post">
<header class="post-header">
<h1 class="post-title">{{ .Title }}</h1>
<div class="post-meta">
<time datetime="{{ .Date.Format "2006-01-02" }}">
{{ .Date.Format "January 2, 2006" }}
</time>
{{ with .Params.categories }}
<span class="categories">
{{ range . }}
<a href="{{ "/categories/" | relURL }}{{ . | urlize }}/" class="category">{{ . }}</a>
{{ end }}
</span>
{{ end }}
</div>
</header>
{{ if .Params.image }}
<div class="post-image">
<img src="{{ .Params.image | relURL }}" alt="{{ .Title }}" />
</div>
{{ end }}
<div class="post-content">
{{ .Content }}
</div>
<footer class="post-footer">
{{ with .Params.tags }}
<div class="tags">
{{ range . }}
<a href="{{ "/tags/" | relURL }}{{ . | urlize }}/" class="tag">#{{ . }}</a>
{{ end }}
</div>
{{ end }}
</footer>
</article>
{{ end }}
自定义 Partial
在 layouts/partials/
创建可复用组件:
<!-- layouts/partials/custom-hero.html -->
<section class="hero custom-hero">
<div class="container">
<div class="row align-items-center">
<div class="col-lg-6">
<h1>{{ .title | default "欢迎" }}</h1>
<p>{{ .description | default "你的描述" }}</p>
{{ if .button_text }}
<a href="{{ .button_url | default "#" }}" class="btn btn-custom">
{{ .button_text }}
</a>
{{ end }}
</div>
{{ if .image }}
<div class="col-lg-6">
<img src="{{ .image | relURL }}" alt="{{ .title }}" class="img-fluid" />
</div>
{{ end }}
</div>
</div>
</section>
在模板中调用 partial:
{{ partial "custom-hero.html" (dict "title" "我的标题" "description" "我的描述" "button_text" "立即开始" "button_url" "/getting-started/") }}
内容类型自定义
自定义 Archetype
在 archetypes/
创建内容模板:
# archetypes/tutorial.md
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
categories:
- "教程"
tags:
- "tutorial"
description: ""
difficulty: "beginner" # 初级、中级、高级
duration: "30 分钟"
prerequisites:
- "基础知识..."
learning_objectives:
- "学会如何..."
- "理解相关概念..."
resources:
- name: "资源 1"
url: "https://example.com"
draft: true
---
## 概述
简要介绍本教程内容和目标。
## 前置条件
开始前请确保:
- [ ] 完成基础设置
- [ ] 了解相关概念
## 步骤
### 步骤 1:准备工作
详细说明第一步。
### 步骤 2:实施
具体实施步骤。
## 总结
总结教程要点和下一步行动。
## 相关资源
- [相关教程 1](link)
- [相关教程 2](link)
自定义 Front Matter
为特定内容类型定义自定义 front matter 字段:
# 产品页面示例
---
title: "产品名称"
price: "¥99"
features:
- "功能 1"
- "功能 2"
specifications:
weight: "1.2kg"
dimensions: "30x20x10cm"
color: "黑色"
gallery:
- "image1.jpg"
- "image2.jpg"
reviews:
rating: 4.5
count: 127
---
JavaScript 自定义
自定义脚本
在 assets/js/custom.js
添加自定义 JS:
// 自定义功能
document.addEventListener('DOMContentLoaded', function() {
// 锚点平滑滚动
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
// 自定义搜索
const searchInput = document.querySelector('#custom-search');
if (searchInput) {
searchInput.addEventListener('input', function() {
const query = this.value.toLowerCase();
const items = document.querySelectorAll('.searchable-item');
items.forEach(item => {
const text = item.textContent.toLowerCase();
item.style.display = text.includes(query) ? 'block' : 'none';
});
});
}
// 主题切换
const themeToggle = document.querySelector('#theme-toggle');
if (themeToggle) {
themeToggle.addEventListener('click', function() {
const currentTheme = document.documentElement.getAttribute('data-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
});
}
});
// 通知工具函数
function showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.className = `notification notification-${type}`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.classList.add('show');
}, 100);
setTimeout(() => {
notification.classList.remove('show');
setTimeout(() => {
document.body.removeChild(notification);
}, 300);
}, 3000);
}
// 自定义分析追踪
function trackEvent(category, action, label) {
if (typeof gtag !== 'undefined') {
gtag('event', action, {
event_category: category,
event_label: label
});
}
}
交互组件
创建交互组件:
// 自定义手风琴组件
class CustomAccordion {
constructor(element) {
this.element = element;
this.triggers = element.querySelectorAll('.accordion-trigger');
this.init();
}
init() {
this.triggers.forEach(trigger => {
trigger.addEventListener('click', (e) => {
e.preventDefault();
this.toggle(trigger);
});
});
}
toggle(trigger) {
const content = trigger.nextElementSibling;
const isOpen = trigger.classList.contains('active');
// 关闭其他项
this.triggers.forEach(t => {
if (t !== trigger) {
t.classList.remove('active');
t.nextElementSibling.style.maxHeight = null;
}
});
// 切换当前项
if (isOpen) {
trigger.classList.remove('active');
content.style.maxHeight = null;
} else {
trigger.classList.add('active');
content.style.maxHeight = content.scrollHeight + 'px';
}
}
}
// 初始化手风琴
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.custom-accordion').forEach(accordion => {
new CustomAccordion(accordion);
});
});
高级自定义
自定义 Shortcode
在 layouts/shortcodes/
创建自定义 shortcode:
<!-- layouts/shortcodes/pricing-table.html -->
<div class="pricing-table">
{{ range .Site.Data.pricing }}
<div class="pricing-plan {{ if .featured }}featured{{ end }}">
<h3 class="plan-name">{{ .name }}</h3>
<div class="plan-price">
<span class="currency">{{ .currency }}</span>
<span class="amount">{{ .price }}</span>
<span class="period">{{ .period }}</span>
</div>
<ul class="plan-features">
{{ range .features }}
<li>{{ . }}</li>
{{ end }}
</ul>
<a href="{{ .signup_url }}" class="btn btn-primary">{{ .button_text }}</a>
</div>
{{ end }}
</div>
数据文件
在 data/
目录创建数据文件:
# data/pricing.yaml
- name: "基础版"
price: 9
currency: "¥"
period: "/月"
features:
- "5 个项目"
- "10GB 存储"
- "邮件支持"
button_text: "立即开始"
signup_url: "/signup/basic"
- name: "专业版"
price: 29
currency: "¥"
period: "/月"
featured: true
features:
- "无限项目"
- "100GB 存储"
- "优先支持"
- "高级分析"
button_text: "升级专业"
signup_url: "/signup/pro"
自定义构建流程
用自定义脚本扩展构建流程:
// scripts/custom-build.js
const fs = require('fs');
const path = require('path');
// 图片处理
function processImages() {
const imagesDir = path.join(__dirname, '../static/images');
const outputDir = path.join(__dirname, '../static/images/processed');
// 创建输出目录
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
// 图片处理逻辑(示例)
console.log('正在处理图片...');
// 此处添加图片处理代码
}
// 内容生成
function generateSitemap() {
const contentDir = path.join(__dirname, '../content');
const pages = [];
// 扫描内容目录
function scanDirectory(dir) {
const files = fs.readdirSync(dir);
files.forEach(file => {
const filePath = path.join(dir, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
scanDirectory(filePath);
} else if (file.endsWith('.md')) {
const relativePath = path.relative(contentDir, filePath);
pages.push(relativePath);
}
});
}
scanDirectory(contentDir);
// 生成 sitemap
const sitemap = pages.map(page => {
const url = page.replace(/\.md$/, '/').replace(/\\/g, '/');
return `https://your-domain.com/${url}`;
}).join('\n');
fs.writeFileSync(path.join(__dirname, '../static/sitemap.txt'), sitemap);
console.log(`已生成 ${pages.length} 个页面的 sitemap`);
}
// 执行自定义构建任务
processImages();
generateSitemap();
在 package.json
添加:
{
"scripts": {
"build": "node scripts/custom-build.js && hugo --environment production --minify",
"build:custom": "node scripts/custom-build.js"
}
}
性能优化
资源优化
在 config/_default/config.toml
配置资源处理:
[build]
postCSS = ["postcss.config.js"]
[imaging]
resampleFilter = "lanczos"
quality = 75
anchor = "smart"
[minify]
[minify.tdewolff]
[minify.tdewolff.html]
keepWhitespace = false
[minify.tdewolff.css]
keepCSS2 = true
[minify.tdewolff.js]
keepVarNames = false
缓存策略
Configure caching headers:
# netlify.toml or _headers file
[[headers]]
for = "*.css"
[headers.values]
Cache-Control = "public, max-age=31536000"
[[headers]]
for = "*.js"
[headers.values]
Cache-Control = "public, max-age=31536000"
[[headers]]
for = "*.woff2"
[headers.values]
Cache-Control = "public, max-age=31536000"
Testing Customizations
Local Testing
Test your customizations locally:
# Start development server
hugo server
# Test with different environments
hugo server --environment production
# Test build process
npm run build
# Test on different devices
hugo server --bind 0.0.0.0 --port 1313
Validation
Validate your customizations:
# Check HTML validity
npm run test:html
# Check CSS
npm run test:css
# Check JavaScript
npm run test:js
# Check accessibility
npm run test:a11y
Troubleshooting
Common Issues
Styles Not Loading
Problem: Custom CSS doesn't appear on the site
Solutions:
- Check file paths in SCSS imports
- Verify build process includes CSS compilation
- Clear browser cache
- Check for CSS syntax errors
JavaScript Errors
Problem: Custom JavaScript doesn't work
Solutions:
- Check browser console for errors
- Verify script loading order
- Ensure DOM elements exist before accessing them
- Check for JavaScript syntax errors
Layout Issues
Problem: Custom layouts don't render correctly
Solutions:
- Check template syntax
- Verify template file locations
- Check for missing partials or data
- Test with different content types
Examples
Here are some examples of what you can achieve with customization:
Example: Creating a Promotional Banner
You can use a custom partial to create a promotional banner that appears on top of your homepage.
-
Create the partial (
layouts/partials/promo-banner.html
):<div class="promo-banner"> <p>🎉 Special Offer! Get 20% off on all books. <a href="/store">Shop Now</a></p> </div>
-
Add styling (
assets/scss/_custom.scss
):.promo-banner { background-color: var(--primary-color); color: white; text-align: center; padding: 0.5rem; }
-
Include the partial in
layouts/_default/baseof.html
:<body> {{ partial "promo-banner.html" . }} {{ partial "header.html" . }} ... </body>
Example: Adding a "Last Updated" Date
Automatically display the last modification date on your blog posts.
-
Enable
enableGitInfo
inconfig/_default/config.toml
:enableGitInfo = true
-
Modify the template (
layouts/blog/single.html
):<div class="post-meta"> ... {{ if .GitInfo }} <span>Last updated on {{ .GitInfo.AuthorDate.Format "January 2, 2006" }}</span> {{ end }} </div>
Next Steps
After customizing your site:
- Set up development environment
- Learn about advanced features
- Explore content analysis
- Contribute improvements
Related Topics
- Developer Setup - Set up advanced development environment
- Architecture Guide - Understand the site architecture
- Advanced Features - Explore advanced functionality
- Configuration Reference - Complete configuration options
Need help? Check our Troubleshooting Guide or Contributing Guidelines.