次世代 Hugo

無駄を削ぎ、本質を研ぐ

建立 CSS Partial

Sam Xiao's Avatar 2025-07-16

HTML 的 <head> 會載入 CSS,也是各 Template 都會重複使用之處,可將其抽成 Partial 方便維護。

Version

Hugo 0.148.0

SCSS

assets/scss/main.scss

@use 'vars';
@use 'baseof';
@use 'home';
@use 'single';
@use 'list';
@use '_partials/header';
@use '_partials/footer';
@use 'page/about';
  • 使用 @use 引用其他 SCSS,本身不寫任何 style

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" />
{{ with resources.Get "scss/main.scss" }}
  {{ $opts := dict
    "enableSourceMap" hugo.IsDevelopment
    "outputStyle" (cond hugo.IsDevelopment "expanded" "compressed")
    "transpiler" "dartsass"
    "targetPath" "css/main.css"
  }}
  {{ with . | toCSS $opts }}
    {{ if hugo.IsDevelopment }}
      <link rel="stylesheet" href="{{ .RelPermalink }}" />
    {{ else }}
      {{ with . | fingerprint }}
        <link
          rel="stylesheet"
          href="{{ .RelPermalink }}"
          integrity="{{ .Data.Integrity }}"
          crossorigin="anonymous" />
      {{ end }}
    {{ end }}
  {{ end }}
{{ end }}
  • 負責載入 CSS

Line 3

<title>{{ .Site.Title }}</title>
  • Site.Title:讀取 title site variable

Line 4

<link rel="icon" href="/favicon.ico" />
  • 引用在 static 目錄下的 favicon.ico

Line 5

{{ with resources.Get "scss/main.scss" }}
{{ end }}
  • resources.Get():從 assets 目錄取得 main.scss
  • with():若 scss/main.scss 存在,將 目前 context 切到所 回傳物件 繼續 pipe

Line 6

{{ $opts := dict
  "enableSourceMap" hugo.IsDevelopment
  "outputStyle" (cond hugo.IsDevelopment "expanded" "compressed")
  "transpiler" "dartsass"
  "targetPath" "css/main.css"
}}
  • dict():整理 toCSS() 所需 map 參數
    • enableSourceMap開發模式true生產模式 時則 false
    • outputStyle開發模式不壓縮生產模式壓縮
    • transpiler:指定使用 Dart Sass,而非內建的 LibSass
    • targetPath:最終產出為 css/main.css

Line 12

{{ with . | toCSS $opts }}
{{ end }}
  • toCSS():將 目前 context 傳入 toCSS $opts
    • 目前 contextscss/main.scss 物件
    • toCSS $opts 為 partial function,因此可將 . 傳入
  • with():若 toCSS() 成功,則將 目前 context 切到 回傳物件 繼續 pipe

Line 13

{{ if hugo.IsDevelopment }}
  <link rel="stylesheet" href="{{ .RelPermalink }}" />

開發模式 下處理 CSS:

  • RelPermalink:直接從 目前 context 取得 其相對路徑
    • 目前 contexttoCSS() 回傳物件

Line 15

{{ else }}
  {{ with . | fingerprint }}
    <link
       rel="stylesheet"
       href="{{ .RelPermalink }}"
       integrity="{{ .Data.Integrity }}"
       crossorigin="anonymous" />
  {{ end }}
{{ end }}

生產模式 下處理 CSS:

  • fingerprint():將 CSS 檔名加上 hash,避免被連覽器 cache
    • 目前 contexttoCSS() 回傳物件
  • with():若 fingerprint() 成功,則將 目前 context 切到 回傳物件 繼續 pipe
  • RelPermalink:直接從 目前 context 取得 其相對路徑
  • Data.Integrity:提供 SRI,用來讓瀏覽器驗證資源內容是否被竄改,需搭配 crossorigin="anonymous"
    • 目前 contextfingerprint() 回傳物件

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 要處理的東西看似簡單,這原本都要靠 Gulp、Webpack、Vite 之類工具處理,但這些 Hugo 都已經內建,只需 Pipe 即可,可將這些處理過程整合在 css Partial 內
  • 之所以要朝狀使用 with(),因為每個 Function 執行會成功或失敗,只有成功後才會將 目前 Context 切到 回傳物件 繼續 Pipe
  • 此段處理 CSS 流程摘自 Hugo 官網,極具參考價值,是非常優秀的 Go template 寫法