这是一个系列博客,记录了我从零开始构建Hugo主题https://github.com/tomowang/hugo-theme-tailwind的过程。全系列包括四篇文章,这是第二篇:
在开始构建主题前,我们需要对hugo主题的一些术语、语法及目录结构有初步的了解。
hugo使用html/template
来渲染内容,方法和变量通过{{ }}
进行访问。常用的语法如下:
{{ $address }}
{{ FUNCTION ARG1 ARG2 .. }}
{{ range $array }}
{{ . }} <!-- The . represents an element in $array -->
{{ end }}
with
if
else
等{{ with .Params.title }}
<h4>{{ . }}</h4>
{{ end }}
{{ if (isset .Params "description") }}
{{ index .Params "description" }}
{{ else }}
{{ .Summary }}
{{ end }}
{{ .Title | .RenderString }}
.
以及全局上下文$.
-
去除额外的空字符串<div>
{{- .Title -}}
</div>
详细的语法描述,可以参考官方文档https://gohugo.io/templates/introduction/。
同时hugo还提供很多内置的变量,这些变量通过主题文件的渲染,最终生成静态页面:
我们在使用主题来渲染页面内容时,需要根据对应的变量,通过属性或者方法调用,最终呈现我们想要的内容。
例如我们期望展示单个文章的阅读时长,可以调用Page
变量的ReadingTime
方法(https://gohugo.io/methods/page/readingtime/)。
部分对象方法较多,具体可以参考官方文档https://gohugo.io/quick-reference/methods/。
我们创建了基本的hugo主题后,使用命令tree -L 2 -d .
可以看到主题目录的基本结构:
.
├── archetypes
├── assets
│ ├── css
│ └── js
├── data
├── i18n
├── layouts
│ ├── _default
│ └── partials
└── static
相关目录含义如下:
archetypes
- 创建新内容页面的模板assets
- 静态资源文件,一般会经由资源管道进行处理data
- 用于存储额外的数据,可以参考https://gohugo.io/templates/data-templates/i18n
- 存储本地化文件layouts
- 核心的模板布局文件static
- 静态文件,会被直接复制到最终public目录其中最核心的为layouts目录,该目录决定了模板支持的页面以及页面的布局。
hugo使用一套规则来查询内容页面应该使用的模板文件。一般站点由以下几种类型的页面构成:
其查找顺序可以参考官方文档https://gohugo.io/templates/lookup-order/。
官方文档介绍了复杂的查找顺序,但主题的大部分页面结构类似,所以实际情况中只需要定义少数页面就能达到期望的结果。
在开始构建前,需要安装tailwindcss
,我参考官方文档做了一些调整:
# install
pnpm install -D tailwindcss
# init
npx tailwindcss init
通过上面的命令安装tailwindcss
并生成tailwind.config.js
配置文件,
由于我们的页面内容在layout目录,需要调整tailwind.config.js
中的content
配置:
content: ["./layouts/**/*.html"],
将tailwindcss
的引用放在assets/css/main.css
中,
@tailwind base;
@tailwind components;
@tailwind utilities;
然后可以通过如下命令对页面内容进行监听生成实际使用的CSS文件:
npx tailwindcss -i assets/css/main.css -o ./static/main.css --watch
基于我的需求以及前文对hugo目录结构的描述,最终我定义了如下的基础页面
baseof.html
- 承载主体页面的布局list.html
- 列表页、术语列表页single.html
- 单页,主要的内容页terms.html
- 术语页通过baseof.html
文件,我们定义基础模板,该模板会应用在所有的页面,其核心内容如下:
DOCTYPE html>
<html lang="{{ or site.Language.LanguageCode site.Language.Lang }}" dir="{{ or site.Language.LanguageDirection `ltr` }}">
<head>
{{ block "title" . }}
<title>
{{ if .IsHome }}{{ $.Site.Title }}{{ with $.Site.Params.Subtitle }} —
{{ . }}{{ end }}{{ else }}{{ .Title }} ::
{{ $.Site.Title }}{{ with $.Site.Params.Subtitle }} — {{ . }}{{ end }}{{ end }}
title>
{{ end }}
{{ partial "head.html" . }}
head>
<body class="w-full bg-slate-50">
<header class="flex flex-none justify-center">
{{ partial "header.html" . }}
header>
<main class="flex flex-auto justify-center">
{{ block "main" . }}{{ end }}
main>
<footer class="flex flex-none justify-center">
{{ partial "footer.html" . }}
footer>
body>
html>
我使用{{ partial "head.html" . }}
引入head
标签需要的内容,并在head.html
中引用了前文生成的CSS文件:
<link rel="stylesheet" href="{{ "main.css" | relURL }}" />
HTML body
标签中,header
标签为头部导航,footer
标签为底部菜单,main
标签为内容主体。
main
标签中{{ block "main" . }}{{ end }}
的内容,我们需要在其他页面进行定义,如简化版的list.html
代码如下:
{{ define "main" }}
<div class="flex flex-col w-full max-w-4xl lg:max-w-5xl relative">
<div class="flex flex-row">
<section class="flex flex-col w-2/3">
{{ range (.Paginate $pages).Pages }}
<article class="flex flex-col gap-y-3 p-6 mt-6 rounded-lg shadow-md bg-white">
<h2 class="text-4xl font-semibold text-slate-800">
<a href="{{ .RelPermalink }}">{{ .Title | markdownify }}a>
h2>
article>
{{ end }}
section>
<aside class="flex flex-col w-1/3 mx-3 top-0 sticky self-start">
{{ partial "sidebar.html" . }}
aside>
div>
{{ partial "pagination.html" . }}
div>
{{ end }}
其中section
标签对应文章列表块,aside
标签对应侧边术语展示块。
单页singhle.html
内容类似,包含了文章标题、TOC内容、文章主体等内容。
tailwindcss
的好处是有现成的typography
插件可以直接渲染文章内容,而不必额外定义复杂的CSS样式。
为了复用一些组件,如标签列表、阅读时长、图标等,我们可以将相同的内容进行抽象,放到layouts/partials
目录中。
以图标为例,我使用https://tabler.io/icons作为图标来源,
下载对应图标的SVG文件放到assets
目录,然后定义partial
页面:
{{- $iconFile := resources.GetMatch (printf "icons/%s.svg" .) -}}
{{- if $iconFile -}}
{{- $iconFile.Content | safeHTML -}}
{{- else -}}
{{- errorf "Error: icon '%s.svg' is not found under 'assets/icons' folder" . -}}
{{- end -}}
其中.
符号为传递的图标名称参数,这样我们就能快速的在页面中进行图标的引用,如
{{ partial "icon" "calendar" }}
通过大概5天业余时间的开发(#bf54ab9),
最终呈现的效果如下:
当然,这是第一阶段的内容,只有白色主题(主题切换按钮点击无反应),没有多语言,也没有响应式。
但是主体的页面结构已经有了,tailwindcss
在处理CSS样式时也表现了其高效、灵活的特性。