# Taxonomies

> Give pages a second index that cuts across the directory tree with tags and categories — term pages, term cards, the rail cloud and the navbar panel are all automatic.

---

LLMS index: [llms.txt](/llms.txt)

---

A directory tree gives a page one path; a taxonomy gives it a second. The same
PostgreSQL backup document sits under an "Operations" directory and is also
reachable from a "backup" tag page. Enabling it needs only Hugo's `taxonomies:`
configuration: the term pages, term cards, rail cloud and navbar panel are all
generated by the theme, with no template to write.

This page carries a category. The "Categories: Customization" line under the
title, and the counted chips under the outline in the right column, need no
configuration on the page itself.

## Enabling a taxonomy {#enable}

Taxonomies are Hugo's, and the theme adds no switch of its own. Write
`taxonomies:` at the **top level** of `hugo.yml`, with the singular name as the
key and the plural as the value:

```yaml {title="hugo.yml"}
taxonomies:
  tag: tags
  category: categories
```

That is this site's configuration. Three things to note:

- Writing `taxonomies:` makes it the **complete list**, not an addition. To keep `tags` / `categories` alongside a custom taxonomy, list them too.
- The plural is also the URL segment: `/tags/`, `/categories/`.
- To turn them all off: `disableKinds: [taxonomy, term]`.

Adding one of your own, for instance grouping by product module:

```yaml {title="hugo.yml"}
taxonomies:
  tag: tags
  category: categories
  module: modules
```

Display names: the six keys `tag`, `tags`, `category`, `categories`, `module`,
`modules` have a localized title in every one of the theme's language files. Any
other taxonomy uses the humanized plural (`products` → `Products`). To name one
yourself, write `title` / `linkTitle` in `content/<plural>/_index.md` and
`_index.zh.md`, and the theme prefers it:

```yaml {title="content/modules/_index.md"}
---
title: Product modules
linkTitle: Modules
---
```

## Tagging a page {#assign}
The front matter key is the **plural** (the value column of `taxonomies`), and
the value is always a list, even with one entry:

```yaml {title="content/docs/ha/patroni.md"}
---
title: Patroni high availability
description: Managing PostgreSQL failover with Patroni.
categories: [High availability]
tags: [PostgreSQL, Patroni, failover]
---
```

Where a whole section shares one category, write it in the section index's
`cascade` rather than repeating it on every page:

```yaml {title="content/docs/customize/_index.md"}
---
title: Customization
linkTitle: Customization
icon: fa-solid fa-sliders
cascade:
  categories: [Customization]
---
```

All six documentation sections on this site are configured that way. A page's
own `categories:` replaces the cascade rather than merging with it: to add one
beside the section's category, write both.

## The term line on a page {#page-header}

Documentation and blog pages render a line of assigned terms under the title and
summary, each linking to its term page — the "Categories: Customization" at the
top of this page. Its container is `.taxonomy-terms-article`, with an additional
`.taxo-<plural>` class per taxonomy; use those two selectors to style it.

By default it lists **every** taxonomy on the page, except the two reserved
plurals `authors` and `series` — each of those has a surface of its own (a
byline and a series strip), so repeating them as chips would say the same thing
twice. Naming one in `page_header` puts it back.

To show only some, in a fixed order:

```yaml {title="hugo.yml"}
params:
  taxonomy:
    page_header: [categories]
```

The key is catalogued in [Configuration](/docs/customize/config/). It cannot be
used to hide the line — see [Limits](#limits).

## Two taxonomies the theme knows by name {#authors}

`authors` and `series` are ordinary Hugo taxonomies, declared the ordinary way —
the theme adds no parameter for either. What it adds is a rendering of each, so
the declaration alone is the whole switch:

```yaml {title="hugo.yml"}
taxonomies:
  category: categories
  tag: tags
  author: authors
  series: series
```

| Plural | What the declaration turns on | The term page becomes |
| --- | --- | --- |
| `authors` | Portraits and linked names in the article head, names on list rows, one `<dc:creator>` per author in the feed | The author's profile: the display name is the term page's link title (`linkTitle`, else `title`), `description` the one-line introduction, the body the long one, and the avatar whatever the featured-image resolver picks |
| `series` | A strip above the article body naming the series, this article's position, the next part, and the whole list behind a `<details>` | The series introduction, listing its members in **reading order** rather than newest-first |

Both are covered in full, with the front matter each expects, in
[Writing a blog](/docs/write/blog/#authors). Two things worth knowing here:

- There is deliberately no `data/authors` file. The profile is the term page, so nothing can disagree with it.
- A series term page is the one term page that is not in reverse-date order. Members with a `series_weight` come first in ascending order, the rest by ascending date. A term page cannot supply an order to Hugo, so the theme resolves it once and both surfaces read the result.

## Term and taxonomy pages {#term-pages}

Each taxonomy generates two levels of page:

| Page | URL | Contents |
| --- | --- | --- |
| Taxonomy list | `/categories/` | Headed by the taxonomy's glyph, its localized name ("Categories") and a term count, then one card per term, most-used first: the term glyph (an author's portrait), the term, and its page count |
| Term page | `/categories/customization/` | Headed by the term's title and its page count (a "Categories" kicker links back to the list where the breadcrumb is off), then every page with that term newest first, styled like the blog list |

A Chinese term's URL uses Chinese characters (the address bar shows 定制站点 and
the HTML is percent-encoded); Hugo does not transliterate. Where ASCII URLs are
wanted, use English terms and give each a display name with `title` in
`content/categories/<term>/_index.zh.md` — Hugo's
[term page content file](https://gohugo.io/content-management/taxonomies/#add-custom-metadata-to-a-taxonomy-or-term)
mechanism.

A term page has no fixed place in the content tree, so it borrows one: when
every member of a term sits under one top-level section, the term page renders
that section's sidebar tree and root link, and a reader clicking a tag from the
documentation stays inside the documentation navigation. Where members span
sections, it falls back to the site-level tree.

Term cards appear on the taxonomy list page only; term pages carry the rail
cloud instead.

## The rail cloud {#rail-cloud}

Documentation, blog and term pages carry one group per taxonomy in the right
column (under the outline), with counted, collapsible chips. The group is
automatic and has no switch: it appears wherever a taxonomy is defined and the
current scope has terms.

Taxonomy list pages and term pages lead the column with a taxonomy switcher:
one row per declared taxonomy with its glyph, name and term count, linking to
its list page, the current one highlighted. A list page counts its clouds
site-wide and leaves out its own taxonomy, whose terms are the cards beside
it. A site with a single taxonomy shows no switcher.

The count is **not site-wide** but per top-level section: it first looks for a
section named after the page's `type` (a `type: docs` page uses the `/docs/`
tree), and otherwise uses the top-level section the page is in. "Tags: release
4" on a blog page means four posts in the blog, not four on the site.

Icons are configured by plural name:

```yaml {title="hugo.yml"}
params:
  ui:
    taxonomy_icons:
      categories: fa-solid fa-folder
      tags: fa-solid fa-tags
      modules: fa-solid fa-cubes
```

Those two are already the defaults for `categories` and `tags`; any other
taxonomy defaults to `fa-solid fa-shapes`. An icon is one Font Awesome class
pair, written as everywhere else on the site.

## The taxonomy panel in the navbar {#navbar-panel}

A main-menu entry pointing at a taxonomy list page automatically becomes a panel
of term chips (by descending usage, with counts), needing no hand-written
dropdown:

```yaml {title="hugo.yml"}
languages:
  en:
    menus:
      main:
        - identifier: tags
          name: Tags
          pageRef: /tags
          weight: 60
```

Both `pageRef: /tags` and the older `url: /tags/` are recognized: a URL-form
menu entry is resolved to a site page before its kind is judged, so migrating
from an older configuration needs no rewrite. Other ways to write menus are in
[Navigation and menus](/docs/customize/navigation/).

## Bilingual terms {#bilingual}

Hugo counts and links taxonomies per language: `/categories/` and
`/zh/categories/` are two unrelated trees, and a Chinese page enters only the
Chinese one. Terms are written once per language in each front matter:

```yaml {title="content/docs/ha/patroni.md"}
categories: [High availability]
tags: [PostgreSQL, Patroni, failover]
```

```yaml {title="content/docs/ha/patroni.zh.md"}
categories: [高可用]
tags: [PostgreSQL, Patroni, 故障切换]
```

Two things to watch:

- The same word written identically in both languages (say `release`) still yields two term pages, `/categories/release/` and `/zh/categories/release/`, each counting only its own language's pages. Do not write English terms on Chinese pages for the sake of uniformity: the rail chips would then show English.
- A taxonomy's display name follows the language (for the six built-in keys), but **a term's name does not**: a term is exactly the string written in front matter, and the theme does not translate it. Write `高可用` on an English page and the English site's chip reads `高可用`.

The rest of running a multilingual site is in
[Languages](/docs/customize/i18n/).

## Switching by content type {#per-type}

The theme has no "show on documentation, hide on blog" switch; the control is
which pages you tag. What this site does:

| Content | categories | tags | Effect |
| --- | --- | --- | --- |
| `content/docs/**` | Section-level cascade (the six sections) | none | The term line has one row, Categories |
| `content/blog/**` | Per post (`release`, `oink`) | Per post (`Oink`, `Release`) | Two rows in the term line, two chip groups in the rail |

To make a whole section disappear from the taxonomy, delete `categories` from
the section index's cascade; nothing else is needed. To keep one page out, write
`categories: []` in its own front matter — an empty list overrides the cascade.

## Verify {#verify}

Three things to look at on the page:

- A "Categories: Customization" line under this page's title;
- Chips grouped by taxonomy under the outline in the right column, each with a count;
- [/categories/](/categories/) showing a card for every term, each leading to its term page.

On the command line, check the output:

```bash
hugo -d public
ls public/categories/          # one directory per term
grep -c 'taxonomy-term' public/docs/customize/index.html
```

The theme repository has a dedicated check verifying that no taxonomy pages are
generated without `taxonomies:`, and that term page titles are correct in both
languages:

```bash
cd ~/pgsty/oink && python3 bin/check-taxonomy.py
```

## Limits {#limits}

- `page_header: []` does **not** hide the term line: an empty list is treated as unset and falls back to "list every taxonomy". To remove the line, stop tagging those pages, or hide `.taxonomy-terms-article` in `assets/scss/_styles_project.scss`.
- The rail cloud has neither a switch nor a cap; a site with very many terms should reduce its taxonomies, as there is no way to trim it in configuration.
- Term pages have no cross-language pairing: switching language on a term page does not guarantee landing on the same term in the other language.

## Related {#related}

- [Page parameters](/docs/write/frontmatter/) — `categories` / `tags` and the other front matter keys
- [Blog posts](/docs/write/blog/) — how the blog list works with taxonomies
- [Navigation and menus](/docs/customize/navigation/) — how to write navbar entries
- [Languages](/docs/customize/i18n/) — per-language content and menus
- [Configuration](/docs/customize/config/) — full definitions of `params.taxonomy.*` and `params.ui.taxonomy_icons`

---

Backlinks:

- [Customization](/docs/customize/)
- [Configuration](/docs/customize/config/)
- [Navigation and menus](/docs/customize/navigation/)
- [Blog posts](/docs/write/blog/)
- [Page parameters](/docs/write/frontmatter/)
