- hello dashboard · 01
The default dashboard end to end
The one to read first. A CSV of sales, four filters, two charts, a sortable table, and the provenance block that lets a reader trace every number back to the file it came from.
Byggekommando
python3 tools/build_dashboard.py \ --data examples/01-hello-dashboard/salgsdata.csv \ --title "Salg per region og produkt 2026" \ --dimension Produkt --measure Belop --time Maaned \ --filters Region,Produkt,Kanal \ --source "Eksempeldata (syntetisk) — salgsdata.csv" \ --requested-by "Prosjekteier" \ --out examples/01-hello-dashboard/salg-dashboard.html - paired dashboard · 02
Paired: a dashboard and its data, in two files
dash.html is a dashboard with no data inside it. The rows live in data.js beside it, and the page loads that file. Replace data.js in a synced folder and everyone who has the pair sees the new numbers without anyone rebuilding the page.
Byggekommando
python3 tools/build_dashboard.py \ --data examples/01-hello-dashboard/salgsdata.csv \ --title "Salg per region og produkt 2026" \ --dimension Produkt --measure Belop --time Maaned \ --filters Region,Produkt,Kanal \ --source "Eksempeldata (syntetisk) — salgsdata.csv" \ --requested-by "Prosjekteier" \ --mode paired \ --out examples/02-paired-dashboard/dash.html - cube dashboard · 03
Cube dashboard: 4 000 000 rows in a 0.81 MB file
This example exists to prove one claim with numbers you can reproduce: Everything is stdlib Python except step 3, which needs Playwright to drive a real browser
Byggekommando
python3 examples/03-cube-dashboard/generate_data.py python3 tools/build_cube_dashboard.py \ --data examples/03-cube-dashboard/transaksjoner.csv \ --dims region,kategori,kanal,aldersgruppe \ --time dato --time-grain month \ --measures "belop:sum:kr,antall:sum" \ --title "Transaksjonsoversikt 2024–2026" \ --requested-by "Prosjekteier" \ --out examples/03-cube-dashboard/transaksjoner.html python3 tools/verify_dashboard.py examples/03-cube-dashboard/transaksjoner.html - generic standalone demo · 04
The generic half, with no dashboard in it
standalonehtmldemo1.html is a document with rows, columns and a table of contents that knows nothing about dimensions, measures or charts. It exists to prove the split in [docs/08](../../docs/08-generic-artifact-model.md) is real: the generic layer carries provenance, a self-contained file and a page that works from file://, and a dashboard is one specialisation of that, not the definition of it.
Byggekommando
python3 examples/04-generic-standalone-demo/build_standalone_demo.py - theme toggle demo · 05
Three navigation decisions, on a throwaway page
A KPI page that exists to answer one question: where does "dark mode" live? Three answers, in the page, on top of the same three numbers. A theme toggle in the header, a persisted choice, and aria-pressed telling a screen reader which state it is in.
Byggekommando
python3 examples/05-theme-toggle-demo/build_theme_toggle_demo.py - chart palette demo · 06
Every chart type and every colour family, side by side
The eight chart types [docs/10](../../docs/10-chart-colour-palettes.md) allows, drawn four times each: one per palette family (categorical, sequential, diverging) with a switcher, so a colour decision is made by looking at real bars and lines instead of at a swatch grid. The palettes are Okabe–Ito, ColorBrewer Set2 and a table view for readers who need the numbers.
Byggekommando
python3 examples/06-chart-palette-demo/build_chart_palette_demo.py - topbar pattern demo · 07
Topbar-mønster: nøytral demo for issue #15
Demoen kravsatt i [issue #15](https://example.com/tun/issues/15) §6. Beviser sections 1–4 der (fast toppfelt, hamburger → overlay-meny, info-panel, fullskjerm-knapp) i en generert, selvstendig fil — bevisst uten Eksempel AS-branding, det er et eget, senere issue.
Byggekommando
python3 examples/07-topbar-pattern-demo/build_topbar_demo.py python3 tools/verify_standalone_core.py examples/07-topbar-pattern-demo/topbar-pattern-demo.html python3 tools/verify_app_shell.py examples/07-topbar-pattern-demo/topbar-pattern-demo.html python3 examples/07-topbar-pattern-demo/verify_topbar_demo.py - consolidated standalone demo · 10
Consolidated standalone demo: the JavaScript-resilience pattern for issue #22
Combines prior demos (topbar mechanics #15, Eksempel AS logo #17, branded topbar + the design system icons #19/#20, theme toggle + colour tokens #12/#14) into one file, and is the artefact three real sharing paths were tested against for [issue #22](https://example.com/tun/issues/22) — logged as row 22 in [docs/01-tenant-constraints.md](../../docs/01-tenant-constraints.md). The normative write-up of what this proves is [docs/11-responsiveness-and-browser-support.md](../../docs/11-responsiveness-and-browser-support.md) §3b/§4 — read that first if you want the "why," not just the "what."
Byggekommando
python3 examples/10-consolidated-standalone-demo/build_consolidated_demo.py python3 tools/verify_standalone_core.py examples/10-consolidated-standalone-demo/consolidated-standalone-demo.html - js free standalone poc · 13
Javascript-løs standalone HTML: POC for issue #30
Beviser mønsteret fra [issue #30](https://example.com/tun/issues/30) på en ekte, renderbar fil — ikke bare i prosa. Rører ikke examples/10-consolidated-standalone-demo/, som fortsatt er JavaScript-drevet slik den alltid har vært; dette er et separat sammenligningspunkt, ikke en erstatning.
Byggekommando
python3 examples/13-js-free-standalone-poc/build_js_free_poc.py python3 tools/verify_standalone_core.py examples/13-js-free-standalone-poc/js-free-standalone-poc.html python3 tools/verify_app_shell.py examples/13-js-free-standalone-poc/js-free-standalone-poc.html python3 examples/13-js-free-standalone-poc/verify_js_free_poc.py - chart emulation nojs poc · 14
Chart-emulering uten JavaScript: hvilke av de tolv diagramtypene tåler det?
Diagnostisk oppfølging av [issue #30](https://example.com/tun/issues/30) og examples/13-js-free-standalone-poc/. Rører ikke examples/06-chart-palette-demo/ — bruker samme datasett og samme Okabe–Ito-palett derfra for ni av de tolv typene (én palett er nok til å vurdere selve teknikken), utvidet med tre stolpevarianter examples/06 ikke har (vertikal, og gruppert flere-serier i begge retninger). Alle tolv bygges med null <script>-tagger.
Byggekommando
python3 examples/14-chart-emulation-nojs-poc/build_chart_emulation_poc.py python3 tools/verify_standalone_core.py examples/14-chart-emulation-nojs-poc/chart-emulation-nojs-poc.html - chart library comparison poc · 15
Chart-bibliotek sammenligning: Chart.js vs. håndrullet vs. Matplotlib
Follow-up til issue #30, men med motsatt utgangspunkt av examples/13/14: denne siden krever JavaScript, med vilje. Den finnes for å sette tre helt forskjellige tilnærminger til diagrammer side om side og la forskjellen faktisk sees, ikke bare beskrives i en tabell.
Byggekommando
uv sync --extra charts # kun nødvendig for å regenerere SVG-ene python3 generate_matplotlib_charts.py # skriver mpl-svg/*.svg på nytt python3 build_comparison_poc.py # bygger selve HTML-filen — ingen matplotlib-avhengighet her python3 ../../tools/verify_standalone_core.py chart-library-comparison-poc.html - embedding mode comparison poc · 16
Embedding-modus sammenligning (issue #39)
Diagnoseverktøy, ikke et designmønster å kopiere videre. Bygget etter et ekte brudd i produksjon (en kollega-kollega/saksrom, 15. sept. 2026, se [issue #39](https://example.com/tun/issues/39)): en fil viste feilaktig "ingen data funnet" åpnet via SharePoints egen innebygde filforhåndsvisning (enkeltklikk "hurtigvisning" i dokumentbiblioteket — IKKE en nedlasting, IKKE /preview-embed.aspx-vieweren som allerede er dekket av docs/01-tenant-constraints.md rad 4).
Byggekommando
python3 examples/16-embedding-mode-comparison-poc/build_embedding_comparison_poc.py - multi regime day board · 17 +1
A day board for an organisation that runs three régimes with three owners
A pupil-transport day overview for Eksempelskolen in Eksempelkommune, both fictional, built as a Tier 1 paired artefact: oversikt.html plus a sibling data.js.
Byggekommando
python3 examples/17-multi-regime-day-board/build_day_board.py uv run python tools/verify_dashboard.py examples/17-multi-regime-day-board/oversikt.html uv run python examples/17-multi-regime-day-board/verify_day_board.py uv run --extra verify python tools/verify_viewports.py \ examples/17-multi-regime-day-board/oversikt.html --text-floor 14 --text-floor-above 1024 - portable slide deck · 18
Portable slide deck
A real single-file presentation adapted after a Slack and iPhone delivery test exposed two separate problems: 1
Byggekommando
python3 tests/test_portable_slide_deck.py - tun pitch · 19
the Tún pitch, as a Tún artefact
tun-pitch.html is the one-page pitch: the system diagram, the four-step process, and the measured answer to the question that always comes back — "how do I actually share an HTML file with one colleague?"
Byggekommando
python3 tools/verify_standalone_core.py examples/19-tun-pitch/tun-pitch.html python3 tools/verify_viewports.py examples/19-tun-pitch/tun-pitch.html \ --text-floor 11 --text-floor-above 1366 - tun for kommunikasjon · 20
The idea, written for a communications reader
One long page, not a deck: what Tún is, in the vocabulary of someone who writes for a living rather than someone who ships data. The three questions a communications adviser actually asks — who sees this, where does it live, what happens when someone disagrees with a number — get an answer each, in their words.
Byggekommando
uv run --extra verify python tools/verify_standalone_core.py \ examples/20-tun-for-kommunikasjon/tun-for-kommunikasjon.html uv run --extra verify python tools/verify_viewports.py \ examples/20-tun-for-kommunikasjon/tun-for-kommunikasjon.html uv run --extra verify python tools/verify_a11y.py \ examples/20-tun-for-kommunikasjon/tun-for-kommunikasjon.html - slide deck detail overlay · 21
Detail overlay, vmin typography, and extended keyboard map
The worked example requested on issue #37: "what we still need: a worked example that shows the overlay pattern". Demonstrates, on a real renderable file — not just prose — the three patterns issue #37's own comment thread identified from a pitch deck built outside this repo: 1
Byggekommando
python3 tools/verify_standalone_core.py examples/21-slide-deck-detail-overlay/detail-overlay-pattern.html python3 tools/verify_slide_deck.py examples/21-slide-deck-detail-overlay/detail-overlay-pattern.html --out /tmp/deck-check --quick python3 tests/test_portable_slide_deck.py - xlsx from file · 22
Reading a real .xlsx from file://, with no library
An .xlsx is a ZIP archive holding SpreadsheetML XML. xlsx-from-file.html unpacks one with nothing but what a modern browser already ships: DecompressionStream('deflate-raw') to inflate each ZIP entry, and DOMParser to read the XML parts. No xlsx library, no bundler, one file.
Byggekommando
python3 examples/22-xlsx-from-file/build_workbook.py uv sync --extra verify uv run playwright install chromium firefox webkit # once uv run --extra verify python examples/22-xlsx-from-file/verify_xlsx_from_file.py - process diagram · 23 +1
A process diagram, Tún drawing its own workflow
tun-workflow.html is a swimlane process diagram of how a Tún dashboard comes to exist: the request, modelling the data, approval for the audience, the build, tun-verify, publishing, and the feedback that sends it round again. Two steps are flagged as places to improve.
Byggekommando
python3 tools/build_process_diagram.py \ examples/23-process-diagram/tun-workflow.process.txt \ --out examples/23-process-diagram/tun-workflow.html python3 tools/build_process_diagram.py \ examples/23-process-diagram/fem-roller.process.txt \ --out examples/23-process-diagram/fem-roller.html - editable document · 24 +1
Editable document: change the text in the browser, download a copy
A decision memo as one self-contained file. It reads like any other Tún artefact. With JavaScript, a Rediger button turns the page editable, and Lagre kopi (or Ctrl+S / ⌘S) downloads an edited copy.
Byggekommando
python3 examples/24-editable-document/build_editable_document.py uv sync --extra verify uv run playwright install chromium firefox webkit # once uv run --extra verify python examples/24-editable-document/verify_editable_document.py uv run --extra verify python tools/verify_standalone_core.py examples/24-editable-document/editable-document.html - living idea picture · 25 +2
The idea, alive: a moving, explorable picture with no script
The whole Tún idea as one figure that moves, can be explored, and plays step by step, in CSS and SVG only. It is the picture on tun.community/ide. That page has no JavaScript, and neither does this.
Byggekommando
python3 examples/25-living-idea-picture/build_idea_picture.py uv run --extra verify python examples/25-living-idea-picture/verify_idea_picture.py uv run --extra verify python tools/verify_viewports.py \ examples/25-living-idea-picture/idea-picture.html - kpi board · 26 +1
The KPI board: rates, English chrome, and a feedback link
Four builder flags had no example. This one uses all four on one artefact, so the things a skill or a person can now do are visible rather than documented: Two artefacts, same data: [sykefravaer.html](sykefravaer.html) in Norwegian with a mailto: link, and [sick-leave.html](sick-leave.html) in English with an issue URL.
Byggekommando
python3 examples/26-kpi-board/generate_data.py python3 tools/build_dashboard.py \ --data examples/26-kpi-board/sykefravaer.csv \ --title "Sykefravaer per enhet" \ --dimension Enhet --measure Sykefravaersdager --time Maaned \ --filters Seksjon,Enhet \ --kpis total,per_period,rows,distinct --measure-kind rate \ --source "Syntetiske data, 144 rader (8 enheter, 18 maneder)" \ --requested-by "HR" --lang nb --feedback-to hei@tun.community \ --out examples/26-kpi-board/sykefravaer.html python3 tools/build_dashboard.py \ --data examples/26-kpi-board/sykefravaer.csv \ --title "Sick leave by unit" \ --dimension Enhet --measure Sykefravaersdager --time Maaned \ --filters Seksjon,Enhet \ --kpis total,per_period,rows,distinct --measure-kind rate \ --source "Synthetic data, 144 rows (8 units, 18 months)" \ --requested-by "HR" --lang en --feedback-to https://github.com/example/tun/issues/new \ --out examples/26-kpi-board/sick-leave.html uv run --extra verify python tools/verify_dashboard.py examples/26-kpi-board/sykefravaer.html --engines all uv run --extra verify python tools/verify_viewports.py examples/26-kpi-board/sick-leave.html - survey board · 27 +1
A survey that answers where people look, without naming them
An employee survey as one file. Pick a unit and a role, and every question shows its distribution: how many people answered, and what share chose each option. Two rules are built into the builder rather than left to whoever assembles the file.
Byggekommando
python3 examples/27-survey-board/generate_data.py # writes survey.csv (synthetic) python3 tools/build_survey_dashboard.py \ --data examples/27-survey-board/survey.csv \ --title "Arbeidsmiljø 2026 — syntetisk eksempel" \ --segments Enhet,Rolle \ --source "Syntetisk eksempeldata (survey.csv)" \ --requested-by "HR" \ --out examples/27-survey-board/survey.html - one dataset three artefacts · 28 +2
one dataset, three artefacts
The same 285 rows, built three ways, so the choice between them stops being a guess.
Byggekommando
python3 tools/build_dashboard.py \ --data examples/28-one-dataset-three-artefacts/saksbehandling.csv \ --title "Saksbehandling per team" \ --dimension Team --measure Timer --time Maaned \ --filters Prioritet,Kanal \ --source "Syntetiske data, 285 rader (5 team, 9 maneder)" \ --requested-by "Prosjekteier" \ --out examples/28-one-dataset-three-artefacts/oversikt.html python3 tools/build_dashboard.py \ --data examples/28-one-dataset-three-artefacts/saksbehandling.csv \ --title "Saksbehandling: styringstall" \ --dimension Team --measure Timer --time Maaned \ --filters Prioritet \ --kpis total,per_period,rows,distinct \ --source "Syntetiske data, 285 rader" \ --requested-by "Prosjekteier" \ --out examples/28-one-dataset-three-artefacts/styringskort.html python3 tools/build_cube_dashboard.py \ --data examples/28-one-dataset-three-artefacts/saksbehandling.csv \ --dims Team,Prioritet,Kanal --time Maaned --time-grain month \ --measures "Timer:sum,Antall:sum" \ --title "Saksbehandling: kube" \ --source "Syntetiske data, 285 rader" \ --requested-by "Prosjekteier" \ --out examples/28-one-dataset-three-artefacts/kube.html - audience split · 29 +1
One lead, two files: the audience split
The smallest honest answer to the question every dashboard project gets: "can't we just filter it so each team sees only their own numbers?"
Byggekommando
cd examples/29-audience-split python3 generate_data.py # writes both extracts cd ../.. python3 tools/build_dashboard.py \ --data examples/29-audience-split/salg-alle.csv \ --title "Salg per produkt og måned, alle team" \ --dimension Produkt --measure Belop --time Maaned --filters Produkt \ --source "Syntetiske salgstall (generate_data.py) — salg-alle.csv" \ --requested-by "Salgssjef" \ --out examples/29-audience-split/alle.html python3 tools/build_dashboard.py \ --data examples/29-audience-split/salg-team-nord.csv \ --title "Salg per produkt og måned, team Nord" \ --dimension Produkt --measure Belop --time Maaned --filters Produkt \ --source "Syntetiske salgstall (generate_data.py) — salg-team-nord.csv" \ --requested-by "Teamleder Nord" \ --out examples/29-audience-split/team-nord.html - json to dashboard · 30
The file you get from an API is not the file the builder reads
A dashboard starts from a flat table. Most systems hand you something else: a JSON endpoint with a wrapper object, nesting, and a field that is a list. The gap between the two is a flattener of about fifteen lines, and this example is that gap, on purpose, so nobody concludes the builder needs a rewrite to read JSON.
Byggekommando
cd examples/30-json-to-dashboard python3 generate_data.py # identical bytes every run python3 flatten.py # salg.csv, with the row count and the sum printed python3 ../../tools/verify_dashboard.py ordrer.html - survey crosstab · 31
Two questions at once, and why that has an all-or-nothing rule
A survey board counts one question at a time. The moment someone asks "and how do those two answers line up", the arithmetic changes: multiplying two questions is exactly how a small group becomes a nameable person, so a cross-tab needs its own rule rather than the per-cell floor the rest of the board uses.
Byggekommando
python3 examples/31-survey-crosstab/generate_data.py python3 tools/build_survey_dashboard.py \ --data examples/31-survey-crosstab/survey-wide.csv --wide \ --questions "Jeg får svar på det jeg spør om,Verktøyene jeg bruker passer til oppgaven,Jeg vet hvem som bestemmer i saken,Jeg har fått opplæring i verktøyene" \ --cross "Jeg får svar på det jeg spør om|Verktøyene jeg bruker passer til oppgaven" \ --cross "Jeg vet hvem som bestemmer i saken|Jeg har fått opplæring i verktøyene" \ --segments Enhet,Rolle \ --title "Arbeidsmiljø 2026 — krysstabeller" \ --source "Syntetisk eksempeldata (survey-wide.csv)" \ --requested-by "HR" \ --out examples/31-survey-crosstab/survey-crosstab.html - rows register · 32 +1
Sometimes the question is "give me the list"
Half of what people share as a spreadsheet is not a chart waiting to happen. It is a list someone wants to search: the open deviations, the cases past their deadline, the contracts in that unit. Tún could already count rows and draw them; it could not hand over the rows themselves with a search field that works from a file:// page in a synced folder.
Byggekommando
python3 examples/32-rows-register/generate_data.py python3 tools/build_register.py \ --data examples/32-rows-register/avvik.csv \ --title "Avvik 2026" \ --columns Saksnummer,Enhet,Avvik,Mottatt,Status,Dager,Belop \ --numeric Dager,Belop --sort Dager --desc \ --source "Syntetisk eksempeldata (avvik.csv)" \ --requested-by "Kvalitet" \ --out examples/32-rows-register/register.html - comparison · 33
"What moved since last month"
Half of the reporting a tenant does is a subtraction. Two extracts of the same measure, one per period, matched on a key, and the reader wants to know what moved, by how much, and what is new or gone. That work is done by hand in Excel, where the arithmetic is invisible, the result dies with the spreadsheet, and nobody can see afterwards whether a key was dropped on the way.
Byggekommando
python3 examples/33-comparison/generate_data.py python3 tools/build_comparison.py \ --before examples/33-comparison/forrige.csv \ --after examples/33-comparison/denne.csv \ --key Region --value Omsetning \ --title "Omsetning per region: september mot oktober" \ --before-label September --after-label Oktober --meaning up \ --source "Syntetisk eksempeldata (forrige.csv, denne.csv)" \ --requested-by Innsikt \ --out examples/33-comparison/sammenligning.html \ --annotate - folder index · 34 +3
The page you land on
A folder of built files has no order. Someone shares Saker/ with four artefacts in it, and the reader sees four file names and picks one, or asks which one they were supposed to open. The dashboards are fine; the folder is the part with no answer in it.
Byggekommando
python3 examples/34-folder-index/build_example.py - timeline · 35
When it happens, and what overlaps
A plan in a spreadsheet is two date columns and a hope that the reader compares them by eye. The question people actually ask is "what overlaps" — which work runs beside which, and where the crunch is. That is a picture, not a pair of columns.
Byggekommando
python3 examples/35-timeline/generate_data.py python3 tools/build_timeline.py \ --data examples/35-timeline/milepaeler.csv \ --title "Utredningen: når skjer det" \ --source "Syntetisk eksempeldata (milepaeler.csv)" \ --requested-by "Programmet" \ --out examples/35-timeline/tidslinje.html - review roundtrip · 36
The file that answers back
Every other artefact in this repository is one-way. Someone opens it, disagrees with a number, and the only route back is a screenshot and a sentence naming neither the row nor the build: "tallet for mars ser feil ut", when the file holds 96 rows and three builds exist. Here the file asks. The reader presses one button, clicks the row, writes a comment, and copies a single text block. On the other side a tool reads the block, finds the row again, and puts the file's own number next to the reader's.
Byggekommando
python3 examples/36-review-roundtrip/build_example.py - printable onepager · 37
The printable one-pager
Everything else in this repository is read on a screen. This one is handed over: printed, or sent as a PDF, and read in a meeting where nobody can filter it.
Byggekommando
python3 examples/37-printable-onepager/generate_data.py python3 tools/build_onepager.py \ --data examples/37-printable-onepager/salgsdata.csv \ --title "Salg per produkt, september 2026" \ --dimension Produkt --measure Belop --period Maaned \ --source "Syntetisk eksempeldata (salgsdata.csv)" \ --requested-by "Salgsavdelingen" \ --out examples/37-printable-onepager/ett-ark.html - distribution · 38
The shape of the data, not its total
Every other artefact this engine builds answers a question about a total. A total says nothing about whether a typical case is typical: three cases of 1, 1 and 40 days have the same sum as eleven cases of 4, and an average of 14 describes neither. Case handling time, claim size, transaction amount, response time: the same thing is true of all of them, and the figure people quote is the one the shape makes misleading.
Byggekommando
python3 examples/38-distribution/generate_data.py python3 tools/build_distribution.py \ --data examples/38-distribution/saksbehandlingstid.csv \ --measure-col Dager \ --group-col Enhet \ --title "Saksbehandlingstid per enhet" \ --source "Syntetisk eksempeldata (saksbehandlingstid.csv)" \ --requested-by "Fagavdelingen" \ --out examples/38-distribution/behandlingstid.html - scorecard · 39
Many measures, one period, with their targets
The monthly management report has the same shape almost everywhere: a short list of measures, each with this period's figure, the change, the target and a status. Two things make it useful, and neither existed in this repository before this artefact: a trend small enough to sit beside the number, and a target to be measured against.
Byggekommando
python3 examples/39-scorecard/generate_data.py python3 tools/build_scorecard.py \ --data examples/39-scorecard/manedstall.csv \ --title "Månedsrapport oktober 2026" \ --source "Syntetisk eksempeldata (manedstall.csv)" \ --requested-by "Kvalitetsavdelingen" \ --green-at 1.00 --amber-at 0.95 \ --out examples/39-scorecard/manedsrapport.html - waterfall · 40
The steps that add up to a change
Reporting usually stops at "the number went from 12,1 to 13,1 million". The next question is always the same: which parts account for it, and do they add up? That answer lives in a spreadsheet somebody keeps by hand, where the arithmetic is invisible and the total is whatever the last person typed. Here it is an artefact: one self-contained file where every step is drawn, every contributor is written out with its own two figures, and the column adds up on the page.
Byggekommando
python3 examples/40-waterfall/generate_data.py python3 tools/build_waterfall.py \ --data examples/40-waterfall/omsetning.csv \ --measure Omsetning --dim Produkt --period-col Måned \ --from September --to Oktober \ --title "Omsetning per produkt: september mot oktober" \ --source "Syntetisk eksempeldata (omsetning.csv)" \ --requested-by "Innsikt" \ --out examples/40-waterfall/bro.html - composition · 41
What the total is made of, and where the mix and the level disagree
Every artefact before this one answers a question about a level (a dashboard), a change in it (timeline, comparison, waterfall), or the shape of one measure (distribution). None answers the question a management report asks most often: what is this total made of, and how did the mix move?
Byggekommando
python3 examples/41-composition/generate_data.py python3 tools/build_composition.py \ --data examples/41-composition/bemanning.csv \ --measure Timer --cat Oppgave --period-col Kvartal \ --title "Hva timene gikk til: 2026" \ --source "Syntetisk eksempeldata (bemanning.csv)" \ --requested-by "Bemanningskontoret" \ --out examples/41-composition/sammensetning.html - funnel · 42
How many reach each stage, and where the cohort leaks
Most operational reporting is a funnel: how many cases start, how many get assessed, how many are decided, how many are paid out. It is drawn badly almost everywhere, in two ways. The stages are inferred from the data rather than declared, so the order is a guess presented as a fact. And the drop between two stages is left as a gap in a picture, when the number a reader actually wants is the difference.
Byggekommando
python3 examples/42-funnel/generate_data.py python3 tools/build_funnel.py \ --data examples/42-funnel/soknader.csv \ --id Soknadsnr --stage Status \ --stages "Mottatt,Vurdert,Innstilt,Utbetalt" \ --title "Søknader gjennom behandlingen" \ --source "Syntetisk eksempeldata (soknader.csv)" \ --requested-by "Saksbehandlingen" \ --out examples/42-funnel/tratt.html - heatmap · 43
Where the mass is, and where there is none
A heatmap is the easiest artefact in this repository to draw wrongly, because every mistake is invisible in a screenshot. A shade nobody can convert back into a number. A legend whose ranges do not match the shades actually used. An empty cell drawn as zero. A ramp that has no direction. White text on a pale cell. Each one looks fine and each one lies.
Byggekommando
python3 examples/43-heatmap/generate_data.py python3 tools/build_heatmap.py \ --data examples/43-heatmap/henvendelser.csv \ --row Enhet --col Uke --measure Antall \ --title "Henvendelser per enhet og uke" \ --source "Syntetisk eksempeldata (henvendelser.csv)" \ --requested-by "Kundeservice" \ --out examples/43-heatmap/varmekart.html - cohort · 44
Of the people who started, how many are still here
A cohort grid is the one artefact in this repository whose column axis is not in the file. The columns are periods since each row's own start, derived from every person's first date, and that derivation is where the shape is drawn wrongly almost every time. Each mistake looks completely normal in a screenshot.
Byggekommando
python3 examples/44-cohort/lag_data.py python3 tools/build_cohort.py \ --data examples/44-cohort/kunder.csv \ --id Kunde --date Dato \ --title "Hvor mange blir med videre" \ --source "Syntetisk eksempeldata (kunder.csv)" \ --requested-by "Kundeservice" \ --out examples/44-cohort/kohort.html - smallmultiples · 45
Which of these groups behaves differently
A small multiple is the one chart that answers a comparison question, and it has exactly one requirement: every panel must share a vertical scale. Give each panel its own axis and a group moving between 4 and 8 looks precisely like one moving between 400 and 800. Every number in the picture is still true, the page is beautiful, and the reading is wrong. verifya11y sees nothing, because nothing is unreadable.
Byggekommando
python3 examples/45-smallmultiples/lag_data.py python3 tools/build_smallmultiples.py \ --data examples/45-smallmultiples/ko.csv \ --group Enhet --period Uke --measure Saker \ --title "Saker i ko, per enhet og uke" \ --source "Syntetisk eksempeldata (ko.csv)" \ --requested-by "Driftsavdelingen" \ --out examples/45-smallmultiples/smaaflater.html - rank change · 46
Who moved, and how far
"Who is top now" is a sorted list. "Who moved" is a different question, and a table answers it badly: the reader compares two long columns by eye and gives up somewhere in the middle. Two ranked columns with a line per entity answers it in one glance.
Byggekommando
python3 examples/46-rank-change/lag_data.py python3 tools/build_rank_change.py \ --data examples/46-rank-change/produkter.csv \ --entity Produkt --period Kvartal --measure Omsetning \ --from 2026-K1 --to 2026-K2 \ --title "Hvem klatret, hvem falt" \ --source "Syntetisk eksempeldata (produkter.csv)" \ --requested-by "Salg" \ --out examples/46-rank-change/rang.html - scatter · 47
Do these two measures move together
Every other artefact here answers "how much" or "in what order". This one answers the question behind half the requests that arrive as two columns and no chart: do these two move together, and which unit does not?
Byggekommando
python3 examples/47-scatter/generate_data.py python3 tools/build_scatter.py \ --data examples/47-scatter/enheter.csv \ --label Enhet --x-col Svartid --y-col Tilfredshet \ --trend \ --title "Svartid mot tilfredshet, per enhet" \ --source "Syntetisk eksempeldata (enheter.csv)" \ --requested-by "Kundeservice" \ --out examples/47-scatter/spredning.html - boxplot · 48
Which of these is wide, which is late, and which has a tail
A distribution per group is the honest answer to "how long does this take", and it stops working at about six groups: six histograms is a wall. A box plot puts twenty groups on one axis, which is the only shape here that answers the question for a whole list at once.
Byggekommando
python3 examples/48-boxplot/generate_data.py python3 tools/build_boxplot.py \ --data examples/48-boxplot/saksbehandlingstid.csv \ --value Dager --group Enhet \ --title "Saksbehandlingstid per enhet" \ --source "Syntetisk eksempeldata (saksbehandlingstid.csv)" \ --requested-by "Fagavdelingen" \ --out examples/48-boxplot/boksplott.html - gantt · 49
Was the plan kept, and by how much did it slip
A timeline (35) shows when things happen. This shows whether the plan held: for every task, one bar for what was planned and one for what happened, on the same date axis, with the difference printed as a signed number of days.
Byggekommando
python3 examples/49-gantt/generate_data.py python3 tools/build_gantt.py \ --data examples/49-gantt/fremdrift.csv \ --task Tiltak --start Planstart --end Planferdig \ --actual-start Faktisk-start --actual-end Faktisk-ferdig \ --today 2026-10-26 \ --title "Fremdrift per tiltak" \ --source "Syntetisk eksempeldata (fremdrift.csv)" \ --requested-by "Prosjektkontoret" \ --out examples/49-gantt/fremdrift.html - calendar · 50
When in the week, and when in the year
A heatmap (43) compares two categories. This compares days: one cell per day, aligned to weekday rows, so "Mondays are always bad" and "August is quiet" become shapes instead of two hundred rows of a table.
Byggekommando
python3 examples/50-calendar/generate_data.py python3 tools/build_calendar.py \ --data examples/50-calendar/hendelser.csv \ --date Dato --value Antall --clamp 40 \ --title "Hendelser per dag, 2026" \ --source "Syntetisk eksempeldata (hendelser.csv)" \ --requested-by "Driftsavdelingen" \ --out examples/50-calendar/kalender.html - bullet · 51
Which of these is behind its target
A scorecard (39) judges a handful of measures over time. This judges a list: fourteen units, each with a number and its own target, and the question is which of them is off plan. A table of two columns makes the reader do the subtraction; a bar chart with no target line hides the only thing that matters.
Byggekommando
python3 examples/51-bullet/generate_data.py python3 tools/build_bullet.py \ --data examples/51-bullet/dekning.csv \ --label Enhet --value Dekning --target Maal --direction up \ --target-note "styremøtet 12.05.2026" \ --title "Dekning mot mål per enhet" \ --source "Syntetisk eksempeldata (dekning.csv)" \ --requested-by "Fagavdelingen" \ --out examples/51-bullet/dekning.html - pareto · 52
Which few explain most of it
A composition (41) asks what a total is made of and draws every part. This asks which parts matter: categories sorted by size, with the cumulative share, and a long tail folded into one bar that carries its count.
Byggekommando
python3 examples/52-pareto/generate_data.py python3 tools/build_pareto.py \ --data examples/52-pareto/henvendelser.csv \ --label Arsak --value Antall \ --title "Henvendelser per årsak" \ --source "Syntetisk eksempeldata (henvendelser.csv)" \ --requested-by "Kundesenteret" \ --out examples/52-pareto/arsaker.html - sankey · 53
Where the volume went between stages, and that it adds up
A funnel (42) counts one cohort through declared stages in a line. This traces volume through a network: several starts, several ends, flows that meet in the middle. The question is where the volume went, and the picture is only worth having if it is arithmetically true.
Byggekommando
python3 examples/53-sankey/generate_data.py python3 tools/build_sankey.py \ --data examples/53-sankey/kanaler.csv \ --from Kanal --to Utfall --value Antall \ --title "Hvor ble henvendelsene av" \ --source "Syntetisk eksempeldata (kanaler.csv)" \ --requested-by "Kundesenteret" \ --out examples/53-sankey/kanaler.html - task space · 55
A task space: one declared workflow in three dimensions
A spatial, rotatable view of a declared sequence. Select a step to read its purpose. This example exists to show when raw WebGL adds a useful way to explore a relationship without adding a library, a server or a rendering dependency. It is a view, not a process engine or a simulation.
Byggekommando
python3 examples/55-task-space/generate_data.py python3 tools/build_task_space.py \ --data examples/55-task-space/trinn.csv \ --step Step --label Title --description Description --x X --y Y --z Z \ --title "Fra spørsmål til leser" \ --source "Syntetisk arbeidsflyt (trinn.csv)" \ --requested-by "Prosjekteier" \ --lang nb \ --out examples/55-task-space/arbeidsflyt.html
Ingen eksempler passer. Prøv et kortere ord.