ChanKay Blog
DemosTechnicalTrading
English简体中文
English简体中文
DemosTechnicalTrading
ChanKay Blog
GitHubBilibili

© 2026 ChanKay Blog

Building a Content Treemap: Counting Articles, Simplifying Taxonomy, and Fixing the Details

Oct 8, 2026•4 min read

Five diagrams explain this blog's content Treemap: article counts, Payload relationships, data flow, in-place exploration, and the border-layer fix.

An empty area below the homepage contribution heatmap led to a word-cloud idea, then to the nested rectangles of a bundle-size analyzer. The first real decision was about data: what does a rectangle count?

The shipped Treemap is Category → Article. Every published article contributes one unit. Categories expand inside the chart; article tiles open their pages. These five diagrams explain the decisions behind it.

1. Tag usage is not article count

The first version sized tiles by tag assignments. With three tags on article A and one on article B, A contributed three quarters of the weight—even though readers might assume the chart represented the number of articles.

Figure 1: Adding editorial tags should not increase an article's weight in an article-count chart.

The aggregation now deduplicates post IDs and groups them by a single Category. Category counts sum to the number of unique published articles. Missing or unpublished categories use an Uncategorized branch without exposing private category labels.

Weights add up, but visible pixels also include headers and padding. The compact navigation fallback is not a proportional chart.

2. Store each relationship once

Category describes subject ownership; Series describes an editorial sequence. Requiring a series just to draw the chart would make content organization serve the component.

Solid arrows below represent references stored on the post; dashed arrows are reverse queries.

Figure 2: Posts store references; Category and Series expose reverse usage through Join fields.

Payload's Join field avoids storing a second array of post IDs or maintaining synchronization hooks. The essential Category configuration is:

{
  name: "posts",
  type: "join",
  collection: "posts",
  on: "category",
}

Once leaves became articles, Tags relationships, badges, queries, and publication rules were unnecessary for this experience. We retired the active collection while retaining historical data for verification.

Nested categories are not implemented. Trading → Equity Research → Hog Cycle remains a future modeling decision. The released hierarchy is Category → Article; Series does not participate in the chart.

3. The CMS owns data; the UI receives a tree

The reusable component lives in packages/ui. Application code owns queries and business adaptation. Mermaid supplied the visual reference; the interactive chart uses D3 coordinates and React rendering.

Figure 3: The UI knows neither Payload collections nor blog routes.

Public queries explicitly use _status = published, draft: false, and overrideAccess: false. They read all pages of 200 records, selecting only needed fields. A paginated Join array is not a total count. Responses omit article bodies and drafts; Category queries disable Join population.

Posts, Pages, Categories, Series, and Media now use Payload's native draft workflow. Series.progress remains a separate business state. Changed indicates newer draft edits in the admin, not a third stored publication value.

Existing chart, card, and foreground CSS tokens supply the theme. D3 Treemap handles layout independently of the rendered interface.

4. Keep exploration inside the chart

The first interaction opened a details panel below the visualization. Readers clicked a rectangle but had to look elsewhere for the result. Category focus and return navigation now stay in the same canvas.

Figure 4: Categories expand; leaves navigate. There is no external details panel.

Element Behavior
Category Name and article count; expands in place
Article leaf Title only; opens the article
Interactive area Pointer; 1.015 hover scale when motion is allowed
Root category Same interaction convention, without a special exception
Keyboard Activation and focus management on expand/return

Each article has weight one, so repeating “1” only takes space away from titles. Hiding that visible number does not change its layout weight.

When proportional tiles become too small, the overview can switch to compact category navigation. Entering a category provides a bounded scrolling canvas. This fallback prioritizes access to content rather than proportional area.

5. The missing border was a paint-order bug

A screenshot showed category borders disappearing beneath the header background. A thicker border would not fix the overlap. We separated the background, hit targets, and frame.

Figure 5: The frame sits above the hover layer without intercepting the buttons beneath it.

This required visual browser checks. Component tests can prove that callbacks fire; they cannot prove that a border remains visible.

The final website suite passed 142 tests covering count conservation, duplicate IDs, focus/return, article activation, and leaf labels. Type checks, scoped lint, and live visual checks complemented them. Historical Tags records and migration journals remain read-only; obsolete migration writes are disabled.

Defining the count made it easier to decide which entities and interactions were necessary. Nested rectangles are a presentation tool, not a reason to invent more content levels.

Discussion

Sign in with GitHub to comment. All language versions share this discussion.

View discussions on GitHub

On this page

  • 1. Tag usage is not article count
  • 2. Store each relationship once
  • 3. The CMS owns data; the UI receives a tree
  • 4. Keep exploration inside the chart
  • 5. The missing border was a paint-order bug