Skip to content
duxt

Layout and components

The layout a split history renders in, and the two MDC components its pages are made of.

The changelog type writes ordinary Markdown pages whose bodies are two MDC components, rendered inside a layout of its own. All three are documented because they are part of the public surface — a consumer can override any of them — not because a page has to be written by hand.

The changelog layout

Bound by the split granularity only; a flat changelog is an ordinary docs page and renders in the docs chrome.

It differs from docs in two ways, and both follow from what the pages are. The sidebar is the release list rather than a prose tree — same navigation, same collection, because a generated section is an ordinary collection and its pages are the releases. And the reading column is capped at a measure: a release note is prose, and prose set across the full window is unreadable. The contents column stays, listing the groups.

Replace it with a file of your own at the same path:

app/layouts/changelog.vue

ChangelogReleases

The overview's timeline: every release, newest first, filterable by what changed. Written onto the section's index page.

PropTypeNotes
releasesarrayEach with version, date, to and its groups

The history opens on four figures — releases, changes, kinds of change, and the day the last one shipped — each a number with a small labelled line under it, as on the landing page — but laid out as a four-column grid across the full width rather than a wrapping row, so they read as the header of the list under them instead of as a caption with a hole beside it. The kinds are counted over the names the file used, so it is the file's own vocabulary being counted; the date is the only one that is not a count, and it is dropped rather than dashed where the file dates nothing. It answers what a list of rows cannot, which is how much there is. The figures count what is shown, so a filtered page's numbers are about the filtered page, with the total kept beside the first one.

The label carries the accent, not the figure: --primary is the one colour a consuming site sets to make the theme its own, and two short labelled lines are where it costs nothing.

Below them every release is one line in columns — version, badge, what it carried, date — so the versions sit line under line and the summaries start at the same x. Under sm the summary drops to a second line; nothing scrolls sideways. The current release says it is current with a badge beside the version — a badge is about the version, so it sits with it — in a fixed track that exists whether or not a row fills it, so the releases without one start their summary exactly where the one with it does. The date sits at the far end beside the arrow: it is the field a reader scans down rather than reads across.

Filtering animates on both sides — the chips that appear above the list and the rows that move below it, at the same timings, so the two read as one gesture. Rows fade and settle over 200 ms, a leaving row is taken out of the flow so the ones under it close the gap while it goes, and the browser's own FLIP moves the survivors. It is a TransitionGroup, not a motion library — a fade and a nudge are not worth a runtime dependency in every site that extends the layer — and every part of it is off under prefers-reduced-motion. A row names the three largest groups and counts the rest: "Features 2 · Bug Fixes 2 · Documentation 1 · and 9 more". Three, by count rather than by the order the file wrote them, because release-please's section order is fixed and means nothing. Nothing is dropped silently, and a filtered row names what it was filtered by first.

The tone is not in the history. It stays where it distinguishes rather than decorates: the filter menu, and the label a release page heads each group with.

A mark in front of a name is drawn away wherever a name is shown — the release-please prefixes its breaking block with, an emoji a hand-kept changelog opens a section with. Leading only, so C++ Support survives. The stored name stays whole: the filter matches on it, the anchor is slugified from it, and the props carry it, which is why the mark is dropped in the drawing rather than in the parser. See changelogLabel.

A release's groups carry a name and a count only — the entries are not here, because each release has a page of its own and repeating its bullets would put the whole history twice into the search index, llms-full.txt and the feed.

The filter chips are built from the names the file itself used. With nothing selected the page shows everything, which is also what the server renders: a filter is a reader's choice, so the page has to be complete before anyone makes one — otherwise the crawler and the reader whose hydration has not landed are each shown a filtered changelog nobody asked for.

ChangelogGroup

One group of one release — "Features", "Bug Fixes", whatever the file said.

PropTypeNotes
namestringThe file's own heading, verbatim
countnumberHow many entries the group lists

The entries arrive in the slot, as the Markdown they were written as — so the search indexes them, llms-full.txt carries them and the copy control hands a model prose rather than a component call. Only the name and the count, which the badge and the filters need as data, travel as props.

The name is a label, not a headline: set small, upper-case and in the group's own tone, above a list that closes up under it. As a 20-pixel heading it claimed the weight of a chapter for a word naming four bullet points, and six of them read as six sections rather than as one release. It is still an <h2> — the outline, the contents column and the anchor are unchanged, and only its setting says what it is worth.

The colour is a hint and never a meaning: a name the hint list does not know still gets a stable colour of its own, which is what makes the same kind of change scannable down a page of forty releases in any language. The label takes the tone's text pair rather than the dot fill, because a run of words and a six-pixel dot do not clear contrast at the same lightness.

The entries' own markers stay grey. The colour is already stated in the label directly above them; repeated on every line it stops being a signal. They are set from duxt.css, not from this component, because they arrive as Markdown through the slot.

Overriding one

Same as any other component the layer ships: a file at the same path in the consuming site wins.

app/components/content/ChangelogGroup.vue

The props above are the contract, and so is the layout's name. A rename here is a breaking change of the layer.

What a release page shows besides its groups

Nothing in the body. The day a release was cut and the diff it was cut from travel as date and compare in the page's frontmatter, and are drawn where the site already answers where a page came from: the provenance block under the contents column, above "Edit this page".

The release date replaces the last-commit line rather than joining it — every release of a changelog lives in one file, so its last commit dates all of them the same day, which is at best the newest one's.

A release page therefore reads like every other Markdown page on the site, which is the point: a release note is prose, not a dashboard.

No heading of its own

Neither component opens on an <h1>. The docs shell draws the header — breadcrumb, title, the copy control beside it — for every generated page whose body opens on no heading, and a release page wants exactly that: a title and a trail like any written page.

Was this page helpful?