The /tags/ page in the reader theme started as a perfectly ordinary taxonomy page. That was correct in the narrow Hugo sense and wrong in the editorial sense. A plain list tells you that tags exist. It does not tell you which ones dominate the corpus, which ones are one-off curiosities, or which ones form the real conceptual backbone of the site.
The goal of the tag cloud was not to make the page decorative. The goal was to make it legible as a structural overview. A useful cloud must answer three questions quickly:
- what topics are central,
- what topics are marginal,
- how much visual emphasis can be added before the page turns into noise.
That third question matters more than people admit. Tag clouds are easy to make ugly. If you give them color for novelty, weight for count, random order for “organic feel”, and large size swings for drama, they stop being navigation and turn into a poster. This theme wanted the opposite: quiet, readable, monochrome, but still meaningfully weighted.
So the implementation followed a few deliberate rules.
Start from count, but do not trust count alone
The source set is Hugo’s normal tag taxonomy ordered by count. That part is conventional and direct:
|
|
This gives a stable descending list of tags by article count. But using that count directly as a visual signal has a problem: a large corpus almost always has a long tail. A few tags are very common, many tags appear only once or twice, and a linear scale tends to flatten the middle or overinflate the top.
That is why the cloud does not use a linear count weight anymore. It uses a logarithmic normalization. The implementation computes the natural logarithm of the minimum count, the maximum count, and each tag’s own count, then normalizes the result into a 0..1 weight interval:
|
|
The effect on the page is practical rather than theoretical. Heavy tags still read as heavy, but medium-frequency tags do not collapse into one indistinguishable visual band. They keep enough separation to matter.
Size and weight are separate, but derived from the same signal
Once the normalized weight exists, the cloud uses it twice:
- to interpolate font size,
- and to interpolate font weight.
Both sets of bounds are configurable in hugo-reader.toml:
|
|
The template then interpolates between those bounds:
|
|
That may sound obvious, but it matters that both are configurable. The cloud is not locked to one aesthetic. If the page starts feeling too shouty, the upper size bound can come down. If the differences feel too timid, the weight range can widen. The implementation keeps the knobs where they belong: in configuration, not in the template body.
Color is monochrome on purpose
The color model is intentionally narrow. Instead of assigning hues, the cloud hashes the tag name into a grayscale value. The target range is also configurable:
|
|
The code hashes the tag name deterministically and then remaps the internal 0..192 scale into the configured grey bounds:
|
|
This gives variation without turning the page into a rainbow. Tags do not compete through hue. They differ through density, scale, and value. That is much more compatible with the rest of the reader theme, where structure is supposed to emerge from calm contrast rather than accent color.
Placement is not random, and that is a feature
A true random shuffle would make the page feel unstable between rebuilds unless a seed were fixed, and Hugo templates are not a great place for high-quality randomness anyway. The implementation therefore uses a two-step placement model:
- alternate front/back insertion so heavy tags tend toward the center,
- apply a deterministic pseudo-shuffle to avoid overly rigid symmetry.
The first part is simple and intentional. Each count-sorted tag is alternately pushed into a front or back list. Reversing the front list and then appending the back list yields a sequence where the heaviest cluster around the middle of the final run.
That alone was too orderly. So the second step performs bounded displacement rounds. Those rounds are also configurable:
|
|
The important distinction is this:
shuffleRoundsmeans how many displacement operations happen,shuffleWindowmeans how far one selected tag may move left or right in a single operation.
The code does not just swap neighbors repeatedly. It picks a source index deterministically from the round number, computes a displacement from the tag-name hash plus round number, clamps the destination into the list bounds, removes the tag, and reinserts it at the new index.
That means the “window” is a real movement window, not just a disguised repeat count. The cloud still stays deterministic from build to build, but it stops looking mechanically mirrored.
Singleton tags should not waste a click
One subtle usability improvement is that tags with exactly one article do not link to a one-entry tag archive. They link directly to the article itself.
That decision looks small, but it respects user intent. If a tag can only take you to one place, the intermediate overview is ceremony. The cloud now checks count and changes the target URL accordingly:
|
|
The result is that dense tags behave like topic indexes, and singleton tags behave like direct shortcuts.
Why the cloud feels calmer than most clouds
The visual result comes from constraint:
- no hue circus,
- no explosive size jumps,
- deterministic placement,
- bounded pseudo-shuffle,
- centered flowing layout,
white-space: nowrapon each tag so the unit stays intact.
The CSS is correspondingly spare:
|
|
This is not an attempt to look algorithmic. It is an attempt to let the algorithm do just enough work that the page feels informative before it feels designed.
There are two motion cues in that CSS that matter:
- on page load, the cloud starts with exaggerated spacing and then settles into its dense final form,
- on hover, a tag brightens and gains a stronger shadow quickly, then relaxes back slowly.
Those are tiny effects, but they keep the page from feeling dead-static. The cloud does not need to pulse or wiggle. It only needs just enough motion to suggest that taxonomy here is active structure, not a frozen appendix.
Effect on the reader
The practical effect is that the page becomes scannable at two speeds.
At a glance:
- the large center cluster shows the dominant themes,
- the sparse outskirts show one-off or niche topics,
- the grayscale variation prevents the cloud from feeling dead-flat.
At a deliberate reading pace:
- the left pane lets you find exact tags alphabetically,
- singleton tags get you straight to content,
- heavier tags invite exploration because they look consequential without shouting.
Why the implementation is deterministic
The cloud is meant to feel lively, but not unstable. That is why all the placement and shuffle logic is deterministic. Rebuilds should not cause the same tag cloud to jump around randomly when nothing in the corpus changed.
So the page gets variation without losing trust:
- count drives prominence,
- name hash drives grayscale,
- deterministic placement drives center-of-mass,
- deterministic displacement rounds soften the symmetry.
That is the whole design philosophy in one sentence: variation without drift.
That balance was the real goal. Not “build a tag cloud.” Build a page where taxonomy behaves like editorial structure instead of storage metadata.