Writing an editorial
How to add an editorial or a material: file locations, frontmatter fields, MDX and math, and where the visualization goes.
Terakhir diperbarui 11 Oktober 2026
Halaman ini belum diterjemahkan ke bahasa Indonesia. Halaman yang ditampilkan dalam bahasa Inggris.
Editorials and materials are plain MDX files committed to the repository. Content has no database. Files under content/ are compiled at build time.
Where files go
- Editorials:
content/editorials/<subject>/<slug>.mdx - Materials:
content/materials/<subject>/<slug>.mdx
<subject> is informatics, physics or astronomy. The URL's slug comes from the filename and not the title, so renaming a title never changes a published URL.
Editorial frontmatter
| Field | Notes |
|---|---|
title | Up to 120 characters. |
subject | Must match the folder the file is in. |
hook | Up to 280 characters. One sentence that makes someone want to read on. |
tags | Short, lowercase labels. |
principle | The general idea this editorial teaches. Free text on purpose. |
errorType | Optional. The specific mistake it corrects. Free text. |
vizConfig | Which visualization engine to use, and its configuration. |
publishedAt | An ISO date, 2020 or later. Earlier dates are rejected as placeholder data. |
updatedAt | Optional ISO date for the last editorial or visualization update. Falls back to publishedAt when omitted. |
status | published (the default) or draft. Draft editorials are visible to editors only and hidden from everyone else. |
author | Who wrote it. |
syllabusTopic | Optional. A topic id, or a subtopic id, from the syllabus for the same subject. |
practiceLinks | Optional. A list of { title, url, checkedOn } for practice problems on other sites (for example TLX). url must be https://. See below. |
The body is everything after the frontmatter, written in Markdown/MDX.
Math
Use $...$ for inline math and $$...$$ for display math. Both compile to static KaTeX HTML at build time.
Placing the visualization
Put <Interactive /> on its own line where the visualization should appear, usually between the idea and the full proof. If you forget it, the visualization still renders, just after the hook with a small notice, so an editorial never ships without one.
Hiding the solution until the reader asks
Wrap a worked solution or proof in <Solution> on its own lines, with a blank line after the opening tag and before the closing tag:
<Solution>
## Full proof
...
</Solution>
By default everyone sees the full text. A reader can switch on "Try it first" (saved only in their browser), and then solutions collapse behind a "Show solution" control. Every <Solution> must be closed, and pnpm test checks that.
Practice links
Use practiceLinks to point at problems that belong to someone else. Link only: do not copy a problem statement. Set checkedOn to the day you last opened the link. After 180 days the page marks the link as possibly moved, so re-check it and update the date.
Choosing a visualization engine
Set vizConfig.discriminant to one of graph-array-stepper, trajectory-sandbox, orbital-sandbox, blackbody-spectrum-comparison, relative-period-sandbox, composed-scene or programmable-scene. The first five are configured in frontmatter. The last two are built visually in the scene builder. Copy from these real examples:
content/editorials/informatics/binary-search-on-answer.mdxforgraph-array-steppercontent/editorials/physics/projectile-range-symmetry.mdxfortrajectory-sandboxcontent/editorials/astronomy/eccentric-transit-duration.mdxfororbital-sandboxcontent/editorials/astronomy/why-classical-blackbody-radiation-fails.mdxforblackbody-spectrum-comparisoncontent/editorials/astronomy/why-a-sidereal-day-is-shorter.mdxforrelative-period-sandbox
Adding a challenge to a trajectory sandbox
A trajectory-sandbox can pose a problem instead of only offering sliders. Add an optional challenge next to speedRange and angleRange in vizConfig.value:
challenge:
prompt: "Land the ball on the marker. Use $R = v^2 \\sin 2\\theta / g$ to plan your launch."
target: { rangeM: 60, toleranceM: 2 }
hint: "Two different angles reach the same range."
successNote: "That is the symmetry behind this editorial."
prompt, hint and successNote are Markdown with $...$ math. The reader sets a launch, presses Play, and when the flight ends the page checks that the landing is within toleranceM of rangeM. After two misses the hint appears. Attempts are counted in the reader's browser only.
The page refuses to build a challenge that cannot be won. rangeM and toleranceM must be positive numbers, and some setting the sliders can actually produce (in whole steps of 1 m/s and 1°, within speedRange and angleRange, or the defaults of 5 to 40 m/s and 5° to 85°) must land inside the tolerance. If it cannot, the visualization shows a configuration error. Check a new target with pnpm test, or open the page.
Keystatic does not know about challenge yet, so an editorial that has one cannot be opened in the Keystatic form (the same limit as a programmable scene). Edit it in the file.
Materials
A material is shorter and unsigned. It has a title, subject, topic (a syllabus topic id or subtopic id), summary (up to 280 characters) and a body. There is at most one material per topic or subtopic. A visualization is optional: set vizConfig the same way as for an editorial (the first three engines; the scene builder only writes editorials) and place <Interactive /> where it belongs. With discriminant: none, or no vizConfig at all, a material simply has no visualization. See content/materials/informatics/binary-search.mdx for an example.
Checking your work
Run pnpm generate to validate frontmatter and pnpm test to check that every material and editorial points at a real syllabus topic. Never rename a syllabus topic's or subtopic's id. Change its name instead.
Languages
An editorial or material is written in one language, set by the optional language field (en by default). To translate it, add a file next to it named <slug>.id.mdx (or .en.mdx) holding only the translated title, hook (summary for a material) and body. Readers in that language get the translation; everyone else sees the original with a notice. You never switch the notice off by hand: it appears only while the translation is missing.
Editing in the browser
You can also write and edit content in the browser. See Editing with Keystatic.