次世代 Hugo

無駄を削ぎ、本質を研ぐ

將多個 CSS 檔合併成單一 CSS 檔

Sam Xiao's Avatar 2025-08-15

實務上會想將每個 Template 或 Partial 有自己的 CSS 檔,在開發階段亦保持獨立,直到編譯階段再將所有 CSS 檔合併成單一 CSS 檔。

Version

Hugo 0.148.2

Hugo Config

hugo.toml

name = 'starter theme'

[params.css]
files = [
  "css/vars.css",
  "css/reset.css",
  "css/baseof.css",
  "css/home.css",
  "css/single.css",
  "css/list.css",
  "css/_partials/header.css",
  "css/_partials/footer.css",
  "css/page/about.css"
]
  • hugo.toml[params.css]fiiles 設定所有 CSS 檔案路徑,將來若有新的 CSS 檔加入,只要修改 hugo.toml 即可,不必修改 CSS Partial

CSS Partial

layouts/_partials/css.html

<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{ .Site.Title }}</title>
<link rel="icon" href="/favicon.ico" />

{{ $isDev := hugo.IsServer }}
{{ $files := .Site.Params.css.files }}

{{ if $isDev }}
  {{ range $files }}
    {{ with resources.Get . }}
      <link rel="stylesheet" href="{{ .RelPermalink }}" />
    {{ else }}
      {{ warnf "CSS file not found: %q" . }}
    {{ end }}
  {{ end }}
{{ else }}
  {{ $res := slice }}
  {{ range $files }}
    {{ with resources.Get . }}
      {{ $res = $res | append . }}
    {{ else }}
      {{ warnf "CSS file not found: %q" . }}
    {{ end }}
  {{ end }}
  {{ $bundle := $res
    | resources.Concat "css/main.css"
    | resources.Minify
    | resources.Fingerprint
  }}
  <link
    rel="stylesheet"
    href="{{ $bundle.RelPermalink }}"
    integrity="{{ $bundle.Data.Integrity }}"
    crossorigin="anonymous" />
{{ end }}
  • CSS Partial 負責將多個 CSS 檔合併成單一 CSS 檔,並加以壓縮與加上 fingerprint

Line 6

{{ $isDev := hugo.IsServer }}
  • hugo.IsServer:判斷是否正在執行開發中的 Web Server,可藉由此判斷是否正在 開發模式,否則則為 編譯模式

Line 7

{{ $files := .Site.Params.css.files }}
  • .Site.Params.css 讀取 files slice

Line 9

{{ if $isDev }}
  {{ range $files }}
    {{ with resources.Get . }}
      <link rel="stylesheet" href="{{ .RelPermalink }}" />
    {{ else }}
      {{ warnf "CSS file not found: %q" . }}
    {{ end }}
  {{ end }}
{{ else }}
  • 若為 開發模式 則多個 CSS 檔不必合併與壓縮,保持原本多個獨立 CSS 檔方便 debug
  • range():使用迴圈走訪 files slice 內每個 CSS 檔路徑
  • with():確保 resources.Get()nil,若為 nil 則使用 warnf() 印出錯誤訊息幫助 debug

Line 17

{{ else }}
  {{ $res := slice }}
  {{ range $files }}
    {{ with resources.Get . }}
      {{ $res = $res | append . }}
    {{ else }}
      {{ warnf "CSS file not found: %q" . }}
    {{ end }}
  {{ end }}
  {{ $bundle := $res
    | resources.Concat "css/main.css"
    | resources.Minify
    | resources.Fingerprint
  }}
  <link
    rel="stylesheet"
    href="{{ $bundle.RelPermalink }}"
    integrity="{{ $bundle.Data.Integrity }}"
    crossorigin="anonymous" />
{{ end }}
  • 若為 編譯模式 則將多個 CSS 檔合併與壓縮,並加上 fingerprint

Line 18

{{ $res := slice }}
  • slice():建立 empty slice

Line 19

{{ range $files }}
  {{ with resources.Get . }}
    {{ $res = $res | append . }}
  {{ else }}
    {{ warnf "CSS file not found: %q" . }}
  {{ end }}
{{ end }}
  • range():使用迴圈走訪 files slice 內每個 CSS 檔路徑
  • with():確保 resources.Get()nil,若為 nil 則使用 warnf() 印出錯誤訊息幫助 debug
  • append():將 element 加入到 files slice 內

Line 26

{{ $bundle := $res
  | resources.Concat "css/main.css"
  | resources.Minify
  | resources.Fingerprint
}}
  • resources.Concat():將多個 CSS 檔合併成 main.css
  • resources.Minify():將 CSS 檔壓縮
  • resources.Fingerprint():將 CSS 檔名加上 fingerprint

Line 31

<link
  rel="stylesheet"
  href="{{ $bundle.RelPermalink }}"
  integrity="{{ $bundle.Data.Integrity }}"
  crossorigin="anonymous" />
  • RelPermalink:取得 main.css相對路徑
  • Data.Integrity:提供 SRI,用來讓瀏覽器驗證資源內容是否被竄改,需搭配 crossorigin="anonymous"

Baseof Template

layouts/baseof.html

<!doctype html>
<html lang="en">
  <head>
    {{ partial "css.html" . }}
  </head>
  <body>
    {{ block "main" . }}{{ end }}
    {{ partial "js.html" . }}
  </body>
</html>
  • 所有 template 基礎的 baseof.html

Line 3

<head>
  {{ partial "css.html" . }}
</head>
  • <head> 內引用 css partial

Conclusion

  • 藉由將 CSS 檔案路徑統一寫在 hugo.toml 內,將來若有新的 CSS 檔案加入,不必去修改 CSS Partial,統一修改 hugo.toml 即可
  • CSS 雖然沒有如 Sass 提供 @import 將多個 SCSS 檔案合併成單一 CSS 檔功能,但透過 Hugo pipe 反而能達成 開發模式 CSS 多檔,而 編譯模式 CSS 單檔的效果