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 shell.
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.
| Prop | Type | Notes |
|---|---|---|
releases | array | Each with version, date, to, its groups and contributors |
The history opens on four figures — releases, changes, contributors, 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 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 — filter the history to breaking changes and the contributor figure answers who has ever shipped 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.
Where the contributors come from
Not from the changelog. A release is a range of commits — everything from the release before it up to itself — and who wrote them is in the repository's git history, keyed by the tag the release was cut as. The authors of the changelog file are whoever ran the release tool, which is a different set of people and the wrong answer given confidently, so it is never used.
Both spellings of a version are tried, so release-please's include-v-in-tag
needs no configuration: a changelog heading of 1.2.0 finds the tag v1.2.0. A
merge commit sets a release's boundary — release-please tags the merge its
release pull request produced — but credits nobody, because it introduces no
change of its own. Accounts GitHub itself marks as apps are left out, so the bot
that cuts the releases is not listed as having written them.
Three things leave a release with no contributors, and each is shown as an absent figure rather than a nought, because a nought is a claim about the project where an absent figure is a claim about this build:
- the artefact came from a downloaded source of another repository, which
Content clones at
--depth 1— a history in which every commit looks as though one person wrote it is wrong data rather than missing data. A downloaded source of the repository being built is the exception: a site that publishes its own tagged documentation reads the history from its checkout, which carries the same tags. Any remote of the checkout counts, and the build needs the full history and the tags —fetch-depth: 0inactions/checkout; - the version was never tagged in this checkout, which is the ordinary state of a release between the moment its changelog entry is written and the moment its tag exists;
- there is no git to ask.
What travels onto the page is a name and, where git carries one, a GitHub
handle — never an email address. The address is what identifies a committer and
is what the build dedupes by, and it stops there: an address inside a commit
object is between a repository and whoever clones it, where the same address on a
prerendered, crawled and indexed page is something the person never agreed to.
The handle is what contributors.avatarUrl turns
into a picture; a contributor without one is drawn as an initial.
Below them every release is one line in columns — version, badge, what it
carried, who carried it, 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.
Beside that summary the row shows up to four faces and counts the rest, in the order the build put them — most of the release first. Faces rather than names, because four names are a second line of prose competing with the summary where four overlapping circles are read as "a few people" without being read at all; the name is still there as each picture's alternative text, so a screen reader hears the people and the row's link says who wrote the release it leads to. They share the summary's column rather than taking a track of their own: a fifth column would hold an empty gutter on every release whose contributors could not be read.
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.
| Prop | Type | Notes |
|---|---|---|
name | string | The file's own heading, verbatim |
count | number | How 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.