Report template kit

CCM report template

One JSON file and one Python script build a standalone report with paper texture, ink drawings, and a stage that changes as the reader scrolls.

A swatch repaints this page, so you can read a theme before you take it. It changes nothing in the kit: the theme block of report.json holds the five colors, and that block is what you copy into your own report.

Individual voices. Shared public knowledge.
  1. Copy the folder for your new report. The download unpacks to a folder that already builds.

    unzip template.zip
    cd CCM-report-template
  2. Edit report.json with a text editor. Every heading, paragraph, statistic, link, and color on the page comes from that one file.

  3. Run the build from the copied folder. Python 3.11 or later works with no packages to install.

    python3 build.py
  4. Open index.html in a browser. The export is one file and it opens from disk.

Build a second report beside the template by naming its content file and its output.

python3 build.py my-report.json --output ../my-report/index.html

To load the stylesheet and the script from this folder instead of embedding them, pass the base URL. That export needs the network; the default one does not.

python3 build.py report.json --output starter.html \
  --assets-url https://jamditis.com/ccm-report-template

Rebuild after each edit. Changes typed into the exported HTML disappear on the next build.

The marks
11 strokes
9 strokes
6 strokes

Every drawing holds 12 marks. Stroke count, size, weight, and jitter come from the mark's own index, so each one is a sibling of the others and none is a copy. They are an illustration, not a chart.

The palette
  • paper #f6f3ebPage and card background
  • ink #292925Text, marks, primary button
  • accent #a33e36Highlights, numerals, focus ring
  • secondary #466958Connection lines and cover caption
  • wash #e7e4d9Dividers, code, methods band

Every other color on the page is mixed from these five, so a new theme moves the whole report and leaves no stranded gray behind.

The type

A story is only the beginning.

Display: the cover, the headings, the large numerals.

Follow the connections.

Body: the sentences, and the labels beside them.

Three roles carry the page, and a preset fills all three. Four presets ship with the kit. A report that names none of them sets in the reader's own faces and asks no font server for anything.

A preset names the display face, the body face and the label face at once, and every one of them carries a display weight of 800 or more. The files are latin subsets under fonts/, each with its OFL licence beside it, and the build writes the ones your preset needs into the page as data, so an export still opens from a disk with no network. The system preset embeds nothing.

system

The reader's own faces, and no file to download. The display weight is 800, which the stack draws where the face has it.

{
  "fonts": {
    "preset": "system"
  }
}
Example cover

A story is only the beginning.

A reporter asks a question. A resident shares what they know. An editor connects the account to a wider story.

1231

field

Figtree gets out of the way of the drawing; Bricolage Grotesque at 800 carries the cover and the numerals.

{
  "fonts": {
    "preset": "field"
  }
}
Example cover

A story is only the beginning.

A reporter asks a question. A resident shares what they know. An editor connects the account to a wider story.

1231

quill

Fraunces at 900, soft and wonky, echoes the hand-drawn marks; Nunito Sans keeps the body warm.

{
  "fonts": {
    "preset": "quill"
  }
}
Example cover

A story is only the beginning.

A reporter asks a question. A resident shares what they know. An editor connects the account to a wider story.

1231

ink

One variable file for the whole page: casual at 1000 for display, linear at 400 for the body.

{
  "fonts": {
    "preset": "ink"
  }
}
Example cover

A story is only the beginning.

A reporter asks a question. A resident shares what they know. An editor connects the account to a wider story.

1231

Both layouts set the reading column at 34em, so a line holds 62 to 66 characters in every preset. The wide layout moves that column to the left gutter and opens a rail beside it for the notes, the pull quotes and any figure or table you mark with "rail": true. Below 1000 pixels the rail folds into the column at the place you wrote each block, so a phone reads one column in both layouts.

reading

The centered column the kit shipped with. A note goes to the margin beside it where the window is wide enough, and to the foot of the section where it is not.

{
  "layout": "reading"
}
The rail

A note and a lifted sentence stand beside the column.

A reporter asks a question. A resident shares what they know. An editor connects the account to a wider story. Each exchange gives information a place to go.1

The column holds 34em in both layouts, so no line runs past 66 characters. What changes is where the column sits and what stands beside it.

Notes

  1. An invented example. The wide layout puts this note in the rail. Every narrower window puts it at the foot of the section. Back to the text

Measured in newsrooms.

The count rose by 120 newsrooms across the four years. These numbers are invented for the guide.

Invented for this specimencenterforcooperativemedia.org

Show the data
CategoryValue
2022210
2023268
2024305
2025330

wide

The same column against the left gutter, with the rail beside it. The figure keeps the full figure width and breaks past the column on purpose.

{
  "layout": "wide"
}
The rail

A note and a lifted sentence stand beside the column.

A reporter asks a question. A resident shares what they know. An editor connects the account to a wider story. Each exchange gives information a place to go.1

The column holds 34em in both layouts, so no line runs past 66 characters. What changes is where the column sits and what stands beside it.

Notes

  1. An invented example. The wide layout puts this note in the rail. Every narrower window puts it at the foot of the section. Back to the text

Measured in newsrooms.

The count rose by 120 newsrooms across the four years. These numbers are invented for the guide.

Invented for this specimencenterforcooperativemedia.org

Show the data
CategoryValue
2022210
2023268
2024305
2025330

Give every section a unique id that starts with a lowercase letter, such as key-findings. Use letters, digits, and hyphens. The ids main and top are reserved. The contents menu follows the order of the sections, and quote sections are left out of it because they carry no title.

TypeFields beyond type and id
summarytitle, text (under 100 words)
takeawayslabel, title, items (list of text, 2 to 5)
proselabel, title, paragraphs (list of text, or a block object)
stepslabel, title, items: each has title and text
glossarytitle, terms: each has term and definition
scrollylabel, title, optional center_label, steps
waterfalllabel, title, dataset (title, unit, totalSquares, status, source, caveat, groups)
findingslabel, title, items: each has value, title, text, and optional unit, note and trend
figurelabel, title, chart: kind, unit, value_format and the values the kind reads, caption, and optional source, note and rail
tabletitle, caption, columns: each has label, type and optional unit, rows, sort, and optional note, source and rail
quotetext, attribution
tabslabel, title, panels: each has label and paragraphs
accordionlabel, title, items: each has summary and paragraphs; optional exclusive
timelinelabel, title, events: each has date, title, text
imagesrc, alt, optional caption, credit, decorative, lightbox, and width of reading, figure or bleed
comparelabel, title, before and after: each has src, alt, label; optional caption
gallerylabel, title, items: each has src, alt, caption
diagramlabel, title, nodes: id, label; edges: from, to, optional label; layout of row, column or ring; optional caption
partnumber, title, optional deck
methodstitle, paragraphs, optional brief, sources: each has label, url

A scrolly section needs at least one step, and each step names a layout of scatter, groups, network, or orbit, a caption describing the state in plain text, and a highlight. The README.md in the download lists every field and what it accepts.

summary

A compressed abstract with its own landmark, for the top of a report.

Use it when a reader decides in one screen whether to read on.

{
  "type": "summary",
  "id": "summary-specimen",
  "title": "In brief",
  "text": "An invented example. This block states what the report holds, so a reader can decide to continue. Write it last, after the report is finished. Do not repeat the standfirst: the standfirst offers the report and this block states its content."
}

An invented example. This block states what the report holds, so a reader can decide to continue. Write it last, after the report is finished. Do not repeat the standfirst: the standfirst offers the report and this block states its content.

takeaways

A short list of the points a reader keeps, on a tinted block.

Use it at the top, when a reader who stops here needs the result.

{
  "type": "takeaways",
  "id": "takeaways-specimen",
  "label": "Example takeaways",
  "title": "Keep these three points.",
  "items": [
    "Shared reporting reaches readers that one newsroom cannot reach alone.",
    "A collaboration needs one editor who holds the schedule.",
    "An invented example. Replace each line with a point your report proves."
  ]
}
Example takeaways

Keep these three points.

  • Shared reporting reaches readers that one newsroom cannot reach alone.
  • A collaboration needs one editor who holds the schedule.
  • An invented example. Replace each line with a point your report proves.

prose

A heading and a run of paragraphs, with five blocks a run can hold.

Use it when the reader needs the argument in sentences.

{
  "type": "prose",
  "id": "prose-specimen",
  "label": "The question",
  "title": "A story is only the beginning.",
  "paragraphs": [
    "A reporter asks a question. A resident shares what they know. An editor connects the account to a wider story. Each exchange gives information a place to go.[^1] When two newsrooms answer one question together, the work becomes {collaborative reporting}.",
    {
      "pullquote": "Each exchange gives information a place to go."
    },
    {
      "subhead": "What a block does"
    },
    "A paragraph can also be a block. Every block keeps the reading column, and none of them adds a card, an icon or a color.",
    {
      "list": [
        "A subhead opens one part of the argument.",
        "A list holds items a reader scans.",
        "A pull quote lifts one sentence the reader has already read."
      ],
      "ordered": false
    },
    {
      "callout": {
        "title": "One fact, set aside",
        "text": "An invented example. A callout holds a fact that matters and would break the sentence beside it."
      }
    },
    {
      "caveat": {
        "text": "An invented example. The sample is too small to carry a claim about the whole state."
      }
    }
  ],
  "notes": [
    "An invented example. A note holds the source or the limit that would break the sentence."
  ]
}
The question

A story is only the beginning.

A reporter asks a question. A resident shares what they know. An editor connects the account to a wider story. Each exchange gives information a place to go.1 When two newsrooms answer one question together, the work becomes collaborative reporting.

What a block does

A paragraph can also be a block. Every block keeps the reading column, and none of them adds a card, an icon or a color.

  • A subhead opens one part of the argument.
  • A list holds items a reader scans.
  • A pull quote lifts one sentence the reader has already read.

Notes

  1. An invented example. A note holds the source or the limit that would break the sentence. Back to the text

steps

A numbered sequence, one numeral and one instruction a step.

Use it when the reader has to do the work in order.

{
  "type": "steps",
  "id": "steps-specimen",
  "label": "Example steps",
  "title": "Start a shared reporting project.",
  "items": [
    {
      "title": "Agree on one question",
      "text": "An invented example. Write the question every partner answers, and keep it to one sentence."
    },
    {
      "title": "Name one editor",
      "text": "One editor holds the schedule and answers every partner. The role moves between projects."
    },
    {
      "title": "Set the publication date",
      "text": "Every partner publishes on the same day, so the work reaches readers as one story."
    }
  ]
}
Example steps

Start a shared reporting project.

  1. Agree on one question

    An invented example. Write the question every partner answers, and keep it to one sentence.

  2. Name one editor

    One editor holds the schedule and answers every partner. The role moves between projects.

  3. Set the publication date

    Every partner publishes on the same day, so the work reaches readers as one story.

glossary

An alphabetized list of terms, with an anchor on every term.

Use it when the report needs words a general reader does not hold.

{
  "type": "glossary",
  "id": "glossary-specimen",
  "title": "Terms used in this report",
  "terms": [
    {
      "term": "shared beat",
      "definition": "One reporter who covers a subject for several newsrooms at once. The newsrooms share the cost and publish the same reporting."
    },
    {
      "term": "collaborative reporting",
      "definition": "Two or more newsrooms that report one story together. A {shared beat} is one form of it."
    }
  ]
}

Terms used in this report

collaborative reporting
Two or more newsrooms that report one story together. A shared beat is one form of it.
shared beat
One reporter who covers a subject for several newsrooms at once. The newsrooms share the cost and publish the same reporting.

collaborative reporting

Two or more newsrooms that report one story together. A shared beat is one form of it.

Open the glossary entry

shared beat

One reporter who covers a subject for several newsrooms at once. The newsrooms share the cost and publish the same reporting.

Open the glossary entry

scrolly

A drawing that holds its place while short cards change it.

Use it when one picture carries the whole argument and each step changes it.

{
  "type": "scrolly",
  "id": "scrolly-specimen",
  "label": "Follow the connections",
  "title": "From one newsroom to a shared network.",
  "steps": [
    {
      "title": "Start with the people doing the work.",
      "text": "Each mark stands for one imagined newsroom. At first, the marks sit apart.",
      "caption": "12 imagined newsrooms, working independently",
      "layout": "scatter",
      "highlight": 0
    },
    {
      "title": "Keep the community at the center.",
      "text": "The shared work matters when people can find and use it.",
      "caption": "Shared work returns to a common public purpose",
      "layout": "orbit",
      "highlight": 0
    }
  ]
}
1 of 2

Start with the people doing the work.

Each mark stands for one imagined newsroom. At first, the marks sit apart.

12 imagined newsrooms, working independently

2 of 2

Keep the community at the center.

The shared work matters when people can find and use it.

Shared work returns to a common public purpose

The live version pins the drawing while the reader scrolls past each step: see it in the sample report.

waterfall

Equal squares fall into place as the reader scrolls, from small groups to the whole population.

Use it for non-overlapping groups with a known total. Readers can edit the data and export their own chart.

{
  "type": "waterfall",
  "id": "waterfall-specimen",
  "label": "A matter of scale",
  "title": "See the whole audience.",
  "dataset": {
    "version": 1,
    "title": "Program reach, in perspective",
    "unit": "people",
    "totalSquares": 2500,
    "status": "Fictional example · not a measured population",
    "source": "Entirely invented figures for demonstrating the storytelling template. These are not results from a real program, organization, or community.",
    "caveat": "The four example groups are mutually exclusive and total 50,000 people. Replace all figures and narration with your own validated data before publication. “Not yet reached” should only be used when the full target population is known.",
    "summaryHeadline": "The audience you know\nis not the whole audience.",
    "groups": [
      {
        "id": "regular",
        "label": "Regular participants",
        "shortLabel": "Regular",
        "value": 420,
        "color": "#ec3864",
        "headline": "The people\nyou know best.",
        "description": "The regulars: {value} {unit}. They are highly visible, but occupy only {tiles} squares in this fictional population."
      },
      {
        "id": "occasional",
        "label": "Occasional participants",
        "shortLabel": "Occasional",
        "value": 1680,
        "color": "#e7bb27",
        "headline": "A wider\ncircle.",
        "description": "Add the occasional participants. The picture grows, but the people actively involved are still a small group."
      },
      {
        "id": "aware",
        "label": "Reached, not participating",
        "shortLabel": "Reached",
        "value": 7900,
        "color": "#00b982",
        "headline": "Awareness\nis not engagement.",
        "description": "These people have been reached by the fictional program but do not participate. A different relationship, and a bigger band."
      },
      {
        "id": "unreached",
        "label": "Not yet reached",
        "shortLabel": "Not reached",
        "value": 40000,
        "color": "#3e4740",
        "headline": "Beyond\nthe usual circle.",
        "description": "The largest group: people the fictional program has not yet reached. This is what a participant-only view would leave out."
      }
    ]
  }
}
A matter of scale

See the whole audience.

Fictional example · not a measured population

Program reach, in perspective
GroupValue
Regular participants420 people
Occasional participants1,680 people
Reached, not participating7,900 people
Not yet reached40,000 people

Entirely invented figures for demonstrating the storytelling template. These are not results from a real program, organization, or community.

The four example groups are mutually exclusive and total 50,000 people. Replace all figures and narration with your own validated data before publication. “Not yet reached” should only be used when the full target population is known.

findings

A row of statistics, each with a numeral, a heading and a sentence.

Use it when three or four numbers carry the result.

{
  "type": "findings",
  "id": "findings-specimen",
  "label": "Example findings",
  "title": "Give the evidence space.",
  "items": [
    {
      "value": "12",
      "title": "Participating newsrooms",
      "text": "An illustrative count. Replace this with a verified result and its source.",
      "unit": "newsrooms",
      "note": "Illustrative count for 2024 to 2026.",
      "trend": [
        4,
        6,
        9,
        11,
        12
      ]
    },
    {
      "value": "3",
      "title": "Shared reporting groups",
      "text": "Explain what this number measures and the period it covers.",
      "unit": "groups"
    }
  ]
}
Example findings

Give the evidence space.

12From 4 to 12 over 5 points.newsrooms

Participating newsrooms

An illustrative count. Replace this with a verified result and its source.

Illustrative count for 2024 to 2026.

3groups

Shared reporting groups

Explain what this number measures and the period it covers.

figure

A chart with direct labels, a one-sentence trend, and its values in a table.

Use it when the shape of the numbers carries the point.

{
  "type": "figure",
  "id": "figure-specimen",
  "label": "Example figure",
  "title": "Partner newsrooms grew in every year of the example set.",
  "chart": {
    "kind": "bar",
    "unit": "newsrooms",
    "value_format": "integer",
    "sort": false,
    "bars": [
      {
        "label": "2022",
        "value": 210
      },
      {
        "label": "2023",
        "value": 268
      },
      {
        "label": "2024",
        "value": 305
      },
      {
        "label": "2025",
        "value": 330
      }
    ]
  },
  "caption": "The count rose by 120 newsrooms across the four years. These numbers are invented for the guide.",
  "source": {
    "label": "Invented for this specimen",
    "url": "https://centerforcooperativemedia.org/"
  }
}

Measured in newsrooms.

The count rose by 120 newsrooms across the four years. These numbers are invented for the guide.

Invented for this specimencenterforcooperativemedia.org

Show the data
CategoryValue
2022210
2023268
2024305
2025330

table

A sortable table with right-aligned numerals and a sticky head.

Use it when the reader needs every value, not the shape of them.

{
  "type": "table",
  "id": "table-specimen",
  "title": "Grants by program and year",
  "caption": "Every value here is invented for the guide. Replace them with verified figures.",
  "columns": [
    {
      "label": "Program",
      "type": "text"
    },
    {
      "label": "Grants",
      "type": "number"
    },
    {
      "label": "Newsrooms",
      "type": "number"
    }
  ],
  "rows": [
    [
      "Local reporting",
      12,
      34
    ],
    [
      "Civic technology",
      5,
      11
    ],
    [
      "Language access",
      8,
      19
    ]
  ]
}
Every value here is invented for the guide. Replace them with verified figures.
Local reporting1234
Civic technology511
Language access819

quote

One sentence at display size, opened by a short accent rule.

Use it when one voice states the point better than the report can.

{
  "type": "quote",
  "id": "quote-specimen",
  "text": "A useful report lets the reader see the evidence, follow the reasoning, and ask a better question.",
  "attribution": "Template editorial principle"
}

A useful report lets the reader see the evidence, follow the reasoning, and ask a better question.

Template editorial principle

tabs

Two to five panels of prose under a row of text tabs.

Use it when a reader compares parallel views instead of reading them in order.

{
  "type": "tabs",
  "id": "tabs-specimen",
  "label": "Example views",
  "title": "One question, three answers.",
  "panels": [
    {
      "label": "Reporters",
      "paragraphs": [
        "An invented view for the template. State what this group gains and what it gives up.",
        "Keep each panel about the same length, so no view reads as the important one."
      ]
    },
    {
      "label": "Editors",
      "paragraphs": [
        "An invented view for the template. Replace it with the second position and its evidence."
      ]
    },
    {
      "label": "Readers",
      "paragraphs": [
        "An invented view for the template. Replace it with the third position and its evidence."
      ]
    }
  ]
}

Reporters

An invented view for the template. State what this group gains and what it gives up.

Keep each panel about the same length, so no view reads as the important one.

Editors

An invented view for the template. Replace it with the second position and its evidence.

Readers

An invented view for the template. Replace it with the third position and its evidence.

accordion

Summaries a reader opens for the detail under each one.

Use it when most readers skip the detail and a few readers need all of it.

{
  "type": "accordion",
  "id": "accordion-specimen",
  "label": "Example questions",
  "title": "The questions readers ask most.",
  "exclusive": true,
  "items": [
    {
      "summary": "How were the newsrooms selected?",
      "paragraphs": [
        "An invented answer for the template. State the sampling frame and who it leaves out."
      ]
    },
    {
      "summary": "What does the count measure?",
      "paragraphs": [
        "An invented answer for the template. State the unit, the period, and the source.",
        "Keep the summary above useful on its own, because many readers never open it."
      ]
    }
  ]
}
How were the newsrooms selected?

An invented answer for the template. State the sampling frame and who it leaves out.

What does the count measure?

An invented answer for the template. State the unit, the period, and the source.

Keep the summary above useful on its own, because many readers never open it.

timeline

Dated events in one column, with the date beside each one.

Use it when the order of the events is the argument.

{
  "type": "timeline",
  "id": "timeline-specimen",
  "label": "Example chronology",
  "title": "How the work moved.",
  "events": [
    {
      "date": "March 2024",
      "title": "The partners agree on one question",
      "text": "An invented event. Replace it with a dated step of your own work."
    },
    {
      "date": "September 2024",
      "title": "The first shared story runs",
      "text": "State what happened and why the date matters. Keep each event to one or two sentences."
    },
    {
      "date": "Spring 2025",
      "title": "The group reports on the result",
      "text": "A date is text, so \"Spring 2025\" and \"12 May 2025\" both hold their own form."
    }
  ]
}
Example chronology

How the work moved.

  1. March 2024

    The partners agree on one question

    An invented event. Replace it with a dated step of your own work.

  2. September 2024

    The first shared story runs

    State what happened and why the date matters. Keep each event to one or two sentences.

  3. Spring 2025

    The group reports on the result

    A date is text, so "Spring 2025" and "12 May 2025" both hold their own form.

image

One picture on the reading column, the figure column or the full page.

Use it when a picture carries evidence a sentence cannot.

{
  "type": "image",
  "id": "image-specimen",
  "src": "data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20width=%22560%22%20height=%22315%22%20viewBox=%220%200%20560%20315%22%3E%3Crect%20width=%22560%22%20height=%22315%22%20fill=%22%23e7e4d9%22/%3E%3Cpath%20fill=%22none%22%20stroke-linecap=%22round%22%20stroke-width=%222%22%20stroke=%22%23292925%22%20d=%22M46%20107Q33%20170%2054%20233M85%20124Q72%20167%2093%20210M124%20141Q111%20164%20132%20187M163%20104Q150%20165%20171%20226M202%20121Q189%20162%20210%20203M241%20138Q228%20190%20249%20242M280%20101Q267%20160%20288%20219M319%20118Q306%20157%20327%20196M358%20135Q345%20185%20366%20235M397%2098Q384%20155%20405%20212M436%20115Q423%20152%20444%20189M475%20132Q462%20180%20483%20228M514%20149Q501%20177%20522%20205%22/%3E%3C/svg%3E",
  "alt": "A field of bowed ink strokes of different heights on a tinted ground.",
  "caption": "This picture is a placeholder. Point src at your own file.",
  "credit": "Drawn by the template",
  "width": "figure",
  "lightbox": true
}
A field of bowed ink strokes of different heights on a tinted ground.
This picture is a placeholder. Point src at your own file.Drawn by the template
A field of bowed ink strokes of different heights on a tinted ground.

compare

Two pictures side by side, with a divider a reader can move.

Use it when one picture changes and the change is the point.

{
  "type": "compare",
  "id": "compare-specimen",
  "label": "Example comparison",
  "title": "Show the change, do not describe it.",
  "before": {
    "src": "data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20width=%22560%22%20height=%22315%22%20viewBox=%220%200%20560%20315%22%3E%3Crect%20width=%22560%22%20height=%22315%22%20fill=%22%23e7e4d9%22/%3E%3Cpath%20fill=%22none%22%20stroke-linecap=%22round%22%20stroke-width=%222%22%20stroke=%22%23292925%22%20d=%22M46%20118Q33%20169%2054%20220M85%20135Q72%20166%2093%20197M124%2098Q111%20167%20132%20236M163%20115Q150%20164%20171%20213M202%20132Q189%20161%20210%20190M241%20149Q228%20189%20249%20229M280%20112Q267%20159%20288%20206M319%20129Q306%20187%20327%20245M358%20146Q345%20184%20366%20222M397%20109Q384%20154%20405%20199M436%20126Q423%20182%20444%20238M475%20143Q462%20179%20483%20215M514%20106Q501%20149%20522%20192%22/%3E%3C/svg%3E",
    "alt": "A field of bowed ink strokes on a tinted ground.",
    "label": "Before"
  },
  "after": {
    "src": "data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20width=%22560%22%20height=%22315%22%20viewBox=%220%200%20560%20315%22%3E%3Crect%20width=%22560%22%20height=%22315%22%20fill=%22%23e7e4d9%22/%3E%3Cpath%20fill=%22none%22%20stroke-linecap=%22round%22%20stroke-width=%222%22%20stroke=%22%23a33e36%22%20d=%22M46%20118Q33%20169%2054%20220M85%20135Q72%20166%2093%20197M124%2098Q111%20167%20132%20236M163%20115Q150%20164%20171%20213M202%20132Q189%20161%20210%20190M241%20149Q228%20189%20249%20229M280%20112Q267%20159%20288%20206M319%20129Q306%20187%20327%20245M358%20146Q345%20184%20366%20222M397%20109Q384%20154%20405%20199M436%20126Q423%20182%20444%20238M475%20143Q462%20179%20483%20215M514%20106Q501%20149%20522%20192%22/%3E%3C/svg%3E",
    "alt": "The same field of strokes, drawn in the accent color.",
    "label": "After"
  },
  "caption": "Both pictures are placeholders. Point src at your own two files, cropped to one size."
}
Before
A field of bowed ink strokes on a tinted ground.
After
The same field of strokes, drawn in the accent color.
Both pictures are placeholders. Point src at your own two files, cropped to one size.

diagram

A few labeled parts, the lines between them, and the same thing in sentences.

Use it when one drawing explains a flow that a sequence of steps would not.

{
  "type": "diagram",
  "id": "diagram-specimen",
  "label": "Example diagram",
  "title": "How one story reaches three newsrooms.",
  "layout": "ring",
  "nodes": [
    {
      "id": "desk",
      "label": "Shared desk"
    },
    {
      "id": "county",
      "label": "County partner"
    },
    {
      "id": "readers",
      "label": "Readers"
    },
    {
      "id": "city",
      "label": "City partner"
    }
  ],
  "edges": [
    {
      "from": "desk",
      "to": "county",
      "label": "sends the draft to"
    },
    {
      "from": "desk",
      "to": "city",
      "label": "sends the draft to"
    },
    {
      "from": "county",
      "to": "readers",
      "label": "publishes for"
    },
    {
      "from": "city",
      "to": "readers",
      "label": "publishes for"
    }
  ],
  "caption": "An invented flow. Replace the nodes and the edges with your own."
}
  1. Shared desk sends the draft to County partner.
  2. Shared desk sends the draft to City partner.
  3. County partner publishes for Readers.
  4. City partner publishes for Readers.
An invented flow. Replace the nodes and the edges with your own.

part

A part number and a part title, with space above and below.

Use it when a report is long enough to read as chapters.

{
  "type": "part",
  "id": "part-specimen",
  "number": 2,
  "title": "What the partners found",
  "deck": "An invented deck. Say in one sentence what this part of the report covers."
}

Part 2

What the partners found

An invented deck. Say in one sentence what this part of the report covers.

methods

A tinted band that carries the method note and the citation list.

Use it at the end, when the reader needs the method and the sources.

{
  "type": "methods",
  "id": "methods-specimen",
  "title": "Methods and sources",
  "brief": "Replace this lead with two plain sentences on what you counted and how you counted it. The note under it carries the detail.",
  "paragraphs": [
    "Replace this note with the study period, the sample, the method, the limits, the funding, and the disclosures."
  ],
  "sources": [
    {
      "label": "Center for Cooperative Media",
      "url": "https://centerforcooperativemedia.org/"
    },
    {
      "label": "Montclair State University",
      "url": "https://www.montclair.edu/"
    }
  ]
}

Methods and sources

How we did this

Replace this lead with two plain sentences on what you counted and how you counted it. The note under it carries the detail.

Replace this note with the study period, the sample, the method, the limits, the funding, and the disclosures.

  1. Center for Cooperative Mediacenterforcooperativemedia.org
  2. Montclair State Universitywww.montclair.edu

authors

A row for each author with the role, the bio and a link.

Use it when the reader needs to know who did the work.

{
  "authors": [
    {
      "name": "Robin Vega",
      "role": "Lead researcher",
      "bio": "An invented author for this specimen. Replace the name, the role and the bio with your own.",
      "url": "https://example.org/people/robin-vega"
    },
    {
      "name": "Sam Okafor",
      "role": "Data reporter",
      "bio": "A second invented author. The cover byline joins the two names."
    }
  ]
}

About the authors

Robin Vega

Lead researcher

An invented author for this specimen. Replace the name, the role and the bio with your own.

Sam Okafor

Data reporter

A second invented author. The cover byline joins the two names.

contributors

A credits list that states the role before the name.

Use it when more hands did the work than the byline shows.

{
  "contributors": [
    {
      "role": "Editing",
      "name": "Dana Whitfield"
    },
    {
      "role": "Illustration",
      "name": "Ola Brandt"
    },
    {
      "role": "Fact checking",
      "name": "Priya Raman"
    }
  ]
}

Credits

Editing
Dana Whitfield
Illustration
Ola Brandt
Fact checking
Priya Raman

form

A one-column form that posts to your endpoint and states what the reader gets.

Use it when a real list is behind it and you can name what it sends.

{
  "form": {
    "endpoint": "https://example.org/subscribe",
    "heading": "Get the next report",
    "reason": "One email a month with the next report and what it found. This specimen sends inline, and the endpoint is a placeholder: a real deployment needs an endpoint that answers with CORS headers, so a send from this page fails.",
    "button": "Subscribe",
    "inline": true
  }
}

Get the next report

corrections

A dated log of what changed after publication.

Use it as soon as you change a published number or a name.

{
  "corrections": [
    {
      "date": "4 March 2026",
      "text": "An invented entry. Corrected the partner count in the findings from 320 to 330."
    },
    {
      "date": "19 March 2026",
      "text": "A second invented entry. Named the county the sample was drawn from."
    }
  ]
}

Corrections

  • 4 March 2026

    An invented entry. Corrected the partner count in the findings from 320 to 330.

  • 19 March 2026

    A second invented entry. Named the county the sample was drawn from.

downloads

A file list inside the methods band that states the format and the size.

Use it when the reader can check your work against the data.

{
  "downloads": [
    {
      "label": "Survey responses",
      "url": "https://example.org/data/responses-2026-03.csv",
      "format": "CSV",
      "size": "412 KB"
    },
    {
      "label": "Method note",
      "url": "https://example.org/data/method-note-2026-03.pdf",
      "format": "PDF",
      "size": "1.2 MB"
    }
  ]
}

The ZIP holds the whole kit: build.py, package.py, report.json, report.css, report.js, favicon.svg, the fonts/ folder with the five faces and their licences, the built index.html, the hosted-asset starter.html, this guide and the template it is built from, README.md, and DESIGN.md. It leaves out the social image, because every published report needs its own.

Four of those files open from here:

  • report.json holds the content of the sample report, including the waterfall chart, its data editor, and the end matter.
  • starter.html loads the stylesheet and the script from this folder.
  • README.md lists every field and what it accepts.
  • DESIGN.md states the design system and the rules it holds to.

Every number, name and quotation in the sample report is invented. Replace the content, the notice, the header links and the template link with your own verified work before you publish.