summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-08-22 00:18:56 +0900
committertanaka takashi <isazaka@gmail.com>2026-08-22 00:19:26 +0900
commit9f2fb3bda733c32995c008bd0f40365e43c348da (patch)
tree663a6b48ec83b2b6ba2c998dcfed89e0bb29f172
parent31b19ee74469c23633bbea4e021d91ddd8c34e3a (diff)
feat: Zolaで「たかしの記録」を構築
Specifications.mdとDesign.mdを実装の基準として、個人アーカイブサイトをZola 0.23.3向けに構築する。 - Home、Notes、Software、About、Tagsの情報設計と専用URLを追加 - Notesの年別アーカイブ、タグ分類、目次、脚注、参考文献、更新日表示を実装 - Softwareの一覧・詳細、cgit/GitHub情報、Notesとの明示的な相互参照を実装 - Tera 2 componentによる再帰TOCと共通partialsを追加 - Minimal Editorial ArchiveのSCSS、レスポンシブ表示、アクセシビリティ対応を追加 - OS設定とlocalStorageに対応するLight/Darkテーマ切替を外部JavaScriptで実装 - Atom/RSS、sitemap、robots.txt、404、favicon、syntax highlightingを設定 - メールアドレスをzola.tomlへ一元化し、AboutとFooterから参照 - 表示名・内部ディレクトリ・変数・URLをSoftware/softwareへ統一 - public/をGit管理対象外とし、外部CDNやNode.js依存を導入しない - zola checkおよびzola buildの成功を確認
-rw-r--r--.gitignore1
-rw-r--r--content/_index.md4
-rw-r--r--content/about.md26
-rw-r--r--content/notes/_index.md9
-rw-r--r--content/notes/reading-and-questions.md12
-rw-r--r--content/notes/static-sites-and-maintenance.md46
-rw-r--r--content/software/_index.md9
-rw-r--r--content/software/archive-site/index.md36
-rw-r--r--content/software/archive-site/screenshot.svg17
-rw-r--r--content/software/content-notes.md23
-rw-r--r--doc/Design.md1851
-rw-r--r--doc/Specifications.md995
-rw-r--r--sass/_archive.scss90
-rw-r--r--sass/_article.scss159
-rw-r--r--sass/_base.scss47
-rw-r--r--sass/_code.scss38
-rw-r--r--sass/_footer.scss29
-rw-r--r--sass/_header.scss77
-rw-r--r--sass/_layout.scss106
-rw-r--r--sass/_reset.scss41
-rw-r--r--sass/_software.scss70
-rw-r--r--sass/_taxonomy.scss19
-rw-r--r--sass/_theme.scss16
-rw-r--r--sass/_tokens.scss42
-rw-r--r--sass/_typography.scss42
-rw-r--r--sass/_utilities.scss25
-rw-r--r--sass/main.scss14
-rw-r--r--static/favicon.svg6
-rw-r--r--static/js/theme.js75
-rw-r--r--templates/404.html17
-rw-r--r--templates/base.html26
-rw-r--r--templates/index.html62
-rw-r--r--templates/notes/page.html31
-rw-r--r--templates/notes/section.html31
-rw-r--r--templates/page.html15
-rw-r--r--templates/partials/footer.html10
-rw-r--r--templates/partials/header.html19
-rw-r--r--templates/partials/note-list-item.html14
-rw-r--r--templates/partials/related-notes.html11
-rw-r--r--templates/partials/related-software.html11
-rw-r--r--templates/partials/software-list-item.html4
-rw-r--r--templates/partials/toc.html10
-rw-r--r--templates/software/page.html27
-rw-r--r--templates/software/section.html24
-rw-r--r--templates/tags/list.html18
-rw-r--r--templates/tags/single.html20
-rw-r--r--zola.toml218
47 files changed, 3978 insertions, 515 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..87174b6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/public/
diff --git a/content/_index.md b/content/_index.md
new file mode 100644
index 0000000..7db2929
--- /dev/null
+++ b/content/_index.md
@@ -0,0 +1,4 @@
++++
+title = "たかしの記録"
+description = "日々考えたこととか思ったことを記録します"
++++
diff --git a/content/about.md b/content/about.md
new file mode 100644
index 0000000..b0f203c
--- /dev/null
+++ b/content/about.md
@@ -0,0 +1,26 @@
++++
+title = "About"
+template = "page.html"
++++
+
+## 名前
+
+たかし。ソフトウェアを作り、本を読み、考えたことを記録しています。
+
+## このサイトについて
+
+「たかしの記録」は、日々考えたこと、感じたこと、調べたことと、作成したソフトウェアを長く保存するための個人アーカイブです。更新の速さよりも、あとから読み返せることを大切にしています。
+
+## 関心分野
+
+- 情報工学とソフトウェア
+- 論理学
+- マルクス、資本論、政治経済
+- 地球環境
+- 読書を通じた考察
+
+## 連絡先
+
+[{{ config.extra.mail }}](mailto:{{ config.extra.mail }})
+
+掲載内容は初期サンプルです。公開時に、プロフィールと連絡先を実際の情報へ差し替えてください。
diff --git a/content/notes/_index.md b/content/notes/_index.md
new file mode 100644
index 0000000..3e6bb92
--- /dev/null
+++ b/content/notes/_index.md
@@ -0,0 +1,9 @@
++++
+title = "Notes"
+description = "考えたこと、感じたこと、調べたことの記録。"
+sort_by = "date"
+template = "notes/section.html"
+page_template = "notes/page.html"
++++
+
+分野を固定せず、文章として残したいものをまとめています。
diff --git a/content/notes/reading-and-questions.md b/content/notes/reading-and-questions.md
new file mode 100644
index 0000000..6f67c0c
--- /dev/null
+++ b/content/notes/reading-and-questions.md
@@ -0,0 +1,12 @@
++++
+title = "読書の途中にある問いを残す"
+description = "結論だけでなく、読んでいる途中の問いを記録することについて。"
+date = 2025-11-03
+
+[taxonomies]
+tags = ["読書", "論理学"]
++++
+
+本を読んだあとに結論だけを書こうとすると、文章を残す機会を逃すことがあります。理解できなかった箇所や、別の文章とのつながりも、その時点での大切な記録です。
+
+問いを未完成のまま残しておけば、次に同じ主題へ戻ったときの出発点になります。
diff --git a/content/notes/static-sites-and-maintenance.md b/content/notes/static-sites-and-maintenance.md
new file mode 100644
index 0000000..10fcd7d
--- /dev/null
+++ b/content/notes/static-sites-and-maintenance.md
@@ -0,0 +1,46 @@
++++
+title = "静的サイトと長期的な保守"
+description = "仕組みを小さく保つことが、長く書き続けるためにどう役立つか。"
+date = 2026-08-21
+updated = 2026-08-22
+
+[taxonomies]
+tags = ["情報工学", "Web"]
+
+[extra]
+related_software = [
+ "software/archive-site/index.md",
+]
++++
+
+個人サイトの目的は、必ずしも最新の機能を提供することではありません。文章を残し、必要なときに読み返せることが中心なら、静的な構成は今もよく合います。
+
+## 保守する対象を減らす
+
+動的な仕組みを採用すると、アプリケーションだけでなく、実行環境やデータベースも保守対象になります。静的サイトでは、公開環境に置くものを生成済みのHTMLとCSSへ限定できます。
+
+```text
+Markdown → Zola → HTML / CSS / JS
+```
+
+構成を小さくすることは、機能を諦めることではありません。目的に必要な部分を見極め、理解できる範囲に留める判断です[^small].
+
+### 更新の手順
+
+記事を書くときの流れも単純です。
+
+1. Markdownを追加する
+2. `zola check`で内部リンクを確認する
+3. `zola build`で静的ファイルを生成する
+4. 生成物だけを配信する
+
+## 読む時間を中心に置く
+
+長く残す文章では、画面の動きより、行長や余白、リンクの見分けやすさが重要です。このサイトでは[Archive Site](@/software/archive-site/index.md)の設計を通して、その考え方を試しています。
+
+## 参考文献
+
+1. Zola, *Documentation*.
+2. W3C, *Web Content Accessibility Guidelines (WCAG)*.
+
+[^small]: ここでいう「小さい」は、ファイル数ではなく、運用時に理解すべき境界が少ないことを指します。
diff --git a/content/software/_index.md b/content/software/_index.md
new file mode 100644
index 0000000..d7e092e
--- /dev/null
+++ b/content/software/_index.md
@@ -0,0 +1,9 @@
++++
+title = "Software"
+description = "作成したソフトウェアと、その背景や過程。"
+sort_by = "date"
+template = "software/section.html"
+page_template = "software/page.html"
++++
+
+ソースコードの正本は自前のcgitで公開し、ここでは人間向けの説明を記録します。
diff --git a/content/software/archive-site/index.md b/content/software/archive-site/index.md
new file mode 100644
index 0000000..ef83fe1
--- /dev/null
+++ b/content/software/archive-site/index.md
@@ -0,0 +1,36 @@
++++
+title = "archive-site"
+description = "文章とソフトウェアを長く残すためのZolaサイト。"
+date = 2026-08-21
+include_in_feeds = false
+
+[extra]
+language = ["Zola", "Tera", "SCSS"]
+license = "MIT"
+source = "https://git.takashi-2521.com/archive-site/"
+github_mirror = "https://github.com/takashi-2521/archive-site"
+related_notes = [
+ "notes/static-sites-and-maintenance.md",
+]
++++
+
+## 概要
+
+NotesとSoftwareを中心に、文章と制作物を長期的に保存するための静的サイトです。
+
+![archive-siteの画面構成例](screenshot.svg)
+
+## 作った背景
+
+断片的なメモとソースコードの間にある、設計意図や調査の過程を一か所に残すために作りました。更新頻度が低くても自然に見える、索引としての情報構造を重視しています。
+
+## 開発の過程
+
+ZolaのSection、Page、Taxonomyをそのまま用い、独自のコンテンツ管理層を作らない方針にしました。表示はTera、スタイルはSCSS、テーマ切替だけを小さなJavaScriptで担当します。
+
+## 特徴
+
+- 年別のNotesアーカイブ
+- タグによる横断的な分類
+- NotesとSoftwareの明示的な関連付け
+- JavaScriptなしでも読める本文とナビゲーション
diff --git a/content/software/archive-site/screenshot.svg b/content/software/archive-site/screenshot.svg
new file mode 100644
index 0000000..18b05c5
--- /dev/null
+++ b/content/software/archive-site/screenshot.svg
@@ -0,0 +1,17 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 540" role="img" aria-labelledby="title desc">
+ <title id="title">archive-siteの画面構成例</title>
+ <desc id="desc">ヘッダー、Notes一覧、Software一覧を罫線で整理した画面。</desc>
+ <rect width="960" height="540" fill="#f6f5f1"/>
+ <rect x="72" y="54" width="816" height="46" fill="none" stroke="#d6d8da"/>
+ <rect x="72" y="144" width="500" height="28" fill="#202124"/>
+ <rect x="72" y="194" width="620" height="12" fill="#aeb2b5"/>
+ <rect x="72" y="246" width="816" height="1" fill="#d6d8da"/>
+ <rect x="72" y="280" width="104" height="18" fill="#315f82"/>
+ <rect x="72" y="326" width="816" height="1" fill="#d6d8da"/>
+ <rect x="72" y="348" width="104" height="12" fill="#aeb2b5"/>
+ <rect x="220" y="344" width="420" height="20" fill="#202124"/>
+ <rect x="72" y="392" width="816" height="1" fill="#d6d8da"/>
+ <rect x="72" y="414" width="104" height="12" fill="#aeb2b5"/>
+ <rect x="220" y="410" width="350" height="20" fill="#202124"/>
+ <rect x="72" y="458" width="816" height="1" fill="#d6d8da"/>
+</svg>
diff --git a/content/software/content-notes.md b/content/software/content-notes.md
new file mode 100644
index 0000000..0d5f6df
--- /dev/null
+++ b/content/software/content-notes.md
@@ -0,0 +1,23 @@
++++
+title = "content-notes"
+description = "Markdownだけで考えの断片を整理する小さな道具。"
+date = 2025-10-18
+include_in_feeds = false
+
+[extra]
+language = ["Rust"]
+license = "MIT"
+source = "https://git.takashi-2521.com/content-notes/"
++++
+
+## 概要
+
+短いメモに日付と題名を付け、単純なディレクトリへ保存するためのコマンドラインツールです。
+
+## 作った背景
+
+思いついたことを残す前に保存場所や形式で迷わないよう、操作を限定した道具が必要でした。
+
+## 開発の過程
+
+保存形式には可搬性の高いMarkdownを採用しました。検索や分類を内部データベースへ閉じ込めず、通常のファイル操作でも扱えることを優先しています。
diff --git a/doc/Design.md b/doc/Design.md
new file mode 100644
index 0000000..bddd04f
--- /dev/null
+++ b/doc/Design.md
@@ -0,0 +1,1851 @@
+# Design.md
+
+# たかしの記録 — Zola 基本・詳細設計書
+
+## 1. 文書目的
+
+本書は `Specifications.md` の要求を、Zolaによる静的Webサイトとして矛盾なく実装するための基本設計・詳細設計を定義する。
+
+設計上の基本原則は次のとおり。
+
+1. Zola公式のSection / Page / Taxonomy / Tera modelに従う
+2. Zola標準機能を優先する
+3. 外部dependencyを増やさない
+4. Build artifact `public/` のみで本番サイトを成立させる
+5. Client PCでbuildし、VPSでbuildしない
+6. 本番runtimeはCaddyのみ
+7. コンテンツ構造とデザインを不要に複雑化しない
+
+---
+
+## 2. 参照仕様とZola準拠方針
+
+本設計は `Specifications.md` を上位仕様とする。
+
+実装時は使用するZolaバージョンの公式ドキュメントを確認すること。
+設定キー・front matter・Tera関数を推測して追加してはならない。
+
+主に以下のZola概念を使用する。
+
+- `zola.toml`
+- Section (`_index.md`)
+- Page (`*.md`)
+- Taxonomy
+- Tera templates
+- Sass/SCSS compilation
+- Static assets
+- Colocated assets
+- Built-in feeds
+- Built-in sitemap
+- Built-in robots.txt
+- `page.toc`
+- `get_page`
+- `get_section`
+- `get_url`
+- `get_taxonomy_url`
+- Internal link `@/`
+
+Zolaはディレクトリ構造からSection/Pageを構成するため、独自CMS層やJSON indexを追加しない。
+
+---
+
+## 3. システム構成
+
+```text
+Client PC
+│
+├── Zola
+├── Git repository
+│ ├── zola.toml
+│ ├── content/
+│ ├── templates/
+│ ├── sass/
+│ └── static/
+│
+├── zola check
+└── zola build
+ │
+ ▼
+ public/
+ │
+ │ archive + SSH
+ ▼
+VPS
+└── /srv/www/takashi-2521.com/
+ ├── releases/<release-id>/
+ └── current -> releases/<release-id>/
+ │
+ ▼
+ Caddy
+```
+
+ZolaはVPSへインストールしない。
+
+---
+
+## 4. Zolaサイト構造
+
+```text
+website/
+├── zola.toml
+├── content/
+│ ├── _index.md
+│ ├── about.md
+│ ├── notes/
+│ │ ├── _index.md
+│ │ ├── example-note.md
+│ │ └── ...
+│ └── software/
+│ ├── _index.md
+│ ├── example-software.md
+│ └── ...
+├── templates/
+│ ├── base.html
+│ ├── index.html
+│ ├── page.html
+│ ├── notes/
+│ │ ├── section.html
+│ │ └── page.html
+│ ├── software/
+│ │ ├── section.html
+│ │ └── page.html
+│ ├── tags/
+│ │ ├── list.html
+│ │ └── single.html
+│ ├── partials/
+│ │ ├── header.html
+│ │ ├── footer.html
+│ │ ├── note-list-item.html
+│ │ ├── software-list-item.html
+│ │ ├── toc.html
+│ │ ├── related-notes.html
+│ │ └── related-software.html
+│ └── 404.html
+├── sass/
+│ ├── main.scss
+│ ├── _tokens.scss
+│ ├── _reset.scss
+│ ├── _base.scss
+│ ├── _typography.scss
+│ ├── _layout.scss
+│ ├── _header.scss
+│ ├── _footer.scss
+│ ├── _archive.scss
+│ ├── _article.scss
+│ ├── _software.scss
+│ ├── _taxonomy.scss
+│ ├── _code.scss
+│ ├── _theme.scss
+│ └── _utilities.scss
+├── static/
+│ ├── favicon.svg
+│ └── js/
+│ └── theme.js
+├── themes/
+└── public/ # generated; Git ignored
+```
+
+### 4.1 Themes
+
+`themes/` はZola標準ディレクトリとして存在してよいが、空のままとする。
+外部themeを使用しない。
+
+### 4.2 public
+
+`public/` は生成物でありGit管理対象外。
+
+---
+
+## 5. `zola.toml` 基本設計
+
+初期値の基準を以下とする。
+
+```toml
+base_url = "https://www.takashi-2521.com"
+title = "たかしの記録"
+description = "日々考えたこととか思ったことを記録します"
+default_language = "ja"
+
+output_dir = "public"
+compile_sass = true
+minify_html = false
+
+generate_feeds = true
+feed_filenames = ["atom.xml", "rss.xml"]
+
+generate_sitemap = true
+generate_robots_txt = true
+
+build_search_index = false
+
+taxonomies = [
+ { name = "tags", feed = false }
+]
+
+[markdown]
+render_emoji = false
+external_links_class = "external-link"
+external_links_target_blank = false
+external_links_no_follow = false
+external_links_no_referrer = false
+external_links_external = true
+smart_punctuation = false
+lazy_async_image = true
+bottom_footnotes = true
+insert_anchor_links = "heading"
+
+[markdown.highlighting]
+error_on_missing_language = true
+style = "class"
+light_theme = "github-light"
+dark_theme = "github-dark"
+
+[link_checker]
+internal_level = "error"
+external_level = "warn"
+
+[slugify]
+paths = "on"
+taxonomies = "on"
+anchors = "on"
+paths_keep_dates = false
+
+[extra]
+home_notes_limit = 5
+home_software_limit = 4
+```
+
+### 5.1 設定上の注意
+
+- `base_url` は本番URLと一致させる。
+- Searchは初期無効。
+- Taxonomyは`tags`のみ。
+- FeedはAtomとRSS。
+- Syntax highlightはclass方式とし、inline styleへの依存を避ける。
+- `external_links_class` を利用し外部リンク記号をCSSで表現する。
+- `minify_html` は開発中false。最終的にtrueへ変更する場合は生成結果を確認してから行う。
+- Highlight themeの具体設定は使用Zola版の公式仕様に合わせる。
+- 利用中Zola版で設定名・構造が異なる場合は、公式ドキュメントを優先してこの設計を等価に実現する。
+
+---
+
+## 6. Content model
+
+### 6.1 Root Section
+
+`content/_index.md`
+
+```toml
++++
+title = "たかしの記録"
+description = "日々考えたこととか思ったことを記録します"
++++
+```
+
+Home自体はZolaのroot sectionとして扱い、`templates/index.html` で描画する。
+
+### 6.2 About Page
+
+`content/about.md`
+
+```toml
++++
+title = "About"
+template = "page.html"
++++
+```
+
+本文に以下をMarkdownで記述する。
+
+- 名前
+- 一言プロフィール
+- サイトの目的
+- 関心分野
+- 連絡先
+
+構造化metadataを必要以上に増やさない。
+
+### 6.3 Notes Section
+
+`content/notes/_index.md`
+
+```toml
++++
+title = "Notes"
+sort_by = "date"
+template = "notes/section.html"
+page_template = "notes/page.html"
++++
+```
+
+#### 6.3.1 Notesのsort
+
+Sectionはdate順にする。
+テンプレートでは新しい順で表示されることを検証する。
+
+#### 6.3.2 Notes Page front matter
+
+```toml
++++
+title = "記事タイトル"
+description = "記事の短い説明"
+date = 2026-08-21
+updated = 2026-08-22
+
+[taxonomies]
+tags = ["情報工学", "AI"]
+
+[extra]
+related_software = [
+ "software/example-software.md"
+]
++++
+
+本文...
+```
+
+#### 6.3.3 Notes必須/任意項目
+
+| Field | 必須 | 用途 |
+|---|---:|---|
+| `title` | Yes | タイトル |
+| `date` | Yes | 公開日 |
+| `description` | No | 一覧・meta description |
+| `updated` | No | 更新日 |
+| `taxonomies.tags` | No | 一覧分類 |
+| `extra.related_software` | No | 明示的な関連Software |
+| Body | Yes | 本文 |
+
+#### 6.3.4 Slug
+
+Markdown filenameを英語ASCII slugとする。
+
+例:
+
+```text
+content/notes/llm-compute-and-energy.md
+```
+
+表示タイトルは日本語でよい。
+
+### 6.4 Software Section
+
+`content/software/_index.md`
+
+```toml
++++
+title = "Software"
+sort_by = "date"
+template = "software/section.html"
+page_template = "software/page.html"
++++
+```
+
+Software一覧の順序は初期状態ではdate順とする。
+公開日を表示する必要はないが、並び順制御に利用する。
+
+将来、並びを明示したくなった場合は`weight`へ変更可能。
+
+#### 6.4.1 Software Page front matter
+
+```toml
++++
+title = "example-software"
+description = "短い概要"
+date = 2026-08-21
+include_in_feeds = false
+
+[extra]
+language = ["Rust", "HTML"]
+license = "MIT"
+source = "https://git.takashi-2521.com/example-software/"
+github_mirror = "https://github.com/example/example-software"
+related_notes = [
+ "notes/example-note.md"
+]
++++
+
+## 概要
+
+...
+
+## 作った背景
+
+...
+
+## 開発の過程
+
+...
+```
+
+#### 6.4.2 Software項目
+
+| Field | 必須 | 用途 |
+|---|---:|---|
+| `title` | Yes | PJ名 |
+| `description` | No | 短い概要 |
+| `date` | Yes | 並び順 |
+| `include_in_feeds` | Yes (`false`) | Notes中心のsite feedからSoftwareを除外 |
+| `extra.language` | No | 使用言語 |
+| `extra.license` | No | ライセンス |
+| `extra.source` | Yes | cgit正本 |
+| `extra.github_mirror` | No | GitHub mirror |
+| `extra.related_notes` | No | 関連Note |
+| Body | Yes | 背景・過程・詳細 |
+
+#### 6.4.3 Software image
+
+画像をSoftwareに紐付ける場合、Zolaのcolocated assetを優先する。
+
+例:
+
+```text
+content/software/example-software/
+├── index.md
+└── screenshot.webp
+```
+
+この場合、Page URLは `/software/example-software/` とし、画像を同じPage Bundle内に置く。
+
+画像が不要なSoftwareは単一Markdownでよい。
+
+---
+
+## 7. Related content設計
+
+### 7.1 基本方針
+
+関連付けはタグから自動推測せず、front matterで明示する。
+
+理由:
+
+- 意図しない関連付けを防止
+- 記事数が少ないため手動管理コストが低い
+- Zola `get_page` で確実に参照できる
+
+### 7.2 Path形式
+
+`extra.related_software` / `extra.related_notes` には **contentディレクトリからのMarkdown path** を保存する。
+
+例:
+
+```toml
+related_software = ["software/ollama-web.md"]
+related_notes = ["notes/ollama-tool-calling.md"]
+```
+
+### 7.3 Tera resolution
+
+例:
+
+```tera
+{% for path in page.extra.related_notes %}
+ {% set related = get_page(path=path) %}
+ <a href="{{ related.permalink }}">{{ related.title }}</a>
+{% endfor %}
+```
+
+Missing referenceはbuild/checkで検出できるよう、原則 `allow_missing=true` を使用しない。
+
+---
+
+## 8. Template設計
+
+### 8.1 `base.html`
+
+責務:
+
+- HTML document skeleton
+- `<html lang="ja">`
+- `<head>`
+- charset / viewport
+- title
+- description
+- canonical
+- RSS/Atom autodiscovery
+- CSS
+- Zola生成syntax-highlight CSS (`giallo-light.css` / `giallo-dark.css`)
+- theme JS
+- Header
+- Main block
+- Footer
+
+概念構造:
+
+```tera
+<!doctype html>
+<html lang="ja">
+<head>
+ ...
+ <link rel="stylesheet" href="{{ get_url(path='main.css') }}">
+ <link id="syntax-light" rel="stylesheet" href="{{ get_url(path='giallo-light.css') }}" media="(prefers-color-scheme: light)">
+ <link id="syntax-dark" rel="stylesheet" href="{{ get_url(path='giallo-dark.css') }}" media="(prefers-color-scheme: dark)">
+ <script src="{{ get_url(path='js/theme.js') }}" defer></script>
+</head>
+<body>
+ {% include "partials/header.html" %}
+ <main id="main-content">
+ {% block content %}{% endblock content %}
+ </main>
+ {% include "partials/footer.html" %}
+</body>
+</html>
+```
+
+#### 8.1.1 Page title
+
+原則:
+
+- Home: `たかしの記録`
+- Other: `<Page title> | たかしの記録`
+
+Tera blockまたは共通変数で実現する。
+
+#### 8.1.2 meta description
+
+優先順位:
+
+1. `page.description` / `section.description`
+2. `config.description`
+
+### 8.2 Header
+
+`partials/header.html`
+
+Desktop:
+
+```text
+たかしの記録 Home Notes Software About [Theme]
+```
+
+Mobileでは自然にwrapさせ、初期実装ではHamburger menuを導入しない。
+
+サイト名自体はHomeへのリンク。
+
+Navigationには`aria-label="メインナビゲーション"`を設定する。
+
+### 8.3 Footer
+
+`partials/footer.html`
+
+表示:
+
+- `© <year> Takashi`
+- RSS
+- Mail
+
+Mail addressは設定値に直接埋め込むか、`config.extra`へ置く。
+公開時のspam対策を別途必要と判断した場合のみ変更する。
+
+### 8.4 Home Template
+
+`templates/index.html`
+
+処理:
+
+1. root `section`
+2. `get_section(path="notes/_index.md")`
+3. Notesから上位5件取得
+4. `get_section(path="software/_index.md")`
+5. Softwareから上位4件取得
+6. Aboutへのリンク
+
+概念:
+
+```tera
+{% set notes = get_section(path="notes/_index.md") %}
+{% set software = get_section(path="software/_index.md") %}
+```
+
+件数は`config.extra.home_notes_limit` / `home_software_limit`で管理する。
+
+Homeでタグは必須表示としない。
+
+### 8.5 Notes一覧 Template
+
+`templates/notes/section.html`
+
+#### 8.5.1 年別グルーピング
+
+Zola公式Archive patternに従い、`section.pages | group_by(attribute="year")` を利用する。
+
+表示:
+
+```text
+Notes
+
+2026
+ 08-21 タイトル
+ タグ1 / タグ2
+
+2025
+ ...
+```
+
+ZolaのSection `sort_by = "date"` を前提とする。
+
+#### 8.5.2 Note list item
+
+`partials/note-list-item.html`
+
+- `<article>` または適切なlist item
+- `<time datetime="">`
+- title link
+- tags
+- optional description
+
+タグURLは `get_taxonomy_url(kind="tags", term=...)` を利用する。
+
+### 8.6 Note detail Template
+
+`templates/notes/page.html`
+
+構造:
+
+```text
+article
+├── header
+│ ├── h1
+│ ├── published
+│ └── updated (optional)
+├── TOC
+├── body
+├── related software (optional)
+└── article footer
+```
+
+#### 8.6.1 Tags
+
+仕様に従い、本文詳細ではタグ一覧を主要表示しない。
+
+#### 8.6.2 TOC
+
+`page.toc` を使用する。
+
+- `page.toc` が空なら非表示
+- Sidebar化しない
+- 記事headerと本文の間に置く
+- H2/H3等の階層を維持
+- Recursive Tera partial/componentで描画可能
+
+#### 8.6.3 TOC accessibility
+
+- `<nav aria-label="目次">`
+- 見出し「目次」
+- ordered/unordered list
+- Anchorは`header.permalink`
+
+#### 8.6.4 Date
+
+公開:
+
+```html
+<time datetime="2026-08-21">2026-08-21</time>
+```
+
+更新日がある場合だけ表示。
+
+#### 8.6.5 Footnotes
+
+Zola Markdown rendererが生成するfootnote markupへSCSSを適用する。
+
+独自脚注システムを作らない。
+
+#### 8.6.6 References
+
+Markdown本文の通常Headingとして「参考文献」を記述する。
+
+例:
+
+```markdown
+## 参考文献
+
+1. ...
+2. ...
+```
+
+専用front matter schemaは導入しない。
+
+### 8.7 Software一覧 Template
+
+`templates/software/section.html`
+
+表示の中心はSoftware名。
+
+例:
+
+```text
+Software
+
+ollama-web
+短い説明(存在する場合)
+
+winagent-sandbox-mcp
+...
+```
+
+一覧には以下を表示しない。
+
+- screenshot
+- license
+- source URL全文
+- language一覧
+- 大量のbadge
+
+### 8.8 Software detail Template
+
+`templates/software/page.html`
+
+構造:
+
+```text
+article
+├── h1 Software名
+├── description
+├── metadata
+│ ├── 使用言語
+│ ├── ライセンス
+│ ├── Source
+│ └── GitHub mirror (optional)
+├── Body
+│ ├── 概要
+│ ├── 背景
+│ └── 過程等
+├── screenshot/assets (optional)
+└── Related Notes (optional)
+```
+
+SourceはGitHubより先に表示。
+
+外部リンクclassによって`↗`表示を適用する。
+
+### 8.9 About
+
+標準`page.html`で十分な場合は専用templateを作らない。
+
+`page.html` は汎用Editorial pageとして以下を描画。
+
+- h1
+- page.content
+
+About固有レイアウトが必要になった時だけ`about.html`を追加する。
+
+### 8.10 Taxonomy templates
+
+`tags/list.html`
+
+- 全タグの一覧
+- term name
+- article count
+
+`tags/single.html`
+
+- タグ名
+- 該当Pages一覧
+- Note/Softwareの混在が読みづらい場合は種類ごとに分離可能
+
+初期仕様ではタグは主にNotes用だが、Zola taxonomy自体はPages全般で利用可能。
+
+### 8.11 404
+
+`templates/404.html`
+
+- base designと同じHeader/Footer
+- 「ページが見つかりません」
+- Homeへのリンク
+- Notesへのリンク
+- Softwareへのリンク
+
+外部依存なし。
+
+---
+
+## 9. Internal Link設計
+
+Markdown本文内の内部リンクはZolaの `@/` syntaxを使用する。
+
+例:
+
+```markdown
+[関連する記事](@/notes/example-note.md)
+```
+
+Template内は`get_url`, `get_page`, `get_section`を使用する。
+
+Content pathを手書きURLへ変換しない。
+
+Broken internal linkは`zola check`でerror扱い。
+
+---
+
+## 10. Tags / Taxonomy設計
+
+`zola.toml`:
+
+```toml
+taxonomies = [
+ { name = "tags", feed = false }
+]
+```
+
+Page:
+
+```toml
+[taxonomies]
+tags = ["情報工学", "地球環境"]
+```
+
+### 10.1 Tag URL
+
+Zola slugificationに任せる。
+
+表示termは日本語を許容する。
+Taxonomy URL生成はZola APIを使用し、自前slug生成をしない。
+
+### 10.2 Category
+
+初期実装ではcategory taxonomyを作らない。
+
+---
+
+## 11. Feed設計
+
+Site-level feed:
+
+- `/atom.xml`
+- `/rss.xml`
+
+Site-level feedを使用する。Software Pageには `include_in_feeds = false` を設定し、日付を持つSoftwareがsite feedへ混入しないようにする。これにより初期feedは実質的にNotesを購読対象とする。
+
+Notes section固有feedは初期実装では生成しない。必要になった場合のみ `content/notes/_index.md` の `generate_feeds = true` を追加する。
+
+`base.html`にautodiscoveryを設定。
+
+概念:
+
+```tera
+<link rel="alternate"
+ type="application/atom+xml"
+ title="Atom"
+ href="{{ get_url(path='atom.xml', trailing_slash=false) }}">
+<link rel="alternate"
+ type="application/rss+xml"
+ title="RSS"
+ href="{{ get_url(path='rss.xml', trailing_slash=false) }}">
+```
+
+FooterのRSSリンクは`rss.xml`を優先してよい。
+Atomもアクセス可能とする。
+
+---
+
+## 12. Sitemap / robots
+
+Zola built-in生成を利用する。
+
+```toml
+generate_sitemap = true
+generate_robots_txt = true
+```
+
+独自generatorを作らない。
+
+robots policyを変更する必要が生じた場合のみbuilt-in template overrideまたはstatic fileを検討する。
+
+---
+
+## 13. Sass設計
+
+### 13.1 基本原則
+
+- 自前SCSS
+- CSS frameworkなし
+- Tailwindなし
+- external reset packageなし
+- BEM等の厳格な命名体系を強制しない
+- component responsibilityが分かる程度のclass設計
+
+### 13.2 Design tokens
+
+`_tokens.scss`
+
+初期候補:
+
+```scss
+:root {
+ --color-bg: #f6f5f1;
+ --color-surface: #fbfaf7;
+ --color-text: #202124;
+ --color-muted: #6d7177;
+ --color-border: #d6d8da;
+ --color-link: #315f82;
+ --color-link-hover: #244a67;
+
+ --font-sans:
+ system-ui, -apple-system, BlinkMacSystemFont,
+ "Segoe UI", "Noto Sans JP", sans-serif;
+
+ --font-mono:
+ ui-monospace, "Cascadia Code", "SFMono-Regular",
+ Consolas, "Liberation Mono", monospace;
+
+ --content-max: 72ch;
+ --site-max: 72rem;
+}
+```
+
+具体色は実装時に視認性を確認して調整可能。
+Accentは青系から逸脱しない。
+
+### 13.3 Dark tokens
+
+```scss
+@media (prefers-color-scheme: dark) {
+ :root:not([data-theme="light"]) {
+ --color-bg: #17191b;
+ --color-surface: #1d2022;
+ --color-text: #e9e9e6;
+ --color-muted: #a0a3a6;
+ --color-border: #363a3e;
+ --color-link: #86b6d8;
+ --color-link-hover: #add3ed;
+ }
+}
+```
+
+User override用`data-theme`も定義する。
+
+---
+
+## 14. Theme switch設計
+
+### 14.1 初期状態
+
+CSS `prefers-color-scheme` を優先。
+
+### 14.2 手動切替
+
+`static/js/theme.js` のみを使用。
+
+責務:
+
+- 現在theme取得
+- light/dark切替
+- `document.documentElement.dataset.theme`
+- `localStorage`保存
+- toggle buttonの`aria-pressed`/label更新
+- `#syntax-light` / `#syntax-dark` の `media` を切り替え、本文themeとsyntax highlighting themeを同期
+
+明示的にLightを選んだ場合:
+
+- `syntax-light.media = "all"`
+- `syntax-dark.media = "not all"`
+
+明示的にDarkを選んだ場合:
+
+- `syntax-light.media = "not all"`
+- `syntax-dark.media = "all"`
+
+保存themeがない場合はHTML/CSSとも `prefers-color-scheme` に従う。
+
+### 14.3 CSP
+
+JSは外部fileのみ。
+Inline scriptは使用しない。
+
+Theme JSが失敗してもOS themeにより読みやすい状態を保つ。
+
+### 14.4 No-JS
+
+No-JSではmanual toggleだけ利用不可。
+本文・navigation・Light/Dark OS preferenceは利用可能。
+
+---
+
+## 15. External link表示
+
+Zola Markdown設定:
+
+```toml
+external_links_class = "external-link"
+```
+
+SCSS:
+
+```scss
+.external-link::after {
+ content: " ↗";
+ font-size: 0.8em;
+ text-decoration: none;
+}
+```
+
+装飾SVG dependencyを使用しない。
+
+Navigationや内部リンクには付けない。
+
+---
+
+## 16. Layout設計
+
+### 16.1 Site container
+
+```scss
+.site-container {
+ width: min(100% - 2rem, var(--site-max));
+ margin-inline: auto;
+}
+```
+
+### 16.2 Article
+
+初期値:
+
+```scss
+.prose {
+ max-width: var(--content-max); // 72ch
+ margin-inline: auto;
+}
+```
+
+仕様で本文幅は未確定だったため、初期実装は **72ch** とする。
+実際の日本語可読性を確認し、65〜75ch相当の範囲で調整可能。
+
+### 16.3 Mobile
+
+- Side padding最低1rem
+- Header navigation wrap可
+- Horizontal body scroll禁止
+- pre/code/tableは必要に応じ内部scroll
+- 画像max-width:100%
+
+---
+
+## 17. Typography設計
+
+### 17.1 Body
+
+Sans-serif system stack。
+
+```scss
+body {
+ font-family: var(--font-sans);
+ line-height: 1.75;
+}
+```
+
+### 17.2 Heading
+
+Sans-serif。
+過度な巨大見出しにしない。
+Home H1だけやや大きくする。
+
+### 17.3 Metadata
+
+Monospaceまたは小さめSans。
+日付などにMonoを利用。
+
+### 17.4 Code
+
+`var(--font-mono)`。
+
+---
+
+## 18. Editorial visual rules
+
+- Border radius最小限
+- Card shadow原則なし
+- Gradientなし
+- Neon/glowなし
+- Glassmorphismなし
+- 1px ruleを主要区切りに使用
+- Section間余白でhierarchyを作る
+- 大きなHero画像なし
+- Iconsは最小限
+- Badge乱用なし
+
+---
+
+## 19. Code block設計
+
+Zola syntax highlightingを利用。
+
+- Client-side highlighting libraryなし
+- Prism/highlight.jsなし
+- class-based highlight
+- `github-light` / `github-dark` のdual theme
+- Zola生成 `giallo-light.css` / `giallo-dark.css` を読み込む
+- theme toggle時にstylesheet mediaも同期する
+- code blockは横scroll可
+- light/darkの視認性を確認
+
+Highlight CSSの生成/適用方法は使用Zola versionの公式仕様に従う。
+
+---
+
+## 20. Image設計
+
+### 20.1 Notes
+
+Markdown imageを使用。
+
+`lazy_async_image = true` により遅延loadを利用する。
+
+Altはplain textで記述。
+
+### 20.2 Software
+
+Colocated assetを推奨。
+
+Screenshotは本文内で説明と一緒に表示。
+
+### 20.3 Formats
+
+原則:
+
+- SVG: icon/diagram
+- WebP/AVIF/JPEG/PNG: screenshot用途に応じて選択
+
+画像最適化toolchainは初期実装に追加しない。
+
+---
+
+## 21. Accessibility設計
+
+### 21.1 Landmarks
+
+- `<header>`
+- `<nav>`
+- `<main>`
+- `<article>`
+- `<footer>`
+
+### 21.2 Skip link
+
+Header先頭にskip linkを設けることを推奨。
+
+```html
+<a class="skip-link" href="#main-content">本文へ移動</a>
+```
+
+### 21.3 Focus
+
+`:focus-visible`を明確に表示。
+
+### 21.4 Color
+
+リンクは青色だけでなく下線でも識別可能にする。
+
+### 21.5 Theme toggle
+
+Button要素を使用。
+Keyboard操作可能。
+
+### 21.6 TOC
+
+`nav aria-label="目次"`。
+
+---
+
+## 22. SEO / metadata設計
+
+最低限:
+
+- `<title>`
+- `<meta name="description">`
+- canonical URL
+- `lang="ja"`
+- RSS/Atom autodiscovery
+- sitemap
+- robots
+
+Open Graph / Twitter Cardは初期必須ではない。
+外部依存なしで容易に実装可能な場合は後で追加可。
+
+---
+
+## 23. SNS共有
+
+初期実装対象外。
+
+理由:
+
+- 仕様が条件付き
+- Editorial designを優先
+- Mastodon等は単一固定share endpointにしづらい
+- 外部service/仕様変化への依存を避ける
+
+将来追加する場合、以下の順で検討。
+
+1. Web Share API
+2. Copy link
+3. 外部dependencyなしのSNS share URL
+
+---
+
+## 24. Comments
+
+初期実装対象外。
+
+Template上に空のcomment UIや未使用placeholderを作らない。
+
+将来commentsを採用する場合は別設計として以下を再評価。
+
+- Data persistence
+- Authentication/abuse
+- Spam
+- Moderation
+- Privacy
+- CSP
+- Backup
+- VPS resource
+- External service dependency
+
+---
+
+## 25. Search
+
+初期:
+
+```toml
+build_search_index = false
+```
+
+Search UIを作らない。
+
+将来追加時はZola built-in search indexを第一候補とし、日本語indexing対応について使用Zola binaryの機能を確認する。
+
+---
+
+## 26. Git設計
+
+Git管理対象:
+
+- `zola.toml`
+- `content/`
+- `templates/`
+- `sass/`
+- `static/`
+- documentation
+- deploy script(別Phaseで追加)
+
+`.gitignore`:
+
+```gitignore
+/public/
+```
+
+Editor/OS生成物は必要に応じ追加。
+
+`themes/` が空でGitに残らない場合、`.gitkeep`は必須ではない。
+
+---
+
+## 27. Build設計
+
+### 27.1 Development
+
+```bash
+zola serve
+```
+
+### 27.2 Validation
+
+```bash
+zola check
+```
+
+### 27.3 Production build
+
+```bash
+zola build
+```
+
+Build成功後、`public/`が自己完結していること。
+
+### 27.4 Build Gate
+
+以下のいずれかで失敗したらDeploy不可。
+
+- `zola check` failure
+- `zola build` failure
+- broken internal link
+- required related content path missing
+- missing required template
+- missing syntax language when error_on_missing_language=true
+
+---
+
+## 28. Deployment interface
+
+Deployment詳細は別Phaseだが、Client siteは以下の契約を満たす。
+
+Input:
+
+```text
+source repository
+```
+
+Build output:
+
+```text
+public/
+```
+
+Deploy sideは`public/`の中身をrelease rootへ展開する。
+
+最終URL rootに余計な`public/` directory layerを作らない。
+
+例:
+
+```text
+release/
+├── index.html
+├── notes/
+├── software/
+├── tags/
+├── main.css
+└── ...
+```
+
+---
+
+## 29. Caddy互換性
+
+既存Caddy:
+
+```text
+root * /srv/www/takashi-2521.com/current
+file_server
+```
+
+Zola outputはCaddy static file serverだけで利用できること。
+
+以下を要求しない。
+
+- rewrite application router
+- server-side include
+- PHP
+- CGI
+- Node
+- Python backend
+
+404 handlingがCaddyで専用設定を必要とする場合は、Deploy統合Phaseで既存Caddy設定との整合を確認する。
+
+---
+
+## 30. CSP compatibility
+
+目標CSP:
+
+```text
+default-src 'self';
+script-src 'self';
+style-src 'self';
+img-src 'self' data:;
+font-src 'self';
+object-src 'none';
+base-uri 'self';
+frame-ancestors 'none';
+```
+
+実装規則:
+
+- Inline JSなし
+- Inline event handlerなし
+- External fontなし
+- External CSSなし
+- External JSなし
+- Evalなし
+- CSS assetはself
+- imageはself/data中心
+
+Syntax highlightがinline styleを生成しないclass modeを優先。
+
+---
+
+## 31. Error handling
+
+Static siteのためruntime error UIは不要。
+
+Build-timeに可能な限り失敗を検知する。
+
+- Broken internal links: error
+- Related page missing: Tera error
+- Missing templates: build error
+- Invalid front matter: build error
+
+External linksは一時障害を考慮し`warn`を基本とする。
+
+---
+
+## 32. Content authoring rules
+
+### 32.1 Notes
+
+- filename: lowercase ASCII kebab-case
+- `date`必須
+- title日本語可
+- tags任意
+- headingsはH2以降を本文主階層に使用
+- H1はtemplate titleに委ねる
+- referencesは本文末尾H2
+
+### 32.2 Software
+
+- filename: software slug
+- cgit source必須
+- GitHubはmirrorとしてoptional
+- Overview/background/processはMarkdown本文
+- screenshotは必要時のみ
+
+### 32.3 Internal references
+
+`@/` linkまたはfront matter pathを使用。
+公開URLを内部参照用にhard-codeしない。
+
+---
+
+## 33. Date display
+
+内部データはISO date。
+
+表示は初期実装で:
+
+```text
+2026-08-21
+```
+
+とする。
+
+将来日本語表記`2026年8月21日`へ変更してもcontent metadataは変えない。
+
+---
+
+## 34. Home content algorithm
+
+Pseudo:
+
+```text
+notes = get_section(notes)
+software = get_section(software)
+
+recent_notes = first 5 notes
+recent_software = first 4 software
+
+render:
+ site intro
+ recent notes
+ all notes link
+ software
+ all software link
+ about short text
+```
+
+Homeは最新更新日を強く演出せず、`最近のNotes`と表記する。
+
+---
+
+## 35. Archive algorithm
+
+Zola公式archive patternを使用。
+
+Pseudo Tera:
+
+```tera
+{% set pages_by_year = section.pages | group_by(attribute="year") %}
+{% for year, pages in pages_by_year %}
+ <section>
+ <h2>{{ year }}</h2>
+ {% for page in pages %}
+ ...
+ {% endfor %}
+ </section>
+{% endfor %}
+```
+
+使用Zola/Tera版でyear orderが期待通りでない場合、公式Archive例のreverse処理を採用する。
+
+---
+
+## 36. TOC recursive rendering
+
+`page.toc`の`children`を階層描画する。
+
+概念:
+
+```text
+toc(headers):
+ ul
+ foreach header
+ li
+ a header.permalink / title
+ if children
+ toc(children)
+```
+
+使用Zola版がTera componentを推奨する場合はcomponentを使用する。
+古いshortcode設計を新規導入しない。
+
+---
+
+## 37. Dark mode persistence
+
+`theme.js` concept:
+
+```text
+on load:
+ stored = localStorage.getItem("theme")
+ if stored light/dark:
+ set data-theme
+
+on toggle:
+ determine current
+ toggle
+ store
+ update accessible label
+```
+
+System preferenceはCSS側で処理する。
+
+localStorageが利用不可でもerrorで本文表示を妨げない。
+
+---
+
+## 38. Favicon
+
+初期faviconは自作SVG 1ファイル。
+
+モチーフは単純な文字または記号。
+
+例候補:
+
+- 「記」
+- `T`
+- 小さな本/ノートを抽象化した幾何形状
+
+初期実装では複雑なfavicon pipelineを作らない。
+
+---
+
+## 39. 404 design
+
+通常Pageと同じwidth/token/themeを使用。
+
+内容:
+
+```text
+404
+ページが見つかりません。
+
+Home
+Notes
+Software
+```
+
+Animationやillustration不要。
+
+---
+
+## 40. Testing / Validation checklist
+
+### 40.1 Zola
+
+- [ ] `zola check`
+- [ ] `zola build`
+- [ ] `/public/index.html`
+- [ ] `/public/notes/index.html`
+- [ ] `/public/software/index.html`
+- [ ] `/public/about/index.html`
+- [ ] `/public/404.html`
+- [ ] `/public/atom.xml`
+- [ ] `/public/rss.xml`
+- [ ] `/public/giallo-light.css`
+- [ ] `/public/giallo-dark.css`
+- [ ] sitemap
+- [ ] robots
+
+### 40.2 Content
+
+- [ ] Note 2件以上でyear grouping確認
+- [ ] 複数年でarchive確認
+- [ ] tag link確認
+- [ ] updatedあり/なし確認
+- [ ] TOCあり/なし確認
+- [ ] footnote確認
+- [ ] references style確認
+- [ ] related software確認
+- [ ] related note確認
+- [ ] Software GitHub mirrorあり/なし確認
+- [ ] Screenshotあり/なし確認
+
+### 40.3 Responsive
+
+- [ ] Desktop
+- [ ] Narrow desktop
+- [ ] Tablet
+- [ ] Mobile
+- [ ] Long title
+- [ ] Long URL
+- [ ] Code block
+- [ ] Table
+- [ ] Image
+
+### 40.4 Accessibility
+
+- [ ] Keyboard only
+- [ ] Focus visible
+- [ ] Skip link
+- [ ] Heading hierarchy
+- [ ] Theme toggle keyboard
+- [ ] Link underline
+- [ ] Dark contrast
+- [ ] Light contrast
+
+### 40.5 Security
+
+- [ ] no external JS
+- [ ] no external CSS
+- [ ] no npm
+- [ ] no Node
+- [ ] no inline event handler
+- [ ] no eval
+- [ ] CSP-compatible
+- [ ] no tracking script
+
+---
+
+## 41. Traceability / 仕様整合性確認
+
+| Specifications要求 | Design対応 | 状態 |
+|---|---|---|
+| Notes + Software中心 | Sectionsとして分離 | OK |
+| Home index | `index.html`で5 Notes / 4 Software | OK |
+| 年別Notes | Zola archive `group_by(year)` | OK |
+| tagsのみ | `taxonomies = [{name="tags"}]` | OK |
+| Note Sans/余白 | Typography/Layout | OK |
+| Note TOC常時 | `page.toc` inline nav | OK |
+| Footnotes | Zola Markdown標準 | OK |
+| References | Markdown H2 +共通SCSS | OK |
+| 公開日/更新日 | date/updated | OK |
+| Software一覧はPJ名中心 | software/section | OK |
+| Software背景・過程 | Markdown body | OK |
+| 言語/License | `extra` | OK |
+| cgit正本 | `extra.source`必須 | OK |
+| GitHub mirror | optional extra | OK |
+| Related Notes/Software | explicit content path + `get_page` | OK |
+| About | `about.md` | OK |
+| Header小型 | partial/header | OK |
+| Source navなし | Headerには含めない | OK |
+| 日本語のみ | `default_language="ja"` | OK |
+| Sans + Mono | SCSS token | OK |
+| 青accent | color token | OK |
+| Dark mode | CSS + minimal JS | OK |
+| Sidebarなし | article inline layout | OK |
+| タグは一覧中心 | Note detailで主要表示しない | OK |
+| RSS + Atom | built-in feeds | OK |
+| Search将来 | initial false | OK |
+| Comments条件付き | initial scope外 | OK |
+| SNS条件付き | initial scope外 | OK |
+| Analyticsなし | scriptなし | OK |
+| Images最小限 | colocated assets optional | OK |
+| No animation | motionなし | OK |
+| Blue links | SCSS | OK |
+| External marker | `external_links_class` + CSS | OK |
+| ASCII slug | authoring rules | OK |
+| Caddy static | public artifact only | OK |
+| npm/Nodeなし | no dependency toolchain | OK |
+
+---
+
+## 42. 仕様上の未確定事項に対する設計判断
+
+### 42.1 本文最大幅
+
+仕様では後決めだったため、初期値 **72ch** を採用。
+日本語本文で広すぎる場合は65〜75ch相当の範囲で調整する。
+
+### 42.2 青の具体色
+
+初期候補として落ち着いた`#315f82`系を採用。
+WCAG contrast確認後に調整可。
+
+### 42.3 Home件数
+
+仕様上「おまかせ」だったため以下を採用。
+
+- Notes 5
+- Software 4
+
+### 42.4 favicon
+
+自作単純SVGとし、外部assetを導入しない。
+最終モチーフはvisual tuning時に決定可能。
+
+### 42.5 Comments
+
+静的構成との境界が未確定のため、初期実装対象外。
+
+### 42.6 SNS共有
+
+「簡単に対応できれば」という条件付き要件のため、初期実装対象外。
+外部dependencyなしで必要性が確認できた時点で追加する。
+
+---
+
+## 43. 実装Phase
+
+### Phase C1 — Foundation
+
+- Zola version確認
+- `zola.toml`
+- content structure
+- base template
+- SCSS entry
+- `zola check/build`
+
+PASS:
+- Build成功
+- Home生成
+
+### Phase C2 — Information Architecture
+
+- Notes Section
+- Software Section
+- About
+- Tags taxonomy
+- URLs
+
+PASS:
+- `/notes/`, `/software/`, `/about/`, `/tags/`
+
+### Phase C3 — Editorial UI
+
+- Header/Footer
+- typography
+- layout
+- light/dark
+- links
+- responsive
+
+PASS:
+- Desktop/Mobile
+- no external dependencies
+
+### Phase C4 — Notes
+
+- year archive
+- tags in list
+- note dates
+- TOC
+- footnotes
+- references
+
+PASS:
+- 複数年sampleで表示確認
+
+### Phase C5 — Software
+
+- software list
+- detail metadata
+- source/mirror
+- related notes
+- optional images
+
+PASS:
+- Source優先表示
+- related page resolution
+
+### Phase C6 — Static Features
+
+- feeds
+- sitemap
+- robots
+- 404
+- syntax highlighting
+- favicon
+
+PASS:
+- expected artifacts生成
+
+### Phase C7 — Final Validation
+
+- `zola check`
+- `zola build`
+- link checks
+- accessibility
+- responsive
+- CSP compatibility
+- Git status
+
+PASS:
+- Specifications受入条件を全件満たす
+
+---
+
+## 44. 実装禁止事項
+
+Codex等の実装エージェントは以下を勝手に追加しない。
+
+- npm
+- Node.js
+- React
+- Vue
+- Svelte
+- htmx
+- Tailwind
+- Bootstrap
+- external CSS framework
+- external icon framework
+- external JS CDN
+- Google Fonts等external font
+- client-side syntax highlighter
+- analytics
+- comments
+- search
+- server backend
+- database
+- Docker
+- unnecessary build script
+- external Zola theme
+- animations
+
+必要が生じた場合は実装前に要求変更として報告する。
+
+---
+
+## 45. 完了判定
+
+Design実装完了時に以下を報告する。
+
+1. 使用Zola version
+2. 実装したdirectory tree
+3. `zola check`結果
+4. `zola build`結果
+5. 生成ファイル確認
+6. 主要URL一覧
+7. external dependencyがないこと
+8. Specifications traceability結果
+9. 未実装項目
+10. 仕様からの逸脱の有無
+
+仕様から逸脱した場合は、理由・影響・代替案を明記する。
+
+---
+
+## 46. 公式Zola仕様との整合上の注意
+
+- `zola.toml` を正式設定ファイルとして使用する。
+- `content/notes/_index.md` と `content/software/_index.md` をSection定義とする。
+- `about.md` はPageとして扱う。
+- Homepageはroot Sectionとして`templates/index.html`を使用する。
+- Notes/SoftwareはSectionの`template` / `page_template`で専用Templateへ接続する。
+- ZolaのTaxonomyで`tags`を実装する。
+- 年別ArchiveはZola公式例と同じ`group_by(attribute="year")`系の実装を使う。
+- 関連Pageは`get_page(path=...)`で解決する。
+- Markdown本文内の内部リンクは`@/`を優先する。
+- Feed、sitemap、robotsはZola built-inを優先する。
+- `static/`はas-is copy、`sass/`はZola compile対象として扱う。
+- Page/SectionのTera contextを使用し、独自content indexを作らない。
+- 利用Zola版でTera Componentsが利用可能・推奨なら、TOC等の再利用可能UIに採用してよい。
+- 将来Searchを有効化する際、日本語検索index対応はZola binary build featureの確認を必須とする。
diff --git a/doc/Specifications.md b/doc/Specifications.md
index d2e9157..9f9c60f 100644
--- a/doc/Specifications.md
+++ b/doc/Specifications.md
@@ -1,755 +1,720 @@
-はい。公式ドキュメントに沿って見直すと、前回の要求仕様にはいくつか **Zola固有の構成・命名に寄せた方がよい点**があります。
+# Specifications.md
-特に重要なのは、Zolaは公式に「サイト構造を決めつけない」としており、基本は `content/` 配下の **Section (`_index.md`) と Page (`*.md`)**、そして `template` / `page_template` でテンプレートを結びつける構造です。公式の初期ディレクトリも `zola.toml / content / sass / static / templates / themes` です。 ([Zola][1])
+# たかしの記録 — 要求仕様書
-その前提で、Codex向け仕様は次のように修正するのがよいです。
+## 1. 文書目的
-````markdown
-# Zola Web Site Client Implementation Requirements
+本書は、個人Webサイト「たかしの記録」の利用者視点・コンテンツ視点での要求仕様を定義する。
-## 1. 目的
-
-自前VPS上で Caddy により配信する個人Webサイトを Zola で構築する。
-
-本番VPSでは動的Webアプリケーションを実行せず、クライアントPC上で Zola により静的HTML/CSS/JSを生成し、生成済み成果物のみをVPSへデプロイする。
-
-npm / Node.js に依存しない構成とし、Webサイト構築に伴うサプライチェーン攻撃面を最小化する。
+本書では「何を提供するサイトか」「誰がどのように読むか」「どの情報をどのように見せるか」を定義し、具体的なZola/Tera/SCSS実装方法は `Design.md` に委ねる。
---
-# 2. Zola採用方針
-
-Zola公式ドキュメントの構造・概念・標準機能に従うこと。
-
-Zolaは以下を基本概念として使用する。
-
-- Site configuration: `zola.toml`
-- Content
- - Section: `_index.md`
- - Page: 通常のMarkdownファイル
-- Templates: Tera
-- Sass: `sass/`
-- Static assets: `static/`
-- Themes: `themes/`
-- Build output: `public/`
+## 2. サイト概要
-独自のディレクトリ規約や別SSG由来の設計を持ち込まず、Zola標準のSection/Pageモデルを優先する。
+### 2.1 サイト名
----
+**たかしの記録**
-# 3. 必須技術構成
-
-| 領域 | 採用 |
-|---|---|
-| Static Site Generator | Zola |
-| Configuration | `zola.toml` |
-| Content | CommonMark Markdown |
-| Content model | Zola Section / Page |
-| Template engine | Tera |
-| Style | Sass / SCSS |
-| JavaScript | 必要最小限の vanilla JavaScript |
-| Backend | なし |
-| Database | なし |
-| npm / Node.js | 使用しない |
-| htmx | 使用しない |
-| External CDN | 原則使用しない |
-| Production Web server | Caddy |
+### 2.2 サイトの目的
----
+本サイトは、運営者が日頃考えたこと・感じたこと・調べたことを記録し、作成したソフトウェアとともに長期的に保存・公開する個人アーカイブである。
-# 4. Zola標準ディレクトリ構造
+主な用途は次のとおり。
-`zola init` が生成する構造を基準とする。
+- 日々考えたことや感じたことの記録
+- 読書を通じて考えたことの記録
+- 技術調査・技術メモの公開
+- 長文記事・考察の公開
+- 作成したソフトウェアの紹介
+- ソフトウェア制作の背景・過程・設計意図の記録
+- ソフトウェアに関連する技術記事との相互参照
-```text
-website/
-├── zola.toml
-├── content/
-├── sass/
-├── static/
-├── templates/
-└── themes/
-````
+### 2.3 主な対象分野
-build後には以下が生成される。
+Notesでは特定分野に限定せず、少なくとも以下を扱う。
-```text
-public/
-```
+- 情報工学・ソフトウェア
+- 論理学
+- マルクス・資本論・政治経済
+- 地球環境
+- 読書を通じた考察
+- 上記にまたがる複合的なテーマ
-`public/` は生成物でありGit管理しない。
+分類は固定カテゴリではなくタグを基本とし、分野横断の記事を許容する。
-`themes/` はZola標準ディレクトリとして保持してよいが、本プロジェクトでは外部themeを原則使用しない。
+### 2.4 想定読者
----
+- 運営者本人
+- ソフトウェアエンジニア
+- 技術分野に関心のある一般読者
+- 学生
+- 個々の記事テーマに関心のある一般人
-# 5. 初期サイト構造
+専門読者だけに限定せず、記事冒頭で主題を把握できることを重視する。
-以下のコンテンツ領域を実装する。
+### 2.5 更新頻度
-```text
-/
-├── about/
-├── projects/
-└── notes/
-```
+通常は **2〜3か月に1〜2回程度** を想定する。
-想定URL:
+更新頻度を競うニュースサイトやSNSではなく、長期間残るアーカイブとして設計する。
-```text
-https://www.takashi-2521.com/
-https://www.takashi-2521.com/about/
-https://www.takashi-2521.com/projects/
-https://www.takashi-2521.com/notes/
-```
+---
-ZolaのSection/Pageモデルに従い、以下を基本構造とする。
+## 3. サイトの基本コンセプト
-```text
-content/
-├── about.md
-│
-├── projects/
-│ ├── _index.md
-│ ├── project-a.md
-│ └── project-b.md
-│
-└── notes/
- ├── _index.md
- ├── note-a.md
- └── note-b.md
-```
+### 3.1 中心コンテンツ
-`projects/_index.md` と `notes/_index.md` は Zola Section とする。
+サイトの主役は以下の2つとする。
-`projects/*.md` および `notes/*.md` は Zola Page とする。
+1. **Notes**
+2. **Software**
-About は単独Pageとして扱う。
+HomeとAboutは、この2つへの入口・補助情報として扱う。
-必要な理由がある場合のみPage Bundle等の別構造を採用してよい。
+### 3.2 デザインコンセプト
----
+デザインは **Minimal Editorial Archive** を基本とする。
-# 6. Section設計
+方向性は以下。
-## 6.1 Projects Section
+- 文章とリンクを主役にする
+- 装飾を抑える
+- 罫線・余白・タイポグラフィで情報階層を表現する
+- カードUIを乱用しない
+- 画像を主役にしない
+- 技術者向けダッシュボード風・SaaSランディングページ風にしない
+- 長文を落ち着いて読める
+- 更新間隔が長くても「止まったサイト」に見えにくい
+- Gwern.netの長文アーカイブ性とSimon Willison's Weblogの実用的な情報構造を参考にする
-`content/projects/_index.md` を作成する。
+### 3.3 基本画面構造
-例:
+全画面の基本構造は以下。
-```toml
-+++
-title = "Projects"
-sort_by = "date"
-template = "projects.html"
-page_template = "project-page.html"
-+++
-```
+- 小さなHeader
+- Main content
+- 小さなFooter
-Zola標準のSection変数を使用する。
+Header/Footerは本文より目立たせず、高さも必要以上に取らない。
-Projects一覧はTera側で、
+Sidebarは使用しない。
-```text
-section.pages
-```
+---
-を利用して生成する。
+## 4. 情報アーキテクチャ
-独自JSON index等を作成しない。
+### 4.1 グローバル構成
----
+トップレベルは以下を基本とする。
-## 6.2 Notes Section
+- `/` — Home
+- `/notes/` — Notes一覧
+- `/software/` — Software一覧
+- `/about/` — About
+- `/tags/` — タグ一覧
+- `/tags/<tag>/` — タグ別Notes等の一覧
-`content/notes/_index.md` を作成する。
+### 4.2 グローバルナビゲーション
-例:
+Headerに以下を表示する。
-```toml
-+++
-title = "Notes"
-sort_by = "date"
-template = "notes.html"
-page_template = "note-page.html"
-+++
-```
+- Home
+- Notes
+- Software
+- About
-新しいNoteは `content/notes/*.md` を追加するだけで一覧へ反映されること。
+Source/cgitへのリンクはグローバルナビゲーションには常設せず、Software内で案内する。
---
-# 7. Page設計
-
-## 7.1 Project Page
+## 5. Home仕様
-Projectは1 Markdown = 1 Zola Pageとする。
+### 5.1 役割
-例:
+Homeはサイト全体の入口・索引として機能する。
-```toml
-+++
-title = "Example Project"
-date = 2026-08-21
-description = "Project description"
+ポートフォリオ用の大きなHero、自己宣伝CTA、プロフィール写真などは必須としない。
-[extra]
-status = "active"
-repository = "https://example.invalid/"
-+++
-```
+### 5.2 表示内容
-Zola標準front matterで直接扱えないプロジェクト固有metadataは `[extra]` を使用する。
+以下の順序を基本とする。
-最低限以下を扱えるようにする。
+1. サイト名
+2. 短い説明文
+3. 最近のNotes
+4. Software
+5. サイトについての短い説明
+6. Aboutへの導線
-* `title`
-* `date`
-* `description`
+### 5.3 トップ説明文
-Project固有情報:
+初期文言:
-* `extra.status`
-* `extra.repository`
+> 日々考えたこととか思ったことを記録します
-必要なら以下も追加可能。
+将来文言を修正できること。
-* `extra.homepage`
-* `extra.language`
-* `extra.license`
+### 5.4 最近のNotes
-metadataを過剰に増やさないこと。
+初期表示件数は **5件** とする。
----
+各項目には以下を表示する。
-## 7.2 Note Page
+- 公開日
+- タイトル
+- 必要に応じ短いdescription
-例:
+Homeでは情報量を抑え、本文を直接大量表示しない。
-```toml
-+++
-title = "Example Note"
-date = 2026-08-21
-description = "Note description"
-+++
+### 5.5 Software
-本文...
-```
+初期表示件数は **4件** とする。
-本文はZolaの `page.content` としてtemplateから表示する。
+Homeでは原則としてSoftware名を中心に表示する。
---
-# 8. Templates
+## 6. Notes仕様
-Zola公式ドキュメントのtemplate inheritanceを利用する。
+### 6.1 Notesの定義
-最低限以下を構成する。
+文章として残したい内容は原則としてNotesとして扱う。
-```text
-templates/
-├── base.html
-├── index.html
-├── projects.html
-├── project-page.html
-├── notes.html
-├── note-page.html
-└── page.html
-```
+技術メモ、読書記録、調査、考察、論考を別々のコンテンツ型へ過度に分割しない。
-必要であれば以下を追加する。
+### 6.2 Notes一覧
-```text
-templates/
-└── partials/
- ├── header.html
- ├── navigation.html
- └── footer.html
-```
+Notes一覧は **年別アーカイブ** とする。
-`base.html` を共通レイアウトとし、各templateはTeraの
+例:
-```text
-{% extends "base.html" %}
-```
-
-およびblockを使用する。
-
----
+- 2026
+ - 08-21 記事タイトル
+ - 05-03 記事タイトル
+- 2025
+ - ...
-# 9. Internal Link方針
+年は新しい順、各年内も原則として公開日の新しい順に表示する。
-サイト内部リンクは、可能な限りURL文字列をハードコードせず、Zolaの `get_url` と `@/` internal link記法を使用する。
+### 6.3 Notes一覧に表示する情報
-例:
+- 公開日
+- タイトル
+- タグ
+- 必要に応じdescription
-```tera
-<a href="{{ get_url(path='@/projects/_index.md') }}">Projects</a>
-```
+タグはNotes一覧およびタグ一覧で表示する。
-これによりcontent path変更時のリンク破損を減らす。
+### 6.4 Note詳細
----
+Note詳細では以下を表示する。
-# 10. Home Page
+- タイトル
+- 公開日
+- 更新日(存在する場合)
+- 目次
+- 本文
+- 脚注
+- 参考文献
+- 関連Software(設定されている場合)
+- 前後の記事導線は必須としない
-Homeは `templates/index.html` で生成する。
+タグはNote詳細の主要表示項目にしない。
-最低限以下を表示可能にする。
+### 6.5 目次
-* サイト名
-* 簡単な説明
-* Aboutへのリンク
-* Projectsへのリンク
-* Notesへのリンク
+Note詳細では **目次を常時表示**する。
-Home用の不要なCMS的データ構造は作らない。
+Sidebarは使わないため、本文冒頭付近にインラインで配置する。
-必要なら `zola.toml` の `extra` をサイト全体metadataとして使用してよい。
+見出しが存在しない場合は空の目次領域を表示しない。
----
+### 6.6 脚注
-# 11. Sass / SCSS
+Markdown/Zola標準機能による脚注を日常的に利用できること。
-Zola標準Sass compilationを使用する。
+脚注の見た目を本文と一貫させる。
-`zola.toml`:
+### 6.7 参考文献
-```toml
-compile_sass = true
-```
+長文記事の末尾にReferences/参考文献セクションを設けられること。
-構成例:
+参考文献は通常のMarkdownで記述可能とし、共通の見た目を持つ。
-```text
-sass/
-├── main.scss
-├── _variables.scss
-├── _base.scss
-├── _typography.scss
-├── _layout.scss
-└── _components.scss
-```
+### 6.8 公開日と更新日
-npm Sass / Dart Sass / PostCSS等は追加しない。
+- 公開日は必須
+- 更新日は任意
+- 更新日が存在する場合は公開日とともに表示する
----
+### 6.9 記事の成熟度
-# 12. Static assets
+Draft / Note / Essay 等の成熟度・状態ラベルは導入しない。
-Zolaの `static/` はbuild時に出力先へそのままコピーされる前提で使用する。
+Zola内部のdraft機能は公開制御に使用してよいが、利用者向けの成熟度表示は行わない。
-例:
+---
-```text
-static/
-├── favicon.svg
-├── robots.txt
-├── images/
-└── js/
- └── site.js
-```
+## 7. Tags仕様
-外部CDN依存を原則禁止する。
+### 7.1 分類方針
-JavaScriptが必要な場合は `static/js/site.js` 等として自前配信する。
+初期実装ではカテゴリを設けず、**tagsのみ**を使用する。
----
+### 7.2 タグの目的
-# 13. JavaScript
+- 分野横断の記事を柔軟に分類する
+- 同じテーマの記事を探せるようにする
+- 固定カテゴリ階層を避ける
-JavaScriptは必要最小限の vanilla JavaScript のみ使用する。
+### 7.3 想定タグ例
-以下の原則に従う。
+- 情報工学
+- 論理学
+- 資本論
+- 政治経済
+- 地球環境
+- AI
+- LLM
+- 読書
+- Windows
+- Git
-1. HTML標準機能で実現可能ならJSを使用しない。
-2. CSSのみで実現可能ならJSを使用しない。
-3. 外部frontend frameworkを導入しない。
-4. inline event handlerを使用しない。
-5. `eval()` を使用しない。
-6. JavaScript無効でも主要コンテンツを閲覧可能にする。
+タグ語彙はコンテンツ追加に応じて増やす。
---
-# 14. Syntax Highlighting
-
-技術記事用のコードブロックsyntax highlightingにはZola標準機能を使用する。
+## 8. Software仕様
-独自のPrism.js / highlight.js等は初期実装では導入しない。
+### 8.1 役割
-設定は採用するZola versionの公式configuration仕様に従うこと。
+Softwareは作成したソフトウェアを人間向けに説明する場所とする。
----
+ソースコードそのものの閲覧・履歴表示はcgitへ委ねる。
-# 15. Feeds
+### 8.2 Software一覧
-NotesについてRSS/Atom等のfeedを生成する。
+一覧では情報量を抑え、**Software名を主表示**とする。
-Zola標準feed生成機能を使用する。
+必要に応じ短いdescriptionを添えてよいが、画像や大量のmetadataは表示しない。
-独自feed generatorは実装しない。
+### 8.3 Software詳細
-採用するZola versionにおける推奨設定を確認して実装すること。
+以下の情報を扱えること。
----
+- Software名
+- 概要
+- 作成した背景・目的
+- 開発・制作の過程
+- 特徴
+- 使用言語
+- ライセンス
+- Source(自前cgit)
+- GitHub mirror(存在する場合)
+- 関連Notes
+- スクリーンショット(必要な場合のみ)
-# 16. Sitemap
+Implementation detailsは本文中で必要な範囲だけ記述し、固定項目として過剰に構造化しない。
-Zola標準のsitemap生成機能を使用する。
+### 8.4 Sourceの優先順位
-独自 `sitemap.xml` generatorは作らない。
+ソースコードの正本は **自前cgit** とする。
----
+Software詳細では以下の順で扱う。
-# 17. robots.txt
+1. Source — cgit
+2. GitHub mirror — 当面維持
-Zolaの標準 `robots.txt` template機能または `static/robots.txt` のうち、公式ドキュメントに沿った単純な方法を採用する。
+### 8.5 スクリーンショット
-不要な独自処理を作らない。
+- Software一覧では表示しない
+- Software詳細で必要な場合のみ表示する
+- 画像が多くなり本文の可読性を損なわないようにする
---
-# 18. 404 Page
+## 9. NotesとSoftwareの関連付け
-Zola標準の404 template仕様に従って実装する。
+NotesとSoftwareは相互に関連付けられること。
-想定:
+例:
-```text
-templates/404.html
-```
+- Softwareから関連Notesへ
+- Noteから関連Softwareへ
-Caddyから静的404として扱える成果物を生成する。
+関連付けは明示的に管理し、単なるタグ一致だけで自動的に関連付けない。
---
-# 19. Search
+## 10. About仕様
-初期実装では検索を無効とする。
+Aboutには以下を掲載する。
-`zola init` / configuration上もsearch indexを生成しない設定とする。
+- 名前
+- 一言プロフィール
+- このサイトの目的
+- 主な関心分野
+- 連絡先
-将来必要になった場合のみZola標準search indexを再検討する。
-
-外部検索libraryは現時点では導入しない。
+職務経歴書・詳細な履歴書としては設計しない。
---
-# 20. Themes
+## 11. Header仕様
-Zolaは `themes/` を標準で持つが、本プロジェクトでは外部themeを原則使用しない。
+### 11.1 表示内容
-理由:
+- サイト名「たかしの記録」
+- Home
+- Notes
+- Software
+- About
+- Dark/Light theme切替(提供する場合)
-* supply-chain面積を増やさない
-* upstream theme更新への依存を避ける
-* CSS/templateを完全に管理可能にする
-* 不要なJavaScriptを取り込まない
+### 11.2 レイアウト
-したがって自前template + 自前SCSSを基本とする。
+- 高さを抑える
+- サイト本文より主張しない
+- DesktopとMobileの双方で簡潔に表示
+- 必要でなければHamburger menuは使用しない
---
-# 21. Zola Configuration
-
-設定ファイル名は公式構成に合わせて、
-
-```text
-zola.toml
-```
+## 12. Footer仕様
-を使用する。
+Footerには最低限以下を表示する。
-最低限:
+- Copyright
+- RSS
+- Mail
-```toml
-base_url = "https://www.takashi-2521.com"
-compile_sass = true
-build_search_index = false
-```
+cgitリンクはSoftware側で扱う。
-実際のsyntax highlighting、feed、taxonomy等については、使用するZola versionの公式documentationを確認して正しいconfiguration keyを使用すること。
+---
-推測で設定名を追加しないこと。
+## 13. Typography仕様
----
+### 13.1 本文
-# 22. Development
+Sans-serifを使用する。
-ローカル開発ではZola標準development serverを使用する。
+Web fontは外部取得せず、system font stackを使用する。
-```bash
-zola serve
-```
+### 13.2 UI
-Zolaは `zola.toml`, `content`, `sass`, `static`, `templates` の変更を監視し、local live reloadに使用できる。
+Sans-serif + Monospaceを役割分担して使用する。
-追加のdev serverは作成しない。
+Monospaceは以下のような補助情報に使用できる。
----
+- 日付
+- metadata
+- ラベル
+- コード
+- 技術的な短い補助情報
-# 23. Validation / Build
+### 13.3 コード
-実装中は以下を使用する。
+system monospaceを使用する。
-```bash
-zola check
-zola build
-```
+---
-local preview:
+## 14. 色・テーマ仕様
-```bash
-zola serve
-```
+### 14.1 Light theme
-build output:
+- 紙に近い暖かみのある明るい背景
+- ほぼ黒の本文
+- muted gray
+- 薄い罫線
+- 青をaccent/link色として使用
-```text
-public/
-```
+### 14.2 Dark theme
-`public/` のみでサイトが成立すること。
+Dark modeに対応する。
----
+- 真っ黒ではなく濃いgrayを基本
+- 明るい本文
+- muted gray
+- dark border
+- light themeと対応する青系accent
-# 24. Git
+### 14.3 Accent
-sourceのみGit管理する。
+Accentは青系1色を基本とする。
-`.gitignore`:
+具体的な色値はDesignで定義する。
-```gitignore
-/public/
-```
+---
-Zolaが実際に生成しない独自cache directoryを推測で追加しないこと。
+## 15. リンク仕様
-Editor / OS固有ファイルは必要に応じて追加可能。
+- 通常リンクは青
+- 原則として下線を付け、リンクであることを明確にする
+- Hoverでは色等を軽く変える
+- 大きなアニメーションを付けない
+- 外部リンクには `↗` 等の簡潔な外部リンク記号を表示する
+- 外部アイコンライブラリは使用しない
---
-# 25. Deployment境界
+## 16. Layout仕様
-本仕様ではVPS側の配信基盤は変更しない。
+- Sidebarなし
+- Main contentを中央付近に配置
+- 長文は左右に十分な余白を確保
+- 最大本文幅はDesign段階で具体値を定義
+- Desktopでは広すぎない
+- Mobileでは横スクロールを発生させない
+- コードブロック等、必要な要素のみ内部横スクロールを許容
-既存:
+---
-```text
-/srv/www/takashi-2521.com/
-├── releases/
-└── current -> releases/<release-id>
-```
+## 17. Motion仕様
-Caddy:
+Animationは原則使用しない。
-```text
-root * /srv/www/takashi-2521.com/current
-```
+以下も初期実装では不要。
-クライアント側では将来、
+- View Transition
+- Parallax
+- Scroll animation
+- Fade-in animation
+- Decorative motion
-```text
-zola check
-→ zola build
-→ public/
-→ archive
-→ SSH transfer
-→ releases/<release-id>
-→ current切替
-```
+---
-を行う。
+## 18. 画像仕様
-本Client Phaseでは `public/` が自己完結したbuild artifactになることを保証する。
+- 画像は最小限
+- Homeに装飾目的の大量画像を置かない
+- Notesでは説明に必要な画像のみ
+- Softwareでは詳細ページのみ
+- 外部画像hotlinkを原則避ける
+- 代替テキストを付与する
---
-# 26. Security Requirements
-
-必須:
+## 19. Feed仕様
-* npmを使用しない
-* Node.js dependencyを使用しない
-* React/Vue/Svelteを使用しない
-* htmxを使用しない
-* Tailwindを使用しない
-* 外部CDNを使用しない
-* external themeを原則使用しない
-* backendを実装しない
-* DBを使用しない
-* buildをVPSで実行しない
+以下を提供する。
-可能な限り以下のCSPと互換な構造にする。
+- Atom
+- RSS
-```text
-default-src 'self';
-script-src 'self';
-style-src 'self';
-img-src 'self' data:;
-font-src 'self';
-object-src 'none';
-base-uri 'self';
-frame-ancestors 'none';
-```
+HeaderまたはFooterから購読導線を用意する。
-inline script / inline event handlerは避ける。
+Feed autodiscoveryに対応する。
---
-# 27. Accessibility
+## 20. Search仕様
-最低限以下を満たす。
+初期リリースではSearchを提供しない。
-* semantic HTML
-* 正しいheading hierarchy
-* keyboard navigation
-* visible focus
-* alt text
-* color contrast
-* `prefers-reduced-motion` を考慮
-* JSなしでも主要navigationが利用可能
+将来の記事数増加に応じて、Zolaの検索インデックス機能を第一候補として追加検討する。
---
-# 28. Client Phase
+## 21. Comments仕様
-## Client-1: Zola initialization
+初期リリースではコメント機能を提供しない。
-* Zola version確認
-* `zola init` 相当の標準構成を確認
-* `zola.toml`
-* 標準directory作成
-* `zola check`
-* `zola build`
+理由:
-PASS:
+- 完全静的構成では保存先が存在しない
+- 自前backendを追加すると現行セキュリティ境界が変化する
+- 外部コメントサービスは外部依存・プライバシーの再検討が必要
-```text
-zola check PASS
-zola build PASS
-public/index.html exists
-```
+将来機能として、以下を別途評価する。
----
+- 自前軽量コメントbackend
+- Gitベース方式
+- 外部コメントサービス
+- Mailによる感想受付
-## Client-2: Section / Page architecture
+---
-* Home
-* About Page
-* Projects Section
-* Project Pages
-* Notes Section
-* Note Pages
+## 22. SNS共有仕様
-PASS:
+初期リリースでは大きなSNS共有ボタン群を必須としない。
-```text
-/
- /about/
- /projects/
- /projects/<page>/
- /notes/
- /notes/<page>/
-```
+外部dependencyを追加せず、簡潔なテキストリンクまたはWeb Share API等だけで実現できる場合に追加を検討する。
-が生成される。
+サイトのEditorialデザインを損なわないことを優先する。
---
-## Client-3: Tera templates / UI
+## 23. Analytics仕様
-* `base.html`
-* template inheritance
-* navigation
-* header/footer
-* SCSS
-* responsive layout
+アクセス解析は導入しない。
-PASS:
+- 外部Analytics scriptなし
+- Tracking cookieなし
+- Marketing trackerなし
-* Desktop正常
-* Mobile正常
-* internal navigation正常
+Webサーバの運用ログはサーバ運用上のログであり、サイト機能としてのアクセス解析とは扱わない。
---
-## Client-4: Content model
+## 24. URL仕様
-Projects/NotesをZola Pageとして管理する。
+### 24.1 URL構造
-PASS:
+- `/notes/<slug>/`
+- `/software/<slug>/`
+- `/tags/<slug>/`
+- `/about/`
-Markdownを1件追加すると、
+### 24.2 Slug
-```text
-Section listing
-+
-individual Page
-```
+Content filename / slugは **英語ASCIIを基本**とする。
-の両方へ自動反映される。
+記事本文や表示タイトルは日本語でよい。
----
-
-## Client-5: Zola static features
-
-* syntax highlighting
-* feed
-* sitemap
-* robots.txt
-* 404
-* favicon
+URLの安定性を優先し、公開後のslug変更は原則避ける。
-Zola標準機能を優先する。
+---
-PASS:
+## 25. 日本語・多言語仕様
-`zola build` の成果物に必要な静的ファイルが生成される。
+- 表示言語は日本語のみ
+- UI文言も日本語
+- 初期設計では多言語対応を実装しない
+- 多言語化のための不要なUIやselectorは設けない
---
-## Client-6: Git / final validation
+## 26. Accessibility要求
-* `.gitignore`
-* sourceのみGit管理
-* external dependenciesなし
-* final `zola check`
-* final `zola build`
+最低限以下を満たす。
----
+- Semantic HTML
+- 適切なheading hierarchy
+- Keyboard navigation
+- Visible focus
+- 十分な色contrast
+- 画像alt
+- Linkが色だけで判別不能にならない
+- HTML/主要navigationはJavaScriptなしでも利用可能
+- Theme切替機能がなくてもOS themeで十分読めること
+- prefers-reduced-motionを尊重(原則motion自体を使用しない)
+
+---
+
+## 27. Performance要求
+
+サイトは静的HTML/CSS/最小JSのみで成立すること。
-# 29. Final Acceptance Criteria
-
-以下をすべて満たすこと。
-
-* [ ] Zola公式のSection/Pageモデルを使用している
-* [ ] `zola.toml` を使用している
-* [ ] `content/` を使用している
-* [ ] Sectionは `_index.md` で定義されている
-* [ ] ProjectはZola Pageとして実装されている
-* [ ] NoteはZola Pageとして実装されている
-* [ ] Section `template` / `page_template` を適切に使用している
-* [ ] Tera template inheritanceを使用している
-* [ ] internal linkにZola標準機能を利用している
-* [ ] Sass compilationはZola標準機能
-* [ ] syntax highlightingはZola標準機能
-* [ ] feedはZola標準機能
-* [ ] sitemapはZola標準機能
-* [ ] 404はZola標準機能
-* [ ] `zola serve` が動作
-* [ ] `zola check` が成功
-* [ ] `zola build` が成功
-* [ ] `public/` 単独で静的サイトとして成立
-* [ ] npm dependencyなし
-* [ ] Node.js dependencyなし
-* [ ] frontend frameworkなし
-* [ ] htmxなし
-* [ ] external CDNなし
-* [ ] external theme依存なし
-* [ ] backendなし
-* [ ] DBなし
-* [ ] `public/` はGit管理外
-
-[1]: https://www.getzola.org/documentation/getting-started/overview/ "Overview | Zola"
+- Backend runtime不要
+- DB不要
+- npm/Node.js不要
+- 外部CDN不要
+- Web font不要
+- 大きなfrontend framework不要
+- 初期表示に不要な画像を読み込まない
+- JavaScriptが無効でも主要本文が読める
+
+---
+
+## 28. Security / Privacy要求
+
+- npm package dependencyを使用しない
+- Node.js runtimeを使用しない
+- 外部themeを原則使用しない
+- 外部JavaScript CDNを使用しない
+- Inline JavaScriptを避ける
+- `eval()` を使用しない
+- 外部trackingを使用しない
+- CSP `default-src 'self'` を中心とした厳格な設定と互換性を持つ
+- Static Siteとして本番にserver-side codeを置かない
+- Caddyは生成済み成果物のみを配信する
+
+---
+
+## 29. 技術基盤に関する制約
+
+以下はDesignに委ねるが、要求上の前提とする。
+
+- SSG: Zola
+- Template: Tera
+- Content: Markdown
+- Style: SCSS
+- Client JS: minimal vanilla JavaScript
+- Production: Caddy static file server
+- Build: Client PC
+- Deployment: `public/` build artifactをVPSへ転送
+- Git source管理とDeployは独立
+- cgitをソースコード正本とする
+
+---
+
+## 30. 初期リリースの非目標
+
+以下は初期リリースで実装しない。
+
+- コメント
+- Search
+- CMS
+- User login
+- Database
+- SSR
+- SPA
+- API backend
+- WebSocket
+- Analytics
+- Frontend framework
+- htmx
+- Tailwind CSS
+- npm/Node.js
+- External theme
+- Heavy animation
+- Large image gallery
+- Multilingual UI
+
+---
+
+## 31. 受入条件
+
+初期リリースは以下を満たすこと。
+
+- [ ] 「たかしの記録」というサイト名が表示される
+- [ ] Home / Notes / Software / Aboutへ到達可能
+- [ ] Homeに最近のNotes最大5件を表示
+- [ ] HomeにSoftware最大4件を表示
+- [ ] Notesが年別に表示される
+- [ ] Notes一覧にタグが表示される
+- [ ] Note詳細に公開日・更新日・目次・本文を表示できる
+- [ ] 脚注を利用できる
+- [ ] 参考文献を自然に表示できる
+- [ ] Software一覧を画像なしの簡潔な一覧として表示
+- [ ] Software詳細に背景・過程・言語・ライセンス・Source等を記載できる
+- [ ] cgitをSource正本として案内できる
+- [ ] GitHub mirrorを任意表示できる
+- [ ] NoteとSoftwareを明示的に相互関連付けできる
+- [ ] Aboutに名前・一言プロフィール・目的・関心分野・連絡先を表示できる
+- [ ] Light/Dark双方で読める
+- [ ] 外部リンクを視覚的に判別できる
+- [ ] RSSとAtomを提供
+- [ ] 404ページを提供
+- [ ] Mobile/Desktop双方で利用可能
+- [ ] JavaScriptなしでも主要コンテンツを閲覧可能
+- [ ] 外部CDN・npm・Node.js・frontend frameworkを必要としない
+- [ ] サイト全体が静的ファイルとして生成可能
+
+---
+
+## 32. 将来拡張
+
+要件変更時に別途設計する。
+
+- Search
+- Comments
+- SNS共有
+- より高度な関連コンテンツ表示
+- タグ数増加へのUI改善
+- Page pagination
+- 多言語
+- 画像処理の高度化
+
+初期実装で将来拡張のための複雑な抽象化を先行導入しない。
diff --git a/sass/_archive.scss b/sass/_archive.scss
new file mode 100644
index 0000000..32318f5
--- /dev/null
+++ b/sass/_archive.scss
@@ -0,0 +1,90 @@
+.compact-list,
+.archive,
+.software-list-full {
+ border-top: var(--rule);
+}
+
+.note-list-item {
+ display: grid;
+ grid-template-columns: 8rem minmax(0, 1fr);
+ gap: 1.5rem;
+ padding-block: 1.25rem;
+ border-bottom: var(--rule);
+}
+
+.note-list-item time {
+ color: var(--color-muted);
+ font-size: 0.8rem;
+}
+
+.note-list-item h3 {
+ margin-bottom: 0.25rem;
+}
+
+.note-list-item h3 a,
+.software-list-item h3 a {
+ color: var(--color-text);
+}
+
+.note-list-item p,
+.software-list-item p {
+ color: var(--color-muted);
+ font-size: 0.95rem;
+}
+
+.tag-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.3rem 0.8rem;
+ margin-top: 0.45rem;
+ list-style: none;
+ font-size: 0.75rem;
+}
+
+.tag-list a {
+ color: var(--color-muted);
+}
+
+.archive-year {
+ display: grid;
+ grid-template-columns: 7rem minmax(0, 1fr);
+ gap: 2rem;
+ padding-block: 2rem;
+ border-bottom: var(--rule);
+}
+
+.archive-year > h2 {
+ font-family: var(--font-mono);
+ font-size: 1rem;
+ font-weight: 500;
+}
+
+.archive-year .note-list-item:first-child {
+ padding-top: 0;
+}
+
+.archive-year .note-list-item:last-child {
+ padding-bottom: 0;
+ border-bottom: 0;
+}
+
+.empty-state {
+ padding-block: 2rem;
+ border-block: var(--rule);
+ color: var(--color-muted);
+}
+
+@media (max-width: 40rem) {
+ .archive-year,
+ .note-list-item {
+ grid-template-columns: 1fr;
+ }
+
+ .archive-year {
+ gap: 1rem;
+ }
+
+ .note-list-item {
+ gap: 0.25rem;
+ }
+}
diff --git a/sass/_article.scss b/sass/_article.scss
new file mode 100644
index 0000000..91a0ac4
--- /dev/null
+++ b/sass/_article.scss
@@ -0,0 +1,159 @@
+.article-header {
+ margin-bottom: 2.5rem;
+}
+
+.article-header h1 {
+ margin-block: 0.7rem 1.25rem;
+}
+
+.article-dates {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem 1.5rem;
+ margin-bottom: 1.5rem;
+ color: var(--color-muted);
+ font-size: 0.78rem;
+}
+
+.article-dates div {
+ display: flex;
+ gap: 0.65rem;
+}
+
+.toc {
+ margin-bottom: 3.5rem;
+ padding: 1.25rem 1.5rem;
+ border-block: var(--rule);
+ background: var(--color-surface);
+}
+
+.toc h2 {
+ margin-bottom: 0.6rem;
+ font-size: 0.95rem;
+}
+
+.toc ul {
+ padding-left: 1.2rem;
+}
+
+.toc > ul {
+ padding-left: 0;
+ list-style: none;
+}
+
+.toc li + li {
+ margin-top: 0.25rem;
+}
+
+.toc a {
+ font-size: 0.9rem;
+}
+
+.prose {
+ font-size: 1.03rem;
+}
+
+.prose > * + * {
+ margin-top: 1.45em;
+}
+
+.prose h2,
+.prose h3 {
+ scroll-margin-top: 2rem;
+}
+
+.prose h2 {
+ margin-top: 3.2em;
+ padding-top: 0.8em;
+ border-top: var(--rule);
+}
+
+.prose h3 {
+ margin-top: 2.3em;
+}
+
+.prose .zola-anchor {
+ color: inherit;
+ text-decoration: none;
+}
+
+.prose ul,
+.prose ol {
+ padding-left: 1.5rem;
+}
+
+.prose li + li {
+ margin-top: 0.35rem;
+}
+
+.prose blockquote {
+ padding-left: 1.25rem;
+ border-left: 3px solid var(--color-border);
+ color: var(--color-muted);
+}
+
+.prose table {
+ display: block;
+ width: max-content;
+ max-width: 100%;
+ overflow-x: auto;
+ border-collapse: collapse;
+}
+
+.prose th,
+.prose td {
+ padding: 0.5rem 0.75rem;
+ border: var(--rule);
+ text-align: left;
+}
+
+.prose figure,
+.prose img {
+ margin-inline: auto;
+}
+
+.prose img {
+ height: auto;
+ border: var(--rule);
+}
+
+.footnote-definition,
+[role="doc-endnotes"] {
+ color: var(--color-muted);
+ font-size: 0.9rem;
+}
+
+.footnote-definition {
+ display: grid;
+ grid-template-columns: auto 1fr;
+ gap: 0.5rem;
+}
+
+.footnote-definition p {
+ margin: 0;
+}
+
+.related-content {
+ margin-top: 4rem;
+ padding-top: 1.5rem;
+ border-top: var(--rule);
+}
+
+.related-content h2 {
+ margin-bottom: 1rem;
+ font-size: 1.1rem;
+}
+
+.related-content ul {
+ list-style: none;
+}
+
+.related-content li + li {
+ margin-top: 0.75rem;
+}
+
+.related-content span {
+ display: block;
+ color: var(--color-muted);
+ font-size: 0.9rem;
+}
diff --git a/sass/_base.scss b/sass/_base.scss
new file mode 100644
index 0000000..d460c41
--- /dev/null
+++ b/sass/_base.scss
@@ -0,0 +1,47 @@
+body {
+ min-height: 100vh;
+ overflow-x: hidden;
+ overflow-wrap: anywhere;
+ background: var(--color-bg);
+ color: var(--color-text);
+ font-family: var(--font-sans);
+ font-size: 1rem;
+ line-height: 1.75;
+ text-rendering: optimizeLegibility;
+}
+
+main,
+article,
+section,
+div {
+ min-width: 0;
+}
+
+a {
+ color: var(--color-link);
+ text-decoration-line: underline;
+ text-decoration-thickness: 0.075em;
+ text-underline-offset: 0.18em;
+}
+
+a:hover {
+ color: var(--color-link-hover);
+}
+
+:focus-visible {
+ outline: 3px solid var(--color-focus);
+ outline-offset: 3px;
+}
+
+::selection {
+ background: var(--color-link);
+ color: var(--color-bg);
+}
+
+.external-link::after {
+ display: inline-block;
+ margin-left: 0.2em;
+ content: "↗";
+ font-size: 0.78em;
+ text-decoration: none;
+}
diff --git a/sass/_code.scss b/sass/_code.scss
new file mode 100644
index 0000000..bc83892
--- /dev/null
+++ b/sass/_code.scss
@@ -0,0 +1,38 @@
+code,
+pre {
+ font-family: var(--font-mono);
+}
+
+.prose code:not(pre code) {
+ padding: 0.1em 0.3em;
+ border: var(--rule);
+ background: var(--color-surface);
+ font-size: 0.88em;
+}
+
+.prose pre {
+ width: 100%;
+ max-width: 100%;
+ overflow-x: auto;
+ padding: 1rem 1.15rem;
+ border: var(--rule);
+ background: var(--color-surface);
+ font-size: 0.88rem;
+ line-height: 1.65;
+}
+
+.giallo-l {
+ display: inline-block;
+ min-height: 1lh;
+ width: 100%;
+}
+
+.giallo-ln {
+ display: inline-block;
+ min-width: 3ch;
+ margin-right: 0.4em;
+ padding: 0.4em;
+ opacity: 0.8;
+ text-align: right;
+ user-select: none;
+}
diff --git a/sass/_footer.scss b/sass/_footer.scss
new file mode 100644
index 0000000..2d5c475
--- /dev/null
+++ b/sass/_footer.scss
@@ -0,0 +1,29 @@
+.site-footer {
+ margin-top: 4rem;
+ border-top: var(--rule);
+ color: var(--color-muted);
+ font-size: 0.85rem;
+}
+
+.footer-inner {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1.5rem;
+ min-height: 5rem;
+}
+
+.footer-inner nav {
+ display: flex;
+ gap: 1rem;
+}
+
+@media (max-width: 32rem) {
+ .footer-inner {
+ align-items: flex-start;
+ flex-direction: column;
+ justify-content: center;
+ gap: 0.3rem;
+ padding-block: 1rem;
+ }
+}
diff --git a/sass/_header.scss b/sass/_header.scss
new file mode 100644
index 0000000..8512a55
--- /dev/null
+++ b/sass/_header.scss
@@ -0,0 +1,77 @@
+.site-header {
+ border-bottom: var(--rule);
+}
+
+.header-inner {
+ display: flex;
+ align-items: center;
+ gap: 1.5rem;
+ min-height: 3.8rem;
+}
+
+.site-title {
+ margin-right: auto;
+ color: var(--color-text);
+ font-weight: 700;
+ text-decoration: none;
+}
+
+.nav-list {
+ display: flex;
+ gap: 1.2rem;
+ list-style: none;
+}
+
+.nav-list a {
+ color: var(--color-muted);
+ font-size: 0.9rem;
+ text-decoration: none;
+}
+
+.nav-list a:hover,
+.nav-list a[aria-current="page"] {
+ color: var(--color-text);
+ text-decoration: underline;
+ text-underline-offset: 0.3em;
+}
+
+.theme-toggle {
+ display: none;
+ padding: 0.2rem 0;
+ border: 0;
+ border-bottom: 1px solid currentColor;
+ background: transparent;
+ color: var(--color-muted);
+ cursor: pointer;
+ font-size: 0.75rem;
+}
+
+.theme-ready .theme-toggle {
+ display: block;
+}
+
+@media (max-width: 42rem) {
+ .header-inner {
+ flex-wrap: wrap;
+ gap: 0.55rem 1rem;
+ padding-block: 0.8rem;
+ }
+
+ .site-title {
+ flex: 1 1 auto;
+ }
+
+ .header-inner nav {
+ order: 3;
+ width: 100%;
+ }
+
+ .nav-list {
+ flex-wrap: wrap;
+ gap: 0.4rem 1.25rem;
+ }
+
+ .theme-toggle {
+ margin-left: auto;
+ }
+}
diff --git a/sass/_layout.scss b/sass/_layout.scss
new file mode 100644
index 0000000..126fa96
--- /dev/null
+++ b/sass/_layout.scss
@@ -0,0 +1,106 @@
+.site-container {
+ width: min(calc(100% - 2rem), var(--site-max));
+ max-width: calc(100% - 2rem);
+ margin-inline: auto;
+}
+
+.site-main {
+ min-height: 65vh;
+}
+
+.narrow,
+.prose,
+.related-content {
+ width: min(100%, var(--content-max));
+ margin-inline: auto;
+}
+
+.home-intro {
+ padding-block: clamp(4rem, 10vw, 7rem);
+ border-bottom: var(--rule);
+}
+
+.home-intro h1 {
+ margin-bottom: 1rem;
+}
+
+.home-intro p {
+ max-width: 38rem;
+}
+
+.home-sections,
+.listing-page,
+.editorial-page,
+.article-page {
+ padding-block: clamp(3.5rem, 8vw, 6rem);
+}
+
+.home-sections {
+ display: grid;
+ gap: clamp(4rem, 9vw, 7rem);
+}
+
+.section-heading {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+ gap: 1rem;
+ margin-bottom: 1.1rem;
+ padding-bottom: 0.75rem;
+ border-bottom: var(--rule);
+}
+
+.section-heading a {
+ white-space: nowrap;
+}
+
+.home-about {
+ width: min(100%, var(--content-max));
+ padding-top: 2rem;
+ border-top: var(--rule);
+}
+
+.home-about h2,
+.home-about p {
+ margin-bottom: 0.8rem;
+}
+
+.page-header {
+ margin-bottom: clamp(2.5rem, 6vw, 4.5rem);
+}
+
+.page-header h1 {
+ margin-bottom: 0.8rem;
+}
+
+.editorial-page .page-header {
+ margin-bottom: 2.5rem;
+}
+
+.error-page {
+ padding-block: clamp(5rem, 14vw, 10rem);
+}
+
+.error-page .error-code {
+ margin-bottom: 1rem;
+ color: var(--color-muted);
+ font-size: 0.9rem;
+}
+
+.error-page h1,
+.error-page > p {
+ margin-bottom: 1rem;
+}
+
+.error-page nav {
+ display: flex;
+ gap: 1.25rem;
+ margin-top: 2rem;
+}
+
+@media (max-width: 38rem) {
+ .section-heading {
+ align-items: flex-start;
+ flex-direction: column;
+ }
+}
diff --git a/sass/_reset.scss b/sass/_reset.scss
new file mode 100644
index 0000000..72924c4
--- /dev/null
+++ b/sass/_reset.scss
@@ -0,0 +1,41 @@
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+}
+
+html {
+ overflow-x: hidden;
+}
+
+body,
+h1,
+h2,
+h3,
+p,
+ul,
+ol,
+dl,
+dd,
+figure,
+blockquote {
+ margin: 0;
+}
+
+ul,
+ol {
+ padding: 0;
+}
+
+img,
+svg {
+ display: block;
+ max-width: 100%;
+}
+
+button,
+input,
+textarea,
+select {
+ font: inherit;
+}
diff --git a/sass/_software.scss b/sass/_software.scss
new file mode 100644
index 0000000..4ad6e42
--- /dev/null
+++ b/sass/_software.scss
@@ -0,0 +1,70 @@
+.software-list-item {
+ padding-block: 1.3rem;
+ border-bottom: var(--rule);
+}
+
+.software-list-item h3 {
+ margin-bottom: 0.25rem;
+ font-family: var(--font-mono);
+}
+
+.software-list-full .software-list-item {
+ display: grid;
+ grid-template-columns: minmax(10rem, 0.6fr) minmax(0, 1.4fr);
+ gap: 2rem;
+}
+
+.source-note {
+ margin-top: 1.5rem;
+ color: var(--color-muted);
+ font-size: 0.9rem;
+}
+
+.software-meta {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ margin-bottom: 3.5rem;
+ border-block: var(--rule);
+}
+
+.software-meta div {
+ display: grid;
+ grid-template-columns: 7rem minmax(0, 1fr);
+ gap: 1rem;
+ padding-block: 0.75rem;
+ border-bottom: var(--rule);
+}
+
+.software-meta div:nth-last-child(-n + 2) {
+ border-bottom: 0;
+}
+
+.software-meta dt {
+ color: var(--color-muted);
+ font-size: 0.85rem;
+}
+
+.software-meta dd {
+ font-family: var(--font-mono);
+ font-size: 0.82rem;
+}
+
+@media (max-width: 42rem) {
+ .software-list-full .software-list-item,
+ .software-meta {
+ grid-template-columns: 1fr;
+ }
+
+ .software-list-full .software-list-item {
+ gap: 0.3rem;
+ }
+
+ .software-meta div,
+ .software-meta div:nth-last-child(-n + 2) {
+ border-bottom: var(--rule);
+ }
+
+ .software-meta div:last-child {
+ border-bottom: 0;
+ }
+}
diff --git a/sass/_taxonomy.scss b/sass/_taxonomy.scss
new file mode 100644
index 0000000..9f635bb
--- /dev/null
+++ b/sass/_taxonomy.scss
@@ -0,0 +1,19 @@
+.taxonomy-list {
+ border-top: var(--rule);
+ list-style: none;
+}
+
+.taxonomy-list li {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+ gap: 1rem;
+ padding-block: 0.85rem;
+ border-bottom: var(--rule);
+}
+
+.taxonomy-list span {
+ color: var(--color-muted);
+ font-family: var(--font-mono);
+ font-size: 0.78rem;
+}
diff --git a/sass/_theme.scss b/sass/_theme.scss
new file mode 100644
index 0000000..ad39b22
--- /dev/null
+++ b/sass/_theme.scss
@@ -0,0 +1,16 @@
+:root[data-theme="light"] body,
+:root[data-theme="dark"] body {
+ background: var(--color-bg);
+ color: var(--color-text);
+}
+
+@media (prefers-reduced-motion: reduce) {
+ *,
+ *::before,
+ *::after {
+ scroll-behavior: auto !important;
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ }
+}
diff --git a/sass/_tokens.scss b/sass/_tokens.scss
new file mode 100644
index 0000000..a0ed8f3
--- /dev/null
+++ b/sass/_tokens.scss
@@ -0,0 +1,42 @@
+:root {
+ color-scheme: light;
+ --color-bg: #f6f5f1;
+ --color-surface: #fbfaf7;
+ --color-text: #202124;
+ --color-muted: #666b70;
+ --color-border: #d6d8da;
+ --color-link: #315f82;
+ --color-link-hover: #244a67;
+ --color-focus: #1e668f;
+ --font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans JP", sans-serif;
+ --font-mono: ui-monospace, "Cascadia Code", "SFMono-Regular", Consolas, "Liberation Mono", monospace;
+ --content-max: 72ch;
+ --site-max: 72rem;
+ --rule: 1px solid var(--color-border);
+}
+
+:root[data-theme="dark"] {
+ color-scheme: dark;
+ --color-bg: #17191b;
+ --color-surface: #1d2022;
+ --color-text: #e9e9e6;
+ --color-muted: #a0a3a6;
+ --color-border: #363a3e;
+ --color-link: #86b6d8;
+ --color-link-hover: #add3ed;
+ --color-focus: #86b6d8;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root:not([data-theme="light"]) {
+ color-scheme: dark;
+ --color-bg: #17191b;
+ --color-surface: #1d2022;
+ --color-text: #e9e9e6;
+ --color-muted: #a0a3a6;
+ --color-border: #363a3e;
+ --color-link: #86b6d8;
+ --color-link-hover: #add3ed;
+ --color-focus: #86b6d8;
+ }
+}
diff --git a/sass/_typography.scss b/sass/_typography.scss
new file mode 100644
index 0000000..4b11814
--- /dev/null
+++ b/sass/_typography.scss
@@ -0,0 +1,42 @@
+h1,
+h2,
+h3 {
+ overflow-wrap: anywhere;
+ color: var(--color-text);
+ font-weight: 650;
+ line-height: 1.3;
+ letter-spacing: -0.02em;
+}
+
+h1 {
+ font-size: clamp(2rem, 5vw, 3.25rem);
+}
+
+h2 {
+ font-size: clamp(1.35rem, 3vw, 1.7rem);
+}
+
+h3 {
+ font-size: 1.08rem;
+}
+
+time,
+.article-kind,
+.article-dates,
+.theme-toggle,
+.tag-list,
+.error-code {
+ font-family: var(--font-mono);
+}
+
+.article-kind {
+ font-size: 0.8rem;
+ letter-spacing: 0.06em;
+}
+
+.article-description,
+.page-header > p,
+.home-intro > p {
+ color: var(--color-muted);
+ font-size: 1.08rem;
+}
diff --git a/sass/_utilities.scss b/sass/_utilities.scss
new file mode 100644
index 0000000..0f9beef
--- /dev/null
+++ b/sass/_utilities.scss
@@ -0,0 +1,25 @@
+.skip-link {
+ position: fixed;
+ z-index: 10;
+ top: 0.5rem;
+ left: 0.5rem;
+ padding: 0.45rem 0.7rem;
+ transform: translateY(-160%);
+ background: var(--color-text);
+ color: var(--color-bg);
+ font-weight: 700;
+}
+
+.skip-link:focus {
+ transform: translateY(0);
+}
+
+.visually-hidden {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ overflow: hidden;
+ clip: rect(0 0 0 0);
+ white-space: nowrap;
+ clip-path: inset(50%);
+}
diff --git a/sass/main.scss b/sass/main.scss
new file mode 100644
index 0000000..7735763
--- /dev/null
+++ b/sass/main.scss
@@ -0,0 +1,14 @@
+@import "tokens";
+@import "reset";
+@import "base";
+@import "typography";
+@import "layout";
+@import "header";
+@import "footer";
+@import "archive";
+@import "article";
+@import "software";
+@import "taxonomy";
+@import "code";
+@import "theme";
+@import "utilities";
diff --git a/static/favicon.svg b/static/favicon.svg
new file mode 100644
index 0000000..39493b6
--- /dev/null
+++ b/static/favicon.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-labelledby="title">
+ <title id="title">たかしの記録</title>
+ <rect width="64" height="64" rx="6" fill="#202124"/>
+ <path d="M15 15h34v34H15z" fill="none" stroke="#86b6d8" stroke-width="3"/>
+ <path d="M24 23h16M24 31h16M24 39h11" stroke="#f6f5f1" stroke-width="3"/>
+</svg>
diff --git a/static/js/theme.js b/static/js/theme.js
new file mode 100644
index 0000000..3d2b25c
--- /dev/null
+++ b/static/js/theme.js
@@ -0,0 +1,75 @@
+(function () {
+ "use strict";
+
+ var root = document.documentElement;
+ root.classList.add("theme-ready");
+ var toggle = document.querySelector("[data-theme-toggle]");
+ var syntaxLight = document.getElementById("syntax-light");
+ var syntaxDark = document.getElementById("syntax-dark");
+ var media = window.matchMedia("(prefers-color-scheme: dark)");
+ var storedTheme = null;
+
+ try {
+ storedTheme = window.localStorage.getItem("theme");
+ } catch (error) {
+ storedTheme = null;
+ }
+
+ function activeTheme() {
+ if (root.dataset.theme === "light" || root.dataset.theme === "dark") {
+ return root.dataset.theme;
+ }
+ return media.matches ? "dark" : "light";
+ }
+
+ function syncSyntax(theme) {
+ if (!syntaxLight || !syntaxDark) {
+ return;
+ }
+ syntaxLight.media = theme === "light" ? "all" : "not all";
+ syntaxDark.media = theme === "dark" ? "all" : "not all";
+ }
+
+ function updateToggle() {
+ if (!toggle) {
+ return;
+ }
+ var dark = activeTheme() === "dark";
+ toggle.setAttribute("aria-pressed", String(dark));
+ toggle.setAttribute("aria-label", dark ? "ライトテーマに切り替える" : "ダークテーマに切り替える");
+ toggle.textContent = dark ? "Light" : "Dark";
+ }
+
+ function applyTheme(theme, persist) {
+ root.dataset.theme = theme;
+ syncSyntax(theme);
+ updateToggle();
+ if (persist) {
+ try {
+ window.localStorage.setItem("theme", theme);
+ } catch (error) {
+ // Storageが利用できなくても、現在のpageではthemeを維持する。
+ }
+ }
+ }
+
+ if (storedTheme === "light" || storedTheme === "dark") {
+ applyTheme(storedTheme, false);
+ } else {
+ syncSyntax(activeTheme());
+ updateToggle();
+ }
+
+ if (toggle) {
+ toggle.addEventListener("click", function () {
+ applyTheme(activeTheme() === "dark" ? "light" : "dark", true);
+ });
+ }
+
+ media.addEventListener("change", function () {
+ if (!root.dataset.theme) {
+ syncSyntax(activeTheme());
+ updateToggle();
+ }
+ });
+})();
diff --git a/templates/404.html b/templates/404.html
new file mode 100644
index 0000000..408e222
--- /dev/null
+++ b/templates/404.html
@@ -0,0 +1,17 @@
+{% extends "base.html" %}
+
+{% block title %}404 | {{ config.title }}{% endblock title %}
+{% block description %}ページが見つかりませんでした。{% endblock description %}
+
+{% block content %}
+<section class="site-container error-page" aria-labelledby="error-title">
+ <p class="error-code">404</p>
+ <h1 id="error-title">ページが見つかりません。</h1>
+ <p>URLが変更されたか、ページが存在しない可能性があります。</p>
+ <nav aria-label="エラーページからの移動">
+ <a href="{{ get_url(path='@/_index.md') }}">Home</a>
+ <a href="{{ get_url(path='@/notes/_index.md') }}">Notes</a>
+ <a href="{{ get_url(path='@/software/_index.md') }}">Software</a>
+ </nav>
+</section>
+{% endblock content %}
diff --git a/templates/base.html b/templates/base.html
new file mode 100644
index 0000000..543259a
--- /dev/null
+++ b/templates/base.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<html lang="ja">
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="color-scheme" content="light dark">
+ <meta name="description" content="{% block description %}{{ config.description }}{% endblock description %}">
+ <title>{% block title %}{{ config.title }}{% endblock title %}</title>
+ <link rel="canonical" href="{{ current_url | default(value=config.base_url) }}">
+ <link rel="icon" href="{{ get_url(path='favicon.svg') }}" type="image/svg+xml">
+ <link rel="alternate" type="application/atom+xml" title="{{ config.title }} Atom" href="{{ get_url(path='atom.xml', trailing_slash=false) }}">
+ <link rel="alternate" type="application/rss+xml" title="{{ config.title }} RSS" href="{{ get_url(path='rss.xml', trailing_slash=false) }}">
+ <link id="syntax-light" rel="stylesheet" href="{{ get_url(path='giallo-light.css') }}" media="(prefers-color-scheme: light)">
+ <link id="syntax-dark" rel="stylesheet" href="{{ get_url(path='giallo-dark.css') }}" media="(prefers-color-scheme: dark)">
+ <link rel="stylesheet" href="{{ get_url(path='main.css') }}">
+ <script src="{{ get_url(path='js/theme.js') }}" defer></script>
+</head>
+<body>
+ <a class="skip-link" href="#main-content">本文へ移動</a>
+ {% include "partials/header.html" %}
+ <main id="main-content" class="site-main" tabindex="-1">
+ {% block content %}{% endblock content %}
+ </main>
+ {% include "partials/footer.html" %}
+</body>
+</html>
diff --git a/templates/index.html b/templates/index.html
new file mode 100644
index 0000000..3f0c8c6
--- /dev/null
+++ b/templates/index.html
@@ -0,0 +1,62 @@
+{% extends "base.html" %}
+
+{% block title %}{{ config.title }}{% endblock title %}
+{% block description %}{{ section.description }}{% endblock description %}
+
+{% block content %}
+{% set about_url = get_url(path='@/about.md') %}
+{% set notes_url = get_url(path='@/notes/_index.md') %}
+{% set software_url = get_url(path='@/software/_index.md') %}
+{% set software = get_section(path='software/_index.md') %}
+{% set notes = get_section(path='notes/_index.md') %}
+<section class="site-container home-intro" aria-labelledby="home-title">
+ <h1 id="home-title">{{ section.title }}</h1>
+ <p>{{ section.description }}</p>
+</section>
+
+<div class="site-container home-sections">
+ <section class="home-section" aria-labelledby="latest-notes">
+ <div class="section-heading">
+ <h2 id="latest-notes">最近のNotes</h2>
+ <a href="{{ notes_url }}">すべてのNotes</a>
+ </div>
+ {% if notes.pages %}
+ <div class="note-list compact-list">
+ {% for note in notes.pages %}
+ {% if loop.index <= config.extra.home_notes_limit %}
+ {% set item = note %}
+ {% include "partials/note-list-item.html" %}
+ {% endif %}
+ {% endfor %}
+ </div>
+ {% else %}
+ <p class="empty-state">ノートは準備中です。新しい記録はここに追加されます。</p>
+ {% endif %}
+ </section>
+
+ <section class="home-section" aria-labelledby="home-software">
+ <div class="section-heading">
+ <h2 id="home-software">Software</h2>
+ <a href="{{ software_url }}">すべてのSoftware</a>
+ </div>
+ {% if software.pages %}
+ <div class="software-list compact-list">
+ {% for software_item in software.pages %}
+ {% if loop.index <= config.extra.home_software_limit %}
+ {% set item = software_item %}
+ {% include "partials/software-list-item.html" %}
+ {% endif %}
+ {% endfor %}
+ </div>
+ {% else %}
+ <p class="empty-state">Softwareは準備中です。</p>
+ {% endif %}
+ </section>
+
+ <section class="home-about" aria-labelledby="home-about-title">
+ <h2 id="home-about-title">このサイトについて</h2>
+ <p>考えたこと、読んだこと、調べたことと、作成したソフトウェアを長期的に残すための個人アーカイブです。</p>
+ <a href="{{ about_url }}">Aboutを読む</a>
+ </section>
+</div>
+{% endblock content %}
diff --git a/templates/notes/page.html b/templates/notes/page.html
new file mode 100644
index 0000000..bcabff8
--- /dev/null
+++ b/templates/notes/page.html
@@ -0,0 +1,31 @@
+{% extends "base.html" %}
+
+{% block title %}{{ page.title }} | {{ config.title }}{% endblock title %}
+{% block description %}{{ page.description | default(value=config.description) }}{% endblock description %}
+
+{% block content %}
+<article class="site-container article-page">
+ <header class="article-header narrow">
+ <p class="article-kind"><a href="{{ get_url(path='@/notes/_index.md') }}">Notes</a></p>
+ <h1>{{ page.title }}</h1>
+ <dl class="article-dates">
+ <div><dt>公開</dt><dd><time datetime="{{ page.date }}">{{ page.date | date(format='%Y-%m-%d') }}</time></dd></div>
+ {% if page.updated %}<div><dt>更新</dt><dd><time datetime="{{ page.updated }}">{{ page.updated | date(format='%Y-%m-%d') }}</time></dd></div>{% endif %}
+ </dl>
+ {% if page.description %}<p class="article-description">{{ page.description }}</p>{% endif %}
+ </header>
+
+ {% if page.toc %}
+ <nav class="toc narrow" aria-label="目次">
+ <h2>目次</h2>
+ {{<toc.render headers={page.toc} />}}
+ </nav>
+ {% endif %}
+
+ <div class="prose">
+ {{ page.content | safe }}
+ </div>
+
+ {% include "partials/related-software.html" %}
+</article>
+{% endblock content %}
diff --git a/templates/notes/section.html b/templates/notes/section.html
new file mode 100644
index 0000000..4fc2675
--- /dev/null
+++ b/templates/notes/section.html
@@ -0,0 +1,31 @@
+{% extends "base.html" %}
+
+{% block title %}{{ section.title }} | {{ config.title }}{% endblock title %}
+{% block description %}{{ section.description }}{% endblock description %}
+
+{% block content %}
+<div class="site-container listing-page">
+ <header class="page-header">
+ <h1>{{ section.title }}</h1>
+ <p>{{ section.description }}</p>
+ </header>
+
+ {% if section.pages %}
+ <div class="archive">
+ {% for year, pages in section.pages | group_by(attribute="year") %}
+ <section class="archive-year" aria-labelledby="year-{{ year }}">
+ <h2 id="year-{{ year }}">{{ year }}</h2>
+ <div class="note-list">
+ {% for note in pages %}
+ {% set item = note %}
+ {% include "partials/note-list-item.html" %}
+ {% endfor %}
+ </div>
+ </section>
+ {% endfor %}
+ </div>
+ {% else %}
+ <p class="empty-state">Notesはまだありません。</p>
+ {% endif %}
+</div>
+{% endblock content %}
diff --git a/templates/page.html b/templates/page.html
new file mode 100644
index 0000000..cc0494e
--- /dev/null
+++ b/templates/page.html
@@ -0,0 +1,15 @@
+{% extends "base.html" %}
+
+{% block title %}{{ page.title }} | {{ config.title }}{% endblock title %}
+{% block description %}{{ page.description | default(value=config.description) }}{% endblock description %}
+
+{% block content %}
+<article class="site-container editorial-page">
+ <header class="page-header narrow">
+ <h1>{{ page.title }}</h1>
+ </header>
+ <div class="prose">
+ {{ page.content | safe }}
+ </div>
+</article>
+{% endblock content %}
diff --git a/templates/partials/footer.html b/templates/partials/footer.html
new file mode 100644
index 0000000..a04136f
--- /dev/null
+++ b/templates/partials/footer.html
@@ -0,0 +1,10 @@
+<footer class="site-footer">
+ <div class="site-container footer-inner">
+ <p>© {{ config.extra.copyright_year }} Takashi</p>
+ <nav aria-label="フッターナビゲーション">
+ <a href="{{ get_url(path='atom.xml', trailing_slash=false) }}">Atom</a>
+ <a href="{{ get_url(path='rss.xml', trailing_slash=false) }}">RSS</a>
+ <a href="mailto:{{ config.extra.mail }}">Mail</a>
+ </nav>
+ </div>
+</footer>
diff --git a/templates/partials/header.html b/templates/partials/header.html
new file mode 100644
index 0000000..dcd4ea2
--- /dev/null
+++ b/templates/partials/header.html
@@ -0,0 +1,19 @@
+{% set home_url = get_url(path='@/_index.md') %}
+{% set about_url = get_url(path='@/about.md') %}
+{% set software_url = get_url(path='@/software/_index.md') %}
+{% set notes_url = get_url(path='@/notes/_index.md') %}
+{% set active_path = current_path | default(value='') %}
+<header class="site-header">
+ <div class="site-container header-inner">
+ <a class="site-title" href="{{ home_url }}">{{ config.title }}</a>
+ <nav aria-label="メインナビゲーション">
+ <ul class="nav-list">
+ <li><a href="{{ home_url }}"{% if active_path == '/' %} aria-current="page"{% endif %}>Home</a></li>
+ <li><a href="{{ notes_url }}"{% if '/notes/' in active_path %} aria-current="page"{% endif %}>Notes</a></li>
+ <li><a href="{{ software_url }}"{% if '/software/' in active_path %} aria-current="page"{% endif %}>Software</a></li>
+ <li><a href="{{ about_url }}"{% if active_path == '/about/' %} aria-current="page"{% endif %}>About</a></li>
+ </ul>
+ </nav>
+ <button class="theme-toggle" type="button" data-theme-toggle aria-pressed="false" aria-label="ダークテーマに切り替える">Dark</button>
+ </div>
+</header>
diff --git a/templates/partials/note-list-item.html b/templates/partials/note-list-item.html
new file mode 100644
index 0000000..67ca8f7
--- /dev/null
+++ b/templates/partials/note-list-item.html
@@ -0,0 +1,14 @@
+<article class="note-list-item">
+ <time datetime="{{ item.date }}">{{ item.date | date(format='%Y-%m-%d') }}</time>
+ <div class="list-item-main">
+ <h3><a href="{{ item.permalink }}">{{ item.title }}</a></h3>
+ {% if item.description %}<p>{{ item.description }}</p>{% endif %}
+ {% if item.taxonomies.tags %}
+ <ul class="tag-list" aria-label="タグ">
+ {% for tag in item.taxonomies.tags %}
+ <li><a href="{{ get_taxonomy_url(kind='tags', term=tag) }}">{{ tag }}</a></li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+ </div>
+</article>
diff --git a/templates/partials/related-notes.html b/templates/partials/related-notes.html
new file mode 100644
index 0000000..59cca8b
--- /dev/null
+++ b/templates/partials/related-notes.html
@@ -0,0 +1,11 @@
+{% if page.extra.related_notes %}
+<aside class="related-content" aria-labelledby="related-notes-title">
+ <h2 id="related-notes-title">関連Notes</h2>
+ <ul>
+ {% for path in page.extra.related_notes %}
+ {% set related = get_page(path=path) %}
+ <li><a href="{{ related.permalink }}">{{ related.title }}</a>{% if related.description %}<span>{{ related.description }}</span>{% endif %}</li>
+ {% endfor %}
+ </ul>
+</aside>
+{% endif %}
diff --git a/templates/partials/related-software.html b/templates/partials/related-software.html
new file mode 100644
index 0000000..a29df4f
--- /dev/null
+++ b/templates/partials/related-software.html
@@ -0,0 +1,11 @@
+{% if page.extra.related_software %}
+<aside class="related-content" aria-labelledby="related-software-title">
+ <h2 id="related-software-title">関連Software</h2>
+ <ul>
+ {% for path in page.extra.related_software %}
+ {% set related = get_page(path=path) %}
+ <li><a href="{{ related.permalink }}">{{ related.title }}</a>{% if related.description %}<span>{{ related.description }}</span>{% endif %}</li>
+ {% endfor %}
+ </ul>
+</aside>
+{% endif %}
diff --git a/templates/partials/software-list-item.html b/templates/partials/software-list-item.html
new file mode 100644
index 0000000..e6c970e
--- /dev/null
+++ b/templates/partials/software-list-item.html
@@ -0,0 +1,4 @@
+<article class="software-list-item">
+ <h3><a href="{{ item.permalink }}">{{ item.title }}</a></h3>
+ {% if item.description %}<p>{{ item.description }}</p>{% endif %}
+</article>
diff --git a/templates/partials/toc.html b/templates/partials/toc.html
new file mode 100644
index 0000000..2089d15
--- /dev/null
+++ b/templates/partials/toc.html
@@ -0,0 +1,10 @@
+{% component toc.render(headers: array) %}
+<ul>
+ {% for header in headers %}
+ <li>
+ <a href="{{ header.permalink | safe }}">{{ header.title }}</a>
+ {% if header.children %}{{<toc.render headers={header.children} />}}{% endif %}
+ </li>
+ {% endfor %}
+</ul>
+{% endcomponent toc.render %}
diff --git a/templates/software/page.html b/templates/software/page.html
new file mode 100644
index 0000000..513aee9
--- /dev/null
+++ b/templates/software/page.html
@@ -0,0 +1,27 @@
+{% extends "base.html" %}
+
+{% block title %}{{ page.title }} | {{ config.title }}{% endblock title %}
+{% block description %}{{ page.description | default(value=config.description) }}{% endblock description %}
+
+{% block content %}
+<article class="site-container article-page software-page">
+ <header class="article-header narrow">
+ <p class="article-kind"><a href="{{ get_url(path='@/software/_index.md') }}">Software</a></p>
+ <h1>{{ page.title }}</h1>
+ {% if page.description %}<p class="article-description">{{ page.description }}</p>{% endif %}
+ </header>
+
+ <dl class="software-meta narrow">
+ {% if page.extra.language %}<div><dt>使用言語</dt><dd>{{ page.extra.language | join(sep=", ") }}</dd></div>{% endif %}
+ {% if page.extra.license %}<div><dt>ライセンス</dt><dd>{{ page.extra.license }}</dd></div>{% endif %}
+ <div><dt>Source</dt><dd><a class="external-link" href="{{ page.extra.source }}" rel="external">cgit</a></dd></div>
+ {% if page.extra.github_mirror %}<div><dt>Mirror</dt><dd><a class="external-link" href="{{ page.extra.github_mirror }}" rel="external">GitHub</a></dd></div>{% endif %}
+ </dl>
+
+ <div class="prose">
+ {{ page.content | safe }}
+ </div>
+
+ {% include "partials/related-notes.html" %}
+</article>
+{% endblock content %}
diff --git a/templates/software/section.html b/templates/software/section.html
new file mode 100644
index 0000000..813f793
--- /dev/null
+++ b/templates/software/section.html
@@ -0,0 +1,24 @@
+{% extends "base.html" %}
+
+{% block title %}{{ section.title }} | {{ config.title }}{% endblock title %}
+{% block description %}{{ section.description }}{% endblock description %}
+
+{% block content %}
+<div class="site-container listing-page">
+ <header class="page-header">
+ <h1>{{ section.title }}</h1>
+ <p>{{ section.description }}</p>
+ </header>
+ {% if section.pages %}
+ <div class="software-list software-list-full">
+ {% for software_item in section.pages %}
+ {% set item = software_item %}
+ {% include "partials/software-list-item.html" %}
+ {% endfor %}
+ </div>
+ {% else %}
+ <p class="empty-state">Softwareはまだありません。</p>
+ {% endif %}
+ <p class="source-note">ソースコードの正本は各Softwareから自前cgitへ案内します。</p>
+</div>
+{% endblock content %}
diff --git a/templates/tags/list.html b/templates/tags/list.html
new file mode 100644
index 0000000..6567506
--- /dev/null
+++ b/templates/tags/list.html
@@ -0,0 +1,18 @@
+{% extends "base.html" %}
+
+{% block title %}タグ | {{ config.title }}{% endblock title %}
+{% block description %}Notesを分類するタグの一覧。{% endblock description %}
+
+{% block content %}
+<div class="site-container listing-page taxonomy-page">
+ <header class="page-header">
+ <h1>タグ</h1>
+ <p>主題を横断してNotesを探すための索引です。</p>
+ </header>
+ <ul class="taxonomy-list">
+ {% for term in terms %}
+ <li><a href="{{ term.permalink }}">{{ term.name }}</a><span>{{ term.page_count }}件</span></li>
+ {% endfor %}
+ </ul>
+</div>
+{% endblock content %}
diff --git a/templates/tags/single.html b/templates/tags/single.html
new file mode 100644
index 0000000..2ec6fa1
--- /dev/null
+++ b/templates/tags/single.html
@@ -0,0 +1,20 @@
+{% extends "base.html" %}
+
+{% block title %}{{ term.name }} | タグ | {{ config.title }}{% endblock title %}
+{% block description %}タグ「{{ term.name }}」の記事一覧。{% endblock description %}
+
+{% block content %}
+<div class="site-container listing-page">
+ <header class="page-header">
+ <p class="article-kind"><a href="{{ get_url(path='tags', trailing_slash=true) }}">タグ</a></p>
+ <h1>{{ term.name }}</h1>
+ <p>{{ term.page_count }}件の記録</p>
+ </header>
+ <div class="note-list">
+ {% for page in term.pages %}
+ {% set item = page %}
+ {% include "partials/note-list-item.html" %}
+ {% endfor %}
+ </div>
+</div>
+{% endblock content %}
diff --git a/zola.toml b/zola.toml
new file mode 100644
index 0000000..7e74e4c
--- /dev/null
+++ b/zola.toml
@@ -0,0 +1,218 @@
+# Zola 0.23.3 configuration
+# https://www.getzola.org/documentation/getting-started/configuration/
+
+# -----------------------------------------------------------------------------
+# Main
+# -----------------------------------------------------------------------------
+
+# サイトの基準URL(唯一の必須項目)
+base_url = "https://www.takashi-2521.com"
+
+# サイト全体のメタデータ。標準feedでも使用される
+title = "たかしの記録"
+description = "日々考えたこととか思ったことを記録します"
+default_language = "ja"
+
+# 外部themeは使用しない。空文字でも指定扱いになるため未指定にする
+# theme = "my-theme"
+
+# build成果物の出力先
+output_dir = "public"
+
+# 再build時に出力先直下のdotfileを保持するか
+preserve_dotfiles_in_output = false
+
+# sass/内のSassをcompileする
+compile_sass = true
+
+# 生成HTMLをminifyするか
+minify_html = false
+
+# content/から除外するassetのglob
+ignored_content = []
+
+# content/内でTera処理を行わないfileのglob
+skip_content_templating = []
+
+# static/から除外するfileのglob
+ignored_static = []
+
+# サイト全体のAtom/RSS feedを生成する
+generate_feeds = true
+
+# sitemapから除外するpagination範囲("none"または"all")
+exclude_paginated_pages_in_sitemap = "none"
+
+# feedのfile名兼template名
+feed_filenames = ["atom.xml", "rss.xml"]
+
+# feedに含める記事数。未指定なら全件
+# feed_limit = 20
+
+# static/および併置assetをcopyではなくhard linkするか
+hard_link_static = false
+
+# Pageとfeedの既定author
+author = "Takashi"
+
+# Notesを分類するtags taxonomy
+taxonomies = [
+ { name = "tags", feed = false },
+]
+
+# taxonomy URLの共通prefix。未指定ならサイトroot
+# taxonomy_root = "blog"
+
+# 既定言語のsearch indexを生成するか
+build_search_index = false
+
+# Zola標準のsitemap.xmlを生成する
+generate_sitemap = true
+
+# Zola標準のrobots.txtを生成する
+generate_robots_txt = true
+
+# -----------------------------------------------------------------------------
+# Markdown
+# -----------------------------------------------------------------------------
+
+[markdown]
+
+# :smile:などのemoji aliasをUnicodeへ変換するか
+render_emoji = false
+
+# 外部linkへ付けるCSS class
+external_links_class = "external-link"
+
+# 外部linkを新しいtabで開くか
+external_links_target_blank = false
+
+# 外部linkへrel="nofollow"を付けるか
+external_links_no_follow = false
+
+# 外部linkへrel="noreferrer"を付けるか
+external_links_no_referrer = false
+
+# 外部linkへrel="external"を付けるか
+external_links_external = true
+
+# 引用符や三点リーダーなどを組版向け文字へ変換する
+smart_punctuation = false
+
+# definition list構文を有効にするか
+definition_list = false
+
+# 画像へdecoding="async"とloading="lazy"を付けるか
+lazy_async_image = true
+
+# footnoteを本文位置ではなく文末へまとめるか
+bottom_footnotes = true
+
+# GitHub形式のalert/calloutを有効にするか
+github_alerts = false
+
+# 見出しへanchor linkを挿入する位置("left"、"right"、"heading"、"none")
+insert_anchor_links = "heading"
+
+# -----------------------------------------------------------------------------
+# Markdown syntax highlighting
+# -----------------------------------------------------------------------------
+
+[markdown.highlighting]
+
+# 未対応言語を含むcode blockをerrorにする
+error_on_missing_language = true
+
+# inline styleではなくclassを出力し、厳格なCSPに対応する
+style = "class"
+
+# 単一theme使用時のtheme名。light/darkとの同時指定はできないため未指定にする
+# theme = "github-light"
+
+# OSのcolor schemeに対応する組み込みtheme
+light_theme = "github-light"
+dark_theme = "github-dark"
+
+# 追加するTextMate grammar/themeのJSON file
+extra_grammars = []
+extra_themes = []
+
+# code blockのdata属性を付ける要素("code"、"pre"、"both"、"none")
+data_attr_position = "code"
+
+# dual themeの生成CSSへcolor-schemeを設定するか
+add_color_scheme = true
+
+# -----------------------------------------------------------------------------
+# Link checker
+# -----------------------------------------------------------------------------
+
+[link_checker]
+
+# このprefixで始まる外部URLはlink確認を省略する
+skip_prefixes = [
+ "http://[2001:db8::]/",
+]
+
+# このprefixで始まる外部URLはanchor確認を省略する
+skip_anchor_prefixes = [
+ "https://caniuse.com/",
+]
+
+# 内部/外部link問題の扱い("error"または"warn")
+internal_level = "error"
+external_level = "warn"
+
+# -----------------------------------------------------------------------------
+# Slugification
+# -----------------------------------------------------------------------------
+
+[slugify]
+
+# path、taxonomy、anchorのslug化方式("on"、"safe"、"off")
+paths = "on"
+taxonomies = "on"
+anchors = "on"
+
+# path slug生成時にfile名先頭の日付を保持するか
+paths_keep_dates = false
+
+# -----------------------------------------------------------------------------
+# Search
+# build_search_index=falseのため現時点では出力されないが、全項目を明示する
+# -----------------------------------------------------------------------------
+
+[search]
+
+include_title = true
+include_description = false
+include_date = false
+include_path = false
+include_content = true
+
+# indexへ含める本文の最大code point数。未指定なら切り詰めない
+# truncate_content_length = 100
+
+# "elasticlunr_javascript"、"elasticlunr_json"、"fuse_javascript"、"fuse_json"
+index_format = "elasticlunr_javascript"
+
+# -----------------------------------------------------------------------------
+# Translations / Languages
+# -----------------------------------------------------------------------------
+
+# default_language向けの任意翻訳。現時点では使用しない
+[translations]
+
+# 追加言語の設定。単一言語サイトのため現時点では使用しない
+[languages]
+
+# -----------------------------------------------------------------------------
+# Custom site metadata
+# -----------------------------------------------------------------------------
+
+[extra]
+
+home_notes_limit = 5
+home_software_limit = 4
+copyright_year = 2026
+mail = "takashi@takashi-2521.com"