Málefni Page Upgrade — Design Spec

Date: 2026-03-23 Status: Approved Scope: Topic detail pages (/malefni/{slug}/)

Summary

Two improvements to the topic detail pages:

  1. Timeline: proportional time axis with hierarchical date labels — replace the current equidistant bar chart (with overlapping full-date labels) with a proportionally-spaced 2026-only timeline using label_date_short()-style hierarchical labels
  2. Claims section: embedded claim tracker — replace the simple server-rendered claims list with a full client-side claim tracker (filtering, search, sort, expandable cards), lazy-loaded from claims.json

Context

The site is receiving significant media attention (Spursmál, Bylgjan, Rás 2). Málefni is the natural entry point for "I want to understand topic X" — upgrading it improves both first impressions and return-visit value.

Design

1. Timeline — Proportional Axis with Hierarchical Labels

Current state

New behaviour

Filter to 2026 only. The EU debate is heating up this year; earlier data is sparse and not relevant to the current picture. The Nunjucks template filters topic.timeline to entries where w.week >= "2026-01-01".

Proportional time positioning. Bars are positioned on a real calendar axis. The container switches from display: flex to position: relative. Each bar gets:

Hierarchical labels in stacked rows below the bar area:

Label thinning when bars are dense:

Tooltips: full date on hover (e.g. "9. mars 2026: 174 tilvísanir, 127 nýjar fullyrðingar") — uses existing isDate filter, same format as before.

Files changed

File Change
_includes/topic-detail.njk Rewrite timeline section: filter to 2026, compute proportional positions, render three label rows
assets/css/topic-detail.css Rewrite .td-timeline-* styles: relative positioning, absolute bars, label row layout
eleventy.config.js Add isMonthShort filter for 3-char Icelandic month abbreviations (used by hierarchical label row 2)

2. Claims Section — Embedded Tracker Instance

Current state

New behaviour

The td-claims section is replaced with a tracker mount point:

<section class="td-claims" id="topic-claim-tracker"
         data-category="fisheries"
         data-base="/assets/data">
  <noscript>...</noscript>
</section>

A new script (topic-claim-tracker.js) creates a TrackerController instance that:

  1. Lazy-loads claims.json + reports.json when the section enters the viewport (IntersectionObserver)
  2. Filters claims by category === topicCategory (derived from data-category)
  3. Renders the full claim tracker UI: search, verdict filter, sort dropdown, expandable cards with explanation, confidence bar, evidence links, sightings toggle

Category mapping: Topic slugs use hyphens (eea-eu-law), claim categories use underscores (eea_eu_law). The script converts with .replace(/-/g, '_').

Differences from /fullyrdingar/ tracker

Aspect /fullyrdingar/ Topic embedded
Category filter Dropdown (all categories) Pre-filtered, no dropdown
URL query sync Yes (pushes to ?q=&verdict=) No (embedded section, don't pollute topic URL)
Data loading Eager (on page load) Lazy (IntersectionObserver on mount point)
Stats row Full site-wide stats Topic-scoped stats (count, sightings, verdict breakdown)

Shared card rendering

The renderClaimCard() function (~110 lines) is currently inline in claim-tracker.js. To avoid duplication, extract it into a new shared module:

New file: assets/js/tracker-claim-card.js

Script loading

The base.njk template currently supports a single extra_css and extra_js string. The topic detail page needs:

Change extra_css to support arrays. In base.njk, update the CSS block:






Then topic-detail.njk frontmatter becomes:

extra_css:
  - /assets/css/topic-detail.css
  - /assets/css/claim-tracker.css
extra_js: /assets/js/topic-claim-tracker.js

Loading tracker-claim-card.js in base.njk: The new shared module must load after the existing shared infrastructure scripts (tracker-renderer.js, tracker-controller.js) but before the page-specific script. Add it to the auto-loaded chain in base.njk, gated on extra_js (same condition as the other shared scripts):




This ensures tracker-claim-card.js is available on both /fullyrdingar/ (which uses claim-tracker.js) and topic detail pages (which use topic-claim-tracker.js). The module attaches to globalThis.ESBvaktinClaimCard following the same IIFE + globalThis pattern used by all other shared modules in this codebase (not ES module exports).

New file: assets/js/topic-claim-tracker.js (~150 lines)

Glue script that:

Files changed

File Change
_includes/topic-detail.njk Replace td-claims section with tracker mount point; update frontmatter for array extra_css
_includes/base.njk Support array extra_css; add tracker-claim-card.js to shared script loading chain
assets/js/tracker-claim-card.js New — extracted shared card rendering + interaction handlers
assets/js/claim-tracker.js Refactor to import from tracker-claim-card.js
assets/js/topic-claim-tracker.js New — topic-scoped claim tracker glue

Data scalability note

Current data: 1,319 claims, 2.5 MB raw / ~608 KB gzipped. Client-side approach is sound up to ~3,000–5,000 claims. If growth exceeds that, consider virtual scrolling or per-category JSON splits. Not needed now.

Out of scope