hugoでカテゴリによって背景を変える

背景

カテゴリごとに背景を変えられたら、今見ている記事の分野が少し分かりやすくなる。 また、背景に軽いアニメーションを加えると、技術メモの見た目にも変化を付けられる。

このサイトでは、Markdownのフロントマターにある categories を使い、カテゴリごとにCSSと背景用HTMLを切り替えている。

現在は次の3種類を設定している。

categoriesの値背景
docker海の背景と揺れるDockerのクジラ
AWSClient、CloudFront、ALB、ECS、RDS、ElastiCacheの構成図
bashtcpdump風のログが流れる黒い画面

カテゴリ名は大文字・小文字を区別するため、AWSの記事では aws ではなく AWS と記述する。

Hugoでの分岐

Hugoでは .Params を経由して、ページのフロントマターに設定した値を参照できる。

今回使用するのは .Params.categories である。

参考: Page variables | Hugo

処理は次の2か所に分けている。

  1. layouts/partials/head.html でカテゴリ別のCSSとJavaScriptを読み込む
  2. layouts/_default/single.html で背景表示に必要なHTMLを生成する

背景画像やアイコンは static/images/、カテゴリ別CSSは static/css/ に配置している。

変更箇所

現在の主な関連ファイルは次のとおり。

ファイル役割
layouts/partials/head.htmlカテゴリ別CSSとJavaScriptの読み込み
layouts/_default/single.html背景用HTMLの生成
static/css/docker.cssDocker背景
static/css/aws.cssAWS構成図の基本レイアウト
static/css/aws-icons.cssAWSアイコンとスケールアニメーション
static/css/aws-flow.css接続線とアイコンだけの表示調整
static/css/aws-article.cssAWS記事本文と構成図の最終的な見た目
static/js/aws-flow.jsオブジェクト間の線と光点の描画
static/css/bash.csstcpdump風背景
assets/css/userstyles.cssサイト全体の共通スタイル

layouts/partials/head.html

<head> 内で、Markdownの categories に応じて読み込むファイルを切り替える。

{{ if in .Params.categories "docker" }}
<link rel="stylesheet" href="/css/docker.css">
{{ else if in .Params.categories "AWS" }}
<link rel="stylesheet" href="/css/aws.css">
<link rel="stylesheet" href="/css/aws-icons.css">
<link rel="stylesheet" href="/css/aws-flow.css">
<link rel="stylesheet" href="/css/aws-article.css">
<script defer src="/js/aws-flow.js"></script>
{{ else if in .Params.categories "bash" }}
<link rel="stylesheet" href="/css/bash.css">
{{ end }}

カテゴリ別のファイルは共通CSSより後に読み込む。 こうすると、背景を持つ記事だけに追加スタイルを適用でき、カテゴリ固有の設定で共通スタイルを上書きできる。

AWSでは役割が増えたため、1つの巨大なCSSにせず、レイアウト、アイコン、通信フロー、記事本文の表示調整を分割している。

layouts/_default/single.html

記事本文を表示した後に、カテゴリごとの背景用HTMLを追加する。

実際のAWS構成図は複数の要素を持つが、分岐の基本形は次のようになる。

{{ if in .Params.categories "docker" }}
<div id="docker-whale"></div>
{{ else if in .Params.categories "AWS" }}
<div id="aws-system" aria-hidden="true">
  <div class="aws-architecture">
    <!-- AWS構成図 -->
  </div>
</div>
{{ else if in .Params.categories "bash" }}
<div id="tcpdump-output" aria-hidden="true">
  <!-- tcpdump風のログ -->
</div>
{{ end }}

背景用要素は記事本文より後ろに配置し、positionz-index で重なり順を制御する。

背景は装飾なので、内容を読み上げる必要がないものには aria-hidden="true" を付ける。

Docker背景

Dockerカテゴリでは、海の背景画像を固定し、その上でDockerのクジラを揺らしている。

body {
  background: url('/images/sea-background.png') no-repeat center center fixed;
  background-size: cover;
}

#docker-whale {
  position: fixed;
  top: 50%;
  left: 50%;
  width: 60%;
  height: 60%;
  background: url('/images/docker.png') center / contain no-repeat;
  animation: wave 20s infinite;
  z-index: 0;
}

main {
  position: relative;
  z-index: 100;
}

@keyframes wave {
  0%, 100% {
    transform: translate(-50%, -50%) rotate(10deg);
  }

  50% {
    transform: translate(-50%, -50%) rotate(-10deg);
  }
}

position: fixed にすることで、本文をスクロールしても背景のクジラは画面内に残る。

AWS背景

AWSカテゴリでは、1枚の背景画像ではなく、HTML、CSS、SVG、JavaScriptを組み合わせて構成図を表示している。

現在の構成は次の流れを表している。

Client
CloudFront
Application Load Balancer
ECS Tasks
  ├─ RDS Writer / Readers
  └─ ElastiCache Primary / Replicas

JavaScriptは実際に描画された各オブジェクトの位置を取得し、SVGの曲線で接続する。 そのため、画面幅が変わった場合や、スケールアウトのアニメーションでノードが移動した場合も線が追従する。

ECS Task、RDS Reader、ElastiCache Replicaは、opacity: 0 から opacity: 1 へ変化させてスケールアウトを表現する。 この透明度はノードの増減を表すアニメーションとして意図的に残している。

本文背景と構成図の透明度を分ける

要素全体に opacity を指定すると、その子要素にある文字やアイコンもすべて薄くなる。 本文の読みやすさを調整したい場合は、本文全体の opacity ではなく、背景色のアルファ値を変更する。

現在のAWS記事では、本文を白60%、背後の構成図を40%見せている。

main {
  background-color: rgba(255, 255, 255, 0.6) !important;
}

main article,
.blog-post-content {
  background-color: transparent !important;
}

.aws-architecture {
  opacity: 1 !important;
}

rgba(255, 255, 255, 0.6) の最後の 0.6 は、白背景の不透明度が60%であることを表す。 構成図そのものは不透明のままにして、本文の白背景だけで見え方を調整する。

このように設定箇所を分けておくと、構成図の色や接続線を薄くせず、本文の可読性だけを調整できる。

Bash背景

Bashカテゴリでは、画面全体に黒い背景を置き、緑色のtcpdump風ログを縦方向へ流している。

#content #tcpdump-output {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  z-index: 1;
  background-color: black;
}

#content #tcpdump-output .tcpdump-content {
  position: absolute;
  width: 100%;
  height: 200%;
  animation: scroll 10s linear infinite;
}

#content #tcpdump-output .tcpdump-text p {
  color: #008000;
}

本文は背景より前面に置き、白い半透明背景を使って可読性を確保する。

Markdownでカテゴリを指定する

Docker記事の例。

---
title: docker
date: "2021-08-08T21:39:03.284Z"
categories:
- "docker"
tags:
- "docker"
---

AWS記事では大文字の AWS を指定する。

---
title: aws/s3
date: "2021-08-21T22:30:03.284Z"
categories:
- "AWS"
tags:
- "s3"
---

Bash記事の例。

---
title: bashでcgi
date: "2019-07-30T22:30:03.284Z"
categories:
- "bash"
tags:
- "bash"
---

実装時の注意点

  • カテゴリ名の大文字・小文字をフロントマターとテンプレートで一致させる
  • 背景用CSSとJavaScriptは対象カテゴリの記事だけで読み込む
  • 本文の可読性は opacity ではなく background-color: rgba(...) で調整する
  • 背景用要素には pointer-events: none を設定し、本文のリンクや選択操作を妨げないようにする
  • 装飾目的の背景は aria-hidden="true" にする
  • 動きが必須でない場合は prefers-reduced-motion でアニメーションを停止できるようにする
  • 複雑な背景はCSSやJavaScriptを役割ごとに分割する

表示例