Hugo博客

hugo-kraft-masonry — Hugo 博客模板

作者 biccio

Kraft Masonry 是一个基于 Hugo 的极简个人博客主题,首页使用 CSS Grid 砌体布局并支持客户端分类筛选,无 npm、Sass 或外部 CDN 依赖。

看看你的网站会是什么样

浏览真实页面截图,比较内容排版与手机效果。点击下方缩略图切换,放大查看整页细节。

1 个预览视图
作者提供的预览
作者提供的预览

来自项目 README 的原始预览。

目前有 1 张原始预览,可打开完整图片查看设计细节。

截图用于了解视觉与布局,交互效果请以实际项目为准。

采集于 2026-10-12查看来源

值得关注的模板亮点

从已有项目资料中,快速了解它的特点。

01

首页使用 CSS Grid 砌体布局,并通过 grid-auto-flow: dense 自动填补空隙

02

部分卡片横跨两列形成 bento 式宽卡,宽卡在 ≥760px 时图文并排显示

03

纯客户端分类筛选,点击分类按钮即时过滤卡片,无需页面刷新

04

内置浅色/深色模式,支持系统偏好检测和手动切换,选择保存到 localStorage

05

无 npm、Sass、外部字体或 CDN 依赖,CSS 与 JS 通过 Hugo Pipes 处理

06

提供 {{< img >}} 短代码,支持 left/right/center/full 对齐、说明文字和宽度覆盖

模板详细介绍

Kraft Masonry 是面向个人博客的 Hugo 主题,首页采用 CSS Grid 砌体布局,通过 `grid-auto-flow: dense` 自动填补空隙,并让部分卡片横跨两列形成 bento 式宽卡。首页顶部包含站点标题、支持 Markdown 的多行副标题以及纯客户端分类筛选栏,文章卡片按日期倒序排列,数量由 `homepagePostLimit` 控制。单篇文章为标题加内容的经典布局,底部提供可点击的标签列表。主题内置浅色与深色模式,支持系统偏好检测和手动切换,并可将选择持久化到 localStorage。

主题不依赖 npm、Sass、外部字体或 CDN,CSS 与 JS 全部通过 Hugo Pipes 处理。它提供 `{{< img >}}` 短代码用于正文内联图片,支持 left、right、center、full 对齐以及可选说明文字和宽度覆盖;浮动图片在移动端会恢复为全宽。结构化数据方面,首页始终输出 WebSite 实体,可选通过 `params.website_ld` 启用站点链接搜索框,通过 `params.person_ld` 输出 Person 实体,每篇文章也可通过 front matter 中的 `schema` 块输出 JSON-LD。评论功能为可选,需配置 EchoThread 的 API key 才会在文章底部显示。

主题要求 Hugo 0.143.0 或更高版本,推荐使用 extended 版本。安装方式支持 Git submodule 或直接复制主题文件夹。exampleSite 目录包含一个可直接运行的示例站点,内含三篇分类(Viaggi、Tecnologia、Vita quotidiana)下的六篇示例文章和多个标签。界面字符串提供意大利语和英语文件,可通过 i18n 目录扩展。

使用限制方面:首页分类筛选完全在客户端进行,不涉及服务端查询;宽卡片的出现频率默认由标题长度、永久链接长度和日期年积日计算决定,也可在 front matter 中用 `featured` 强制指定;卡片摘要最终会被截断为普通卡 200 字符、宽卡 240 字符。演示图片、字体、图标等第三方素材的许可需单独核对,代码本身的 MIT 许可不覆盖这些素材。

Hugo博客

适合你的项目吗?

适用场景

  • 搭建以图文卡片流展示文章的个人博客首页
  • 需要按分类快速筛选文章列表,且不希望每次筛选都刷新页面
  • 希望博客同时提供浅色和深色外观,并跟随系统偏好
  • 在文章正文中插入带对齐方式和说明文字的内联图片
  • 为博客首页或文章添加 JSON-LD 结构化数据以增强搜索结果展示

适合人群

  • 使用 Hugo 构建个人博客的开发者或写作者
  • 偏好无 npm、无外部 CDN 依赖的静态站点构建流程
  • 需要砌体式卡片首页和客户端分类筛选的博客项目
  • 希望界面字符串支持意大利语或英语,并可按需扩展其他语言

上手与定制指南

结合仓库当前版本的说明,按下面的资料检查内容、配置与运行要求。

  1. 1

    需要 Hugo 0.143.0 或更高版本,推荐使用 extended 版本。

  2. 2

    可通过 Git submodule 将主题添加到 themes/hugo-kraft-masonry,或在 hugo.toml 中设置 theme 参数。

  3. 3

    exampleSite 目录包含可直接运行的示例站点,复制其内容后运行 hugo server 即可预览。

  4. 4

    评论功能默认关闭,需在 params.echothread.apiKey 中配置 EchoThread API key 才会显示。

  5. 5

    演示图片、字体、图标等第三方素材需按各自许可单独核对,代码的 MIT 许可不覆盖这些素材。

查看仓库使用说明

授权与来源资料

使用条件与限制

免费使用 · 保留许可声明

本次收录的是公开仓库中采用 MIT 许可的免费版本;免费范围为该版本代码,不代表外部服务或第三方素材免费。

使用时需要遵守

  • 允许免费使用、修改、商用和分发该版本代码。
  • 分发代码副本或实质性部分时,保留原作者版权声明和完整 MIT 许可声明。
  • 代码按现状提供;担保与责任限制以原许可证及适用法律为准。

额外费用与待核对事项

  • 演示图片、字体、图标及其他第三方素材需按各自许可核对,不能仅凭代码的 MIT 许可判断。
  • 域名、托管、数据库、支付或 AI 等外部服务可能另外收费。
  • 评论功能为可选,需配置 EchoThread 的 API key 才能启用;未设置时不会输出任何评论 HTML。
  • Google Analytics 为可选,需提供 GA4 测量 ID;仅在 production 构建时注入 gtag.js,本地预览不发送流量。
  • 首页分类筛选不更新 URL,筛选后的视图无法通过链接分享,也不会被搜索引擎索引。
  • `/posts/` 没有内置分页;文章数量增长时需自行在 list.html 中添加 Hugo 的 `.Paginate`。
  • 标题字体依赖系统字体;在 Linux 上这些字体通常未安装,会回退到通用无衬线字体,跨平台外观可能不一致。
  • 卡片摘要会被截断:普通卡 200 字符,宽卡(featured)240 字符,即使 summary 字段更长也会被截断。
  • 使用 `grid-auto-flow: dense` 时,较晚的文章可能在视觉上先于较早的文章显示;阅读与 Tab 顺序仍按标记(日期顺序)。
查看原文依据

Permission is hereby granted, free of charge

许可证原文

**EchoThread comments** — opt-in comment section per post; set `params.echothread.apiKey` to enable

README 原文

`params.googleAnalytics` | — | GA4 measurement ID (e.g. `G-XXXXXXXXXX`). When set, the `gtag.js` snippet is injected in `<head>` — only on production builds

README 原文

The homepage category filter does not update the URL, so a filtered view is not shareable via link and not indexed by search engines.

README 原文

No built-in pagination on `/posts/`. If your post count grows, add Hugo's standard `.Paginate` to `layouts/_default/list.html`.

README 原文

`--font-title` relies on system fonts. On Linux, "Avenir Next", "Century Gothic", and "Futura" are rarely installed and the stack falls back to a generic sans-serif.

README 原文

the theme truncates the final string to **200 characters** for normal cards and **240 characters** for wide (`featured`) cards.

README 原文

to fill gaps left by wide cards, the browser may display a later post visually before an earlier one. Reading / tab order always follows the markup (date order).

README 原文
https://github.com/biccio/hugo-kraft-masonry/blob/main/LICENSEhttps://github.com/biccio/hugo-kraft-masonry/blob/main/README.md

DeepSeek 根据仓库许可证和 README 整理 ·2026-10-12。结果仅覆盖已读取资料,依赖、图片、字体及后续条款变更以各自原文为准。

项目核对记录与介绍依据

模板资料

来源平台
GitHub
资料与维护参考分
资料一般 · 68 / 100
技术栈
Hugo
网站类型
博客
这个分数如何计算

本站按许可资料(最高30分)、演示链接(12分)、预览图(10分)、Star数(15分)、记录的维护时间(15分)、描述完整度(8分)、功能记录(5分)和分类资料(5分)计算。它不是代码、安全或性能测试,也不是 Google 评分;有链接不代表已实测可用。

这个项目需要核对什么

记录的仓库更新时间
2026-06-30
截图检查日期
2026-10-12

目前没有记录演示链接。请依据仓库说明在本地评估;仅凭截图无法确认交互行为。

目录记录的主要技术栈是 Hugo。请通过依赖清单和锁文件核对实际版本与托管要求。这些说明基于收录资料,不代表代码审计或部署实测。

查看具体选型方法 →

介绍的制作与核对状态

介绍由 AI 辅助整理目录与仓库资料。仓库作者不等于这篇介绍的审核者。

审核声明: [email protected] · 2026-10-13

查看当时使用的来源资料

SHA-256: ed8ab39fdfd0d9da3d428b31c12c1449f7136a57176dc2561e68f555ed02f5ac

2026-10-12T08:56:19.323Z

来源文件 / 仓库
# Kraft Masonry

A minimal Hugo theme for personal blogs.

**Homepage**: site title in the top-left header, a hero area with one or more subtitle lines (markdown formatting supported), a client-side category filter, and a CSS Grid masonry with automatic bento-style tiling — some cards span two columns and display their image and text side by side. **Single posts**: classic title-and-content layout with a clickable tag list at the bottom. Full **light / dark mode** support with system detection and a manual toggle.

No npm, no Sass, no external fonts or CDN dependencies. CSS and JS are processed entirely through Hugo Pipes.

---

## Screenshots

![Homepage](img/screenshot-home.png)
***
![Single post](img/screenshot-single.png)

---

## Features

- **Masonry homepage** — CSS Grid with `grid-auto-flow: dense` for automatic gap filling
- **Bento-style wide cards** — selected cards span 2 columns with image and text side by side
- **Client-side category filter** — instant filtering by category, no page reload
- **Tag taxonomy** — tag list at the bottom of each post, clickable tag cloud at `/tags/`
- **Light / dark mode** — system preference detection + manual toggle persisted to `localStorage`
- **Responsive** — 3 / 2 / 1 column breakpoints, with an optional toggle to hide the hero on mobile
- **Inline image shortcode** — `{{< img >}}` with `left` / `right` / `center` / `full` alignment, optional caption and width override; floats collapse gracefully on mobile
- **Structured data** — optional JSON-LD (schema.org) per post, from a `schema` front-matter block; `WebSite` entity always emitted on the homepage (enables Sitelinks Search Box via `params.website_ld`); optional `Person` entity via `params.person_ld`
- **EchoThread comments** — opt-in comment section per post; set `params.echothread.apiKey` to enable
- **No external dependencies** — system font stacks, Hugo Pipes for CSS/JS minification and fingerprinting
- **i18n ready** — Italian and English string files included; add more via `i18n/`

---

## Requirements

Hugo **0.143.0** or later (extended edition recommended for future Sass support).

---

## Installation

### Option A — Git submodule (recommended)

```bash
cd your-site
git submodule add https://github.com/YOUR_USERNAME/hugo-kraft-masonry.git themes/hugo-kraft-masonry
```

Then in your `hugo.toml`:

```toml
theme = "hugo-kraft-masonry"
```

### Option B — Direct copy

Copy the `hugo-kraft-masonry/` folder (excluding `exampleSite/`) into your site's `themes/` directory.

### Try the demo site

The `exampleSite/` directory contains a ready-to-run site with six sample posts across three categories ("Viaggi", "Tecnologia", "Vita quotidiana") and multiple tags.

```bash
mkdir my-site && cd my-site
git submodule add https://github.com/YOUR_USERNAME/hugo-kraft-masonry.git themes/hugo-kraft-masonry
cp -r themes/hugo-kraft-masonry/exampleSite/* .
hugo server
```

---

## Configuration

A minimal `hugo.toml`:

```toml
baseURL  = "https://example.com/"
title    = "My Blog"
theme    = "hugo-kraft-masonry"

# i18n: set to "it" for Italian UI strings, "en" for English
defaultContentLanguage = "en"

[taxonomies]
  category = "categories"
  tag      = "tags"

[params]
  description = "A personal blog about travel, tech, and everyday life."
  author      = "Your Name"

  [params.hero]
    subtitle = [
      "A **minimal** Hugo theme for personal blogs.",
      "Masonry grid · Category filter · Tags · Light & dark mode.",
      "Free and open source — [MIT licence](https://github.com/YOUR_USERNAME/hugo-kraft-masonry/blob/main/LICENSE)."
    ]
```

### All parameters

| Parameter | Default | Description |
|---|---|---|
| `params.hero.subtitle` | — | Array of strings rendered below the site header as the homepage hero. Each element is a separate line, processed through Hugo's `markdownify` — inline Markdown is fully supported: `**bold**`, `*italic*`, `[link](url)`, and raw HTML (requires `markup.goldmark.renderer.unsafe = true`). The hero is **not shown** if this key is absent. |
| `params.disableDescriptionMobile` | `false` | When `true`, hides the homepage hero / intro block (the `hero.subtitle` lines) on viewports ≤ 640px. The hero stays visible on tablet and desktop. Handy for surfacing the post grid immediately on small screens. Implemented by adding the `hero--hide-mobile` class to the hero in `layouts/index.html`, which a `@media (max-width: 640px)` rule in `main.css` sets to `display: none`. |
| `params.description` | — | Fallback meta description for pages that don't define their own. |
| `params.author` | site `title` | Displayed in the right footer span when `params.footer.right` is not set. |
| `params.footer.left` | `© <year> <site title>` | Left span of the footer. Accepts inline Markdown (`**bold**`, `[link](url)`, HTML entities). Rendered with Hugo's `markdownify`. |
| `params.footer.right` | i18n `builtWith` + `params.author` | Right span of the footer. Accepts inline Markdown. Rendered with Hugo's `markdownify`. |
| `params.dateFormat` | `:date_long` | Layout for the **visible** post date, rendered with Hugo's locale-aware `time.Format`. Use a predefined token — `:date_full`, `:date_long` (e.g. *June 19, 2026* / *19 giugno 2026*), `:date_medium`, `:date_short` — or a custom Go layout, e.g. `02/01/2006` → *19/06/2026* (numeric, day-first). Month and weekday names are localized to the page language; the `<time datetime>` attribute always stays ISO 8601. For per-language formats, set it under `[languages.xx.params]`. |
| `params.mainSections` | `["posts"]` | Content sections treated as "posts" for the homepage masonry grid. |
| `params.homepagePostLimit` | `12` | Maximum number of posts shown in the homepage grid, sorted by date descending. |
| `params.enableCategoryFilter` | `true` | Show / hide the category filter bar and its JS on the homepage. |
| `params.wideCardEvery` | `4` | Controls wide-card frequency when `featured` is not set in front matter. A card is made wide when `(len(title) + len(permalink) + date.YearDay) mod wideCardEvery == 0`. Default: roughly 1 in 4 cards. |
| `params.googleAnalytics` | — | GA4 measurement ID (e.g. `G-XXXXXXXXXX`). When set, the `gtag.js` snippet is injected in `<head>` — only on production builds (`hugo --environment production` / `HUGO_ENV=production`), so local/preview traffic isn't tracked. |
| `params.echothread.apiKey` | — | EchoThread API key from your dashboard. When set, the comment widget is rendered at the bottom of every post. Leave unset to disable comments entirely. |
| `params.echothread.shortname` | — | Your EchoThread shortname (the `data-shortname` attribute on the widget). |
| `params.echothread.theme` | `auto` | Widget colour scheme: `auto` (follows OS preference), `light`, or `dark`. |
| `params.echothread.accentColor` | — | Optional hex colour for the widget accent (e.g. `#2f5d50` to match the theme default). |
| `params.website_ld.search_url_template` | — | Enables Sitelinks Search Box rich result. Must contain `{search_term_string}`. See [Homepage WebSite structured data](#homepage-website-structured-data). |
| `params.person_ld.*` | — | Person JSON-LD for the homepage. See [Homepage Person structured data](#homepage-person-structured-data) for all sub-keys. |

---

## Post front matter

```yaml
---
title: "My Post Title"
date: 2026-06-01
categories: ["travel"]     # one or more categories
tags: ["gravel", "italy"]  # one or more tags
image: "https://..."       # cover image URL (optional)
featured: true             # force this card to be wide (optional)
summary: "A hand-written summary shown in the card instead of the auto-generated one."  # optional
---

Opening paragraph — used as the card abstract when no summary field is set.

<!--more-->

Rest of the post body.
```

### Cover image

Two lookup strategies, in priority order:

1. **Page bundle resource**: if the post is a [page bundle](https://gohugo.io/content-management/page-bundles/) and contains a file whose name starts with `cover`, `feature`, or `thumbnail` (e.g. `cover.jpg`), it is used automatically.
2. **`image` front matter parameter**: an absolute URL or site-root path.

If neither is present the card is shown without an image (text adjusts automatically).

### Abstract

The card abstract comes from Hugo's `.Summary`, processed through three fallback strategies in priority order:

1. **`summary` front matter field** — write the abstract directly in the post header. Hugo uses this verbatim, giving you full control over wording without touching the post body:
   ```yaml
   summary: "A concise, hand-crafted description shown in the homepage card."
   ```
2. **`<!--more-->` marker** — everything before the marker becomes the summary. The marker also controls where Hugo truncates the post on list pages.
3. **Automatic** — Hugo takes the first ~70 words of the body if neither of the above is present.

Regardless of the source, the theme truncates the final string to **200 characters** for normal cards and **240 characters** for wide (`featured`) cards. This means a long `summary` field or a lengthy `<!--more-->` section will still be cut off at those limits in the card — they only affect what text is available before truncation, not the truncation itself.

---

## Inline images

Use the `{{< img >}}` shortcode to insert images anywhere in the body of a post with explicit alignment. Hugo's standard Markdown `![]()` syntax still works and produces a centred block image, but the shortcode adds float support and captions.

```
{{< img src="photo.jpg" alt="Alt text" align="left" caption="Optional caption" >}}
{{< img src="/images/map.png" alt="Map" align="right" width="280px" >}}
{{< img src="panorama.jpg" alt="Landscape" align="full" >}}
```

| Parameter | Default | Description |
|---|---|---|
| `src` | *(required)* | Path or URL of the image. |
| `alt` | `""` | Alternative text for accessibility. |
| `align` | `center` | `left` — float left, text wraps right. `right` — float right, text wraps left. `center` — centred block. `full` — 100 % content width. |
| `caption` | — | Optional caption rendered in `<figcaption>` (monospace, muted, centred). |
| `width` | — | Explicit width override, e.g. `300px` or `50%`. Overrides the default 42 % float width. |

Floating images (`left` / `right`) are 42 % wide (max 420 px) on desktop and revert to full-width on viewports ≤ 600 px. To stop text wrapping after a float, insert an empty paragraph with the `img-clear` class:

```html
<div class="img-clear"></div>
```

---

## Wide cards (bento tiling)

Cards with the `post-card--wide` class span 2 grid columns. On screens ≥ 760 px the image and text are displayed side by side; below that breakpoint (or on single-column mobile) they stack normally.

**Which cards are wide?** Decided per post in this order:

1. **Front matter override** (explicit, stable):
   ```yaml
   featured: true   # always wide
   featured: false  # never wide
   ```
2. **Deterministic pseudo-random** (when `featured` is not set): computed from `len(title) + len(permalink) + date.YearDay` modulo `params.wideCardEvery` (default `4`). The result is stable across rebuilds — layout does not shift between deploys.

> **Note on `grid-auto-flow: dense`**: to fill gaps left by wide cards, the browser may display a later post visually before an earlier one. Reading / tab order always follows the markup (date order). If you want strict visual order, remove `dense` from the masonry rule in `assets/css/main.css` — gaps will remain empty instead.

---

## Category filter

The filter bar above the grid is purely **client-side** (no page reload):

1. `partials/category-filter.html` renders one button per category in `.Site.Taxonomies.categories`, with `data-filter="<slug>"`.
2. `partials/post-card.html` adds `data-categories="slug1 slug2 ..."` to each card.
3. `assets/js/filter.js` listens for clicks and toggles the `hidden` attribute on non-matching cards.

Slugs are generated with Hugo's `urlize` function on both sides, so mixed-case category names always match correctly.

Without JS the filter bar is visible but inert — all posts remain shown. For JS-free category browsing the dedicated `/categories/<slug>/` pages (linked from card tags and from `/categories/`) remain available.

---

## Tag taxonomy

With `[taxonomies] tag = "tags"` in `hugo.toml`, Hugo automatically generates:

- **`/tags/`** — alphabetical tag cloud with post counts.
- **`/tags/<slug>/`** — masonry grid of all posts carrying that tag, with a `# Tag Name · N posts` header.

Tags are linked from the footer of each single post (shown only when `tags` is defined in front matter and the page type is not `page`). Hovering a tag pill fills it with the accent colour — the same interaction as the active category filter button.

---

## Footer

The footer has two configurable spans — left and right — set via `[params.footer]` in `hugo.toml`. Both accept inline Markdown and are rendered with Hugo's `markdownify`.

```toml
[params.footer]
  left  = "&copy; 2026 My Blog"
  right = "Built with [Hugo](https://gohugo.io) &amp; **Kraft Masonry**"
```

If either key is omitted, the theme falls back to its default:
- **left** — `© <current year> <site title>` (auto-updated each build)
- **right** — the i18n `builtWith` string followed by `params.author` (or the site title if `author` is not set)

---

## Google Analytics

Set `params.googleAnalytics` in `hugo.toml` to your GA4 measurement ID to enable tracking:

```toml
[params]
  googleAnalytics = "G-XXXXXXXXXX"
```

The `gtag.js` snippet is injected in `<head>` (see `layouts/partials/google-analytics.html`) only when building with `hugo --environment production` or `HUGO_ENV=production`, so `hugo server` and preview builds don't send traffic. Leave the parameter unset to disable analytics entirely.

---

## Comments (EchoThread)

[EchoThread](https://echothread.io) is a privacy-first, lightweight comment service (no ads, no tracking, < 15 KB). Set `params.echothread.apiKey` in `hugo.toml` to render the widget at the bottom of every post:

```toml
[params.echothread]
  apiKey      = "YOUR_API_KEY"   # from echothread.io dashboard (required)
  shortname   = "yourshortname"  # your EchoThread shortname (required)
  theme       = "auto"           # auto | light | dark (default: auto)
  accentColor = "#2f5d50"        # optional — match your brand colour
```

Leave the block absent (or omit `apiKey`) to disable comments entirely — no HTML is emitted.

Each post's comment thread is identified by `File.UniqueID` (an MD5 of the source file path), so threads survive URL changes. `data-page-url` and `data-page-title` are passed automatically for the dashboard view. The widget language follows the page's Hugo language code (`data-lang`).

---

## Structured data (JSON-LD)

Any post can carry an optional `schema` front-matter block, which the theme renders as a `<script type="application/ld+json">` (schema.org) in `<head>` — improving SEO and rich-result eligibility. Page-derived fields (headline, dates, URL, cover image, word count, language) are filled in automatically; the block adds the semantic metadata. Front-matter `type` keys map to the JSON-LD `@type`, and omitted fields fall back to site values (`params.author`, the site title as publisher, the site language). Pages without a `schema` block emit nothing.

```yaml
schema:
  type: "BlogPosting"            # JSON-LD @type (default: BlogPosting)
  inLanguage: "en-us"            # default: site language
  articleSection: "Technology"   # default: first category
  author:
    name: "Jane Doe"
    url: "https://example.com/about/"
  publisher:
    name: "My Blog"
    url: "https://example.com/"
  keywords: ["hugo", "performance"]   # default: post tags
  about:                          # entities the post is about
    - type: "SoftwareApplication"
      name: "Hugo"
      sameAs: "https://gohugo.io/"
  mentions:                       # entities merely mentioned
    - type: "Person"
      name: "Ada Lovelace"
      sameAs: "https://en.wikipedia.org/wiki/Ada_Lovelace"
```

The JSON is assembled with Hugo's `dict`/`slice` and serialised with `jsonify`, so quoting and escaping are always valid. See `layouts/partials/schema.html`, and validate output with Google's [Rich Results Test](https://search.google.com/test/rich-results) or [Schema Markup Validator](https://validator.schema.org/).

---

## Homepage WebSite structured data

A `schema.org/WebSite` `<script type="application/ld+json">` block is **always emitted** on the homepage using the site title and `baseURL` — no configuration required. This is the type that Google's Rich Results Test detects on the homepage.

Optionally, add a `SearchAction` (Sitelinks Search Box) by setting `search_url_template`. The URL must point to a real search endpoint on your site and must contain the literal placeholder `{search_term_string}`:

```toml
[params.website_ld]
  search_url_template = "https://example.org/?q={search_term_string}"
```

The resulting JSON-LD:

```json
{
  "@context": "https://schema.org",
  "@type": "WebSite",
  "name": "My Blog",
  "url": "https://example.org/",
  "potentialAction": {
    "@type": "SearchAction",
    "target": {
      "@type": "EntryPoint",
      "urlTemplate": "https://example.org/?q={search_term_string}"
    },
    "query-input": "required name=search_term_string"
  }
}
```

Google may show a search box directly in search results (Sitelinks Search Box) if it decides the site qualifies. Leave `search_url_template` absent to omit the `potentialAction`.

> **Note:** `schema.org/Person` (via `params.person_ld`) is indexed by Google and contributes to the Knowledge Graph, but is not a rich-result type — it will not appear as a "detected item" in the Rich Results Test. The `WebSite` block above is what makes the test show a detected item on the homepage.

---

## Homepage Person structured data

Set `[params.person_ld]` in `hugo.toml` to emit a `schema.org/Person` `<script type="application/ld+json">` block in the `<head>` of the **homepage only**. All fields are optional — only those present are included in the output. The `address` sub-object is omitted when no `address_*` key is set.

```toml
[params.person_ld]
  person_name            = "John Doe"
  person_family_name     = "Doe"
  person_given_name      = "John"
  person_image           = "https://example.org/john_doe.webp"
  person_telephone       = "+491231234567"
  person_email           = "[email protected]"
  person_url             = "https://john_doe.example.org/"
  person_vatid           = "DE123456789"
  person_knows_language  = ["de-DE", "en-US"]
  address_street         = "Am Hauptbahnhof"
  address_postal_code    = "60329"
  address_locality       = "Frankfurt am Main"
  address_region         = "Hesse"
  address_country        = "DE"
```

| TOML key | schema.org property | Notes |
|---|---|---|
| `person_name` | `name` | Full display name. |
| `person_family_name` | `familyName` | |
| `person_given_name` | `givenName` | |
| `person_image` | `image` | Absolute URL of the person's photo. |
| `person_telephone` | `telephone` | Include country code, e.g. `+491231234567`. |
| `person_email` | `email` | |
| `person_url` | `url` | Canonical URL of the person's website or profile. |
| `person_vatid` | `vatID` | VAT identification number. |
| `person_knows_language` | `knowsLanguage` | Array of BCP 47 language tags, e.g. `["de-DE", "en-US"]`. |
| `address_street` | `address.streetAddress` | Implies a `PostalAddress` sub-object. |
| `address_postal_code` | `address.postalCode` | |
| `address_locality` | `address.addressLocality` | City / town. |
| `address_region` | `address.addressRegion` | State, county, or region. |
| `address_country` | `address.addressCountry` | ISO 3166-1 alpha-2 country code, e.g. `DE`. |

The object is assembled with Hugo's `dict` and serialised with `jsonify` (same approach as the per-post `schema.html`). Validate the output with Google's [Rich Results Test](https://search.google.com/test/rich-results) or [Schema Markup Validator](https://validator.schema.org/). Leave the block absent to disable homepage structured data entirely.

---

## Light / dark mode

- On load, an **inline blocking script** in `<head>` (`partials/theme-init.html`) sets `data-theme="light"` or `"dark"` on `<html>` before the first paint, reading `localStorage` first, then `prefers-color-scheme`. No flash.
- The **sun / moon toggle button** in the header (SVG icons, no icon font) flips `data-theme` and persists the choice to `localStorage`.
- **Without JS**, a `@media (prefers-color-scheme: dark)` rule applies the dark palette automatically when the OS is in dark mode.

Both themes are defined as CSS custom properties:

```css
:root                    { /* light theme */ }
:root[data-theme="dark"] { /* dark theme  */ }
```

---

## Typography & design tokens

All colours, fonts, and key dimensions are defined as CSS custom properties in `assets/css/main.css`. Override any of them without touching the rest of the stylesheet:

```css
:root {
  /* Colours — light theme */
  --color-bg:         #ffffff;
  --color-surface:    #f7f6f2;
  --color-ink:        #232017;
  --color-muted:      #837a64;
  --color-accent:     #2f5d50;
  --color-on-accent:  #fbf8f1;
  --color-line:       #e4dfd1;

  /* Font stacks */
  --font-title:   "Avenir Next", "Century Gothic", "Futura", "Trebuchet MS", sans-serif;
  --font-display: "Iowan Old Style", "Palatino Linotype", "Source Serif Pro", Georgia, serif;
  --font-body:    -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  --font-mono:    "IBM Plex Mono", "SF Mono", "Roboto Mono", Consolas, monospace;
}
```

Three intentionally different typefaces:
- **`--font-title`** (geometric sans) — site logo in the header
- **`--font-display`** (serif) — post titles in cards and single pages
- **`--font-body`** (system sans) — body text

To use a self-hosted custom font (e.g. for GDPR / privacy reasons), add `@font-face` rules at the top of `main.css` and update the relevant `--font-*` variable.

---

## File structure

```
hugo-kraft-masonry/
├── archetypes/
│   └── default.md              # default front matter for "hugo new"
├── assets/
│   ├── css/main.css            # all styles (design tokens in :root)
│   └── js/
│       ├── filter.js           # client-side category filter
│       └── theme-toggle.js     # light/dark toggle
├── i18n/
│   ├── en.toml                 # English UI strings
│   └── it.toml                 # Italian UI strings
├── layouts/
│   ├── 404.html
│   ├── index.html              # homepage: hero + filter + masonry
│   ├── _default/
│   │   ├── baseof.html         # shared HTML skeleton
│   │   ├── list.html           # sections, category pages, tag pages
│   │   └── single.html         # classic post layout + tag footer
│   ├── shortcodes/
│   │   └── img.html            # inline image with alignment (left/right/center/full)
│   └── partials/
│       ├── category-filter.html
│       ├── comments-echothread.html  # EchoThread widget (params.echothread.apiKey)
│       ├── footer.html
│       ├── google-analytics.html  # GA4 snippet (params.googleAnalytics, production only)
│       ├── head.html
│       ├── header.html         # site logo + nav + light/dark toggle
│       ├── post-card.html      # masonry card (normal + wide)
│       ├── schema.html         # JSON-LD structured data (per post)
│       ├── schema-homepage.html  # JSON-LD Person data (homepage only, params.person_ld)
│       ├── schema-website.html   # JSON-LD WebSite data (homepage always, SearchAction via params.website_ld)
│       └── theme-init.html     # inline blocking script (no flash)
├── exampleSite/                # demo site with sample posts
├── theme.toml
├── LICENSE
└── README.md
```

---

## Netlify deployment

`exampleSite/netlify.toml` provides a ready-to-use deployment configuration:

```toml
[build]
  publish = "public"
  command = "hugo --gc --minify"

[build.environment]
  HUGO_VERSION = "0.143.0"

[context.production.environment]
  HUGO_ENV = "production"

[context.deploy-preview]
  command = "hugo --gc --minify --buildFuture -b $DEPLOY_PRIME_URL"
```

Copy it to your site root alongside `hugo.toml`.

---

## i18n

The theme ships with English (`en.toml`) and Italian (`it.toml`) UI strings (navigation labels, filter text, tag counts, etc.). Set `defaultContentLanguage` in `hugo.toml` to select the active locale. To add a new language, create `i18n/<code>.toml` with the same keys.

---

## Known limitations

- The homepage category filter does not update the URL, so a filtered view is not shareable via link and not indexed by search engines. Dedicated `/categories/<slug>/` pages remain available for that.
- No built-in pagination on `/posts/`. If your post count grows, add Hugo's standard `.Paginate` to `layouts/_default/list.html`.
- `--font-title` relies on system fonts. On Linux, "Avenir Next", "Century Gothic", and "Futura" are rarely installed and the stack falls back to a generic sans-serif. For a consistent cross-platform appearance, self-host a geometric sans (e.g. [Jost](https://fonts.google.com/specimen/Jost), [DM Sans](https://fonts.google.com/specimen/DM+Sans)) and update `--font-title`.

---

## License

MIT — see [LICENSE](LICENSE).
反馈内容错误

看看同类模板

对比同技术栈与用途的模板,找到更适合的视觉与内容结构。