Skip to content
Download ZIP

Blue accent palette · 8.2 KB ZIP File receipt View as Markdown View code

Preview

Fit to the available width. The frame follows the height of its content; previews taller than the maximum auto-height scroll inside it.

Give this component to your coding agent Copy a prompt that fetches this exact version and palette through the PageSugar MCP server.
cmp_results_statistics_01 · version 1.0.0 · Blue accent palette
Using the PageSugar MCP server, fetch component cmp_results_statistics_01 version 1.0.0 with variant "blue", first inspect its requirements and license status and confirm this project uses Svelte 5 and Tailwind CSS 4. Retrieve every manifest file, including binary assets and any manifest-only response files, preserving relative paths. Then integrate the source and follow its usage notes. Run project checks, review the browser result and report anything unverified. Do not substitute another version or invent missing files.

Not connected yet? Set up the MCP server

Code

Palette
Blue accent
Version
1.0.0
Digest
Full digest
sha256-dc2cdbc252cb718358b67ad392287ea6621323571c4d790de78f9f6278104eba
ResultsStatistics.svelte Svelte · 13.7 KB Raw
<script module lang="ts">
	export interface ResultStat {
		/** The figure exactly as it should read, already formatted: "12,400", "99.95%", "1.2 million". */
		value: string;
		/** A unit set smaller after the figure, e.g. "ms", "days", "hours". Keep "%" in the value. */
		unit?: string;
		/** What the figure measures, e.g. "Teams planning in Halcyon". Read before the value by screen readers. */
		label: string;
		/** When and how it was measured, e.g. "Paid workspaces, 30 June 2026". */
		context?: string;
		/** The `id` of the note that explains this figure. Unknown ids render no marker. */
		noteId?: string;
	}

	export interface ResultNote {
		/** Referenced from `ResultStat.noteId`. Notes are numbered in the order given. */
		id: string;
		/** The methodology or source, in a sentence or two. */
		text: string;
	}
</script>

<script lang="ts">
	import type { Snippet } from 'svelte';

	interface Props {
		/** Figures the business can substantiate, in reading order. An empty list renders nothing. */
		stats: ResultStat[];
		/** Methodology and source notes, listed under the figures and linked from their markers. */
		notes?: ResultNote[];
		/** Short label above the title. */
		eyebrow?: string;
		/** Section heading; the section is labelled by it. */
		title?: string;
		/** Accessible name for the section when there is no visible title. */
		label?: string;
		/** One or two sentences under the title, held to a readable measure. */
		description?: string;
		/** Extra content under the description, such as a link to the full report. */
		intro?: Snippet;
		/** `row` runs the figures across, `grid` sets them two by two, `split` puts the heading beside them. */
		layout?: 'row' | 'grid' | 'split';
		/** `plain` has no background, `panel` sets the section on a raised surface, `inverted` is a dark band. */
		tone?: 'plain' | 'panel' | 'inverted';
		/** Level of the section heading. */
		headingLevel?: 2 | 3;
		/** Accessible name of a footnote marker, followed by its number: "Note 1". */
		noteLabel?: string;
		/** Accessible name of a back link, followed by the figure's label: "Back to Uptime in 2025". */
		backLabel?: string;
		/** Accessible name of the list of notes. */
		notesLabel?: string;
	}

	let {
		stats,
		notes = [],
		eyebrow,
		title,
		label,
		description,
		intro,
		layout = 'row',
		tone = 'plain',
		headingLevel = 2,
		noteLabel = 'Note',
		backLabel = 'Back to',
		notesLabel = 'Notes'
	}: Props = $props();

	const uid = $props.id();
	const heading = $derived(`h${headingLevel}`);
	const hasHeader = $derived(Boolean(eyebrow || title || description || intro));
	/*
	 * Split needs something to put beside the figures; without header content it lays out as a
	 * row. One figure under a heading would strand a short column across the band, so it takes
	 * the split rail whatever the layout.
	 */
	const shape = $derived(
		!hasHeader ? (layout === 'split' ? 'row' : layout) : stats.length === 1 ? 'split' : layout
	);
	const split = $derived(shape === 'split');

	/*
	 * Note numbers follow the notes array. The first note with a given non-empty id owns it: later
	 * duplicates still render but take no markers, so every marker and back link has one partner.
	 */
	const numberOf = $derived(
		new Map(
			notes.flatMap((note, index) =>
				note.id && notes.findIndex((other) => other.id === note.id) === index
					? [[note.id, index + 1] as const]
					: []
			)
		)
	);
	const citedBy = $derived(
		notes.map((note, index) =>
			note.id && numberOf.get(note.id) === index + 1
				? stats.flatMap((stat, statIndex) => (stat.noteId === note.id ? [statIndex] : []))
				: []
		)
	);
	/*
	 * Row: four figures run across from lg and pair up below it; three run across from lg; longer
	 * lists wrap in threes unless they divide into fours. Grid and split always pair. One figure
	 * takes the full width. The notes use the same columns, so each note starts on a figure's edge.
	 */
	const count = $derived(stats.length);
	/*
	 * Figure size follows the width each figure gets: one alone is set largest, three or fewer a
	 * row (or two a row in the grid) step up on wide screens, and four across stay at 48 px.
	 * Container queries are avoided here because containment would stop each figure's rows joining
	 * the shared subgrid.
	 *
	 * Figures are proportional on purpose. Set tabular at 48 to 72 px, Inter's "1" sits in a full
	 * digit cell and opens a visible gap ("1 1 min", "1 2,400"); these figures sit side by side,
	 * never in a column, so there is nothing for tabular widths to line up.
	 */
	const size = $derived(
		count === 1
			? 'text-6xl sm:text-7xl'
			: shape === 'grid' || (shape === 'row' && count <= 3)
				? 'text-5xl lg:text-6xl'
				: 'text-5xl'
	);
	const columns = $derived(
		count === 1
			? ''
			: shape !== 'row' || count === 2
				? 'sm:grid-cols-2'
				: count === 3
					? 'sm:grid-cols-2 lg:grid-cols-3'
					: count % 4 === 0
						? 'sm:grid-cols-2 lg:grid-cols-4'
						: 'sm:grid-cols-2 lg:grid-cols-3'
	);
</script>

{#if count > 0}
	<section
		class={[
			'results-statistics px-4 py-16 sm:px-6 sm:py-24 lg:px-8 lg:py-32',
			tone === 'inverted' && 'bg-(--_surface)'
		]}
		data-tone={tone}
		aria-labelledby={title ? `${uid}-title` : undefined}
		aria-label={title ? undefined : label}
	>
		<div class="mx-auto max-w-6xl">
			<div
				class={[
					tone === 'panel' && 'rounded-2xl bg-(--_surface) p-6 ring-1 ring-(--_hairline) sm:p-12',
					split && 'lg:grid lg:grid-cols-12 lg:gap-x-6'
				]}
			>
				{#if hasHeader}
					<div class={['max-w-2xl text-start', split && 'lg:col-span-4']}>
						{#if eyebrow}
							<p
								class="results-statistics__eyebrow text-xs leading-none font-medium tracking-[0.06em] text-balance break-words text-(--_muted) uppercase"
							>
								{eyebrow}
							</p>
						{/if}
						{#if title}
							<svelte:element
								this={heading}
								id="{uid}-title"
								class={[
									'text-3xl leading-[1.15] font-semibold tracking-tight text-balance break-words text-(--_ink) sm:text-4xl rtl:tracking-normal',
									eyebrow && 'mt-2'
								]}
							>
								{title}
							</svelte:element>
						{/if}
						{#if description}
							<p
								class={[
									'max-w-xl text-base leading-6 text-pretty break-words text-(--_muted) sm:text-lg sm:leading-7',
									(eyebrow || title) && 'mt-4'
								]}
							>
								{description}
							</p>
						{/if}
						{#if intro}
							<div class={(eyebrow || title || description) && 'mt-6'}>
								{@render intro()}
							</div>
						{/if}
					</div>
				{/if}

				<div class={[hasHeader && 'mt-12', split && 'lg:col-span-7 lg:col-start-6 lg:mt-0']}>
					<!--
						Each figure is a label (dt) followed by its value and context (dd), so a screen reader
						hears what is measured before the number. The value is moved above the label visually
						only, and the three rows share a subgrid so labels line up across the band.
					-->
					<dl class={['grid gap-x-6 gap-y-12', columns]}>
						{#each stats as stat, index (index)}
							{@const number = numberOf.get(stat.noteId ?? '')}
							<div
								class="row-span-3 grid min-w-0 grid-cols-1 grid-rows-subgrid gap-y-0 border-t border-(--_hairline) pt-6"
							>
								<dt
									class="mt-4 max-w-md text-base leading-6 font-medium text-pretty break-words text-(--_ink)"
								>
									{stat.label}
								</dt>
								<dd
									class={[
										'order-first leading-[1.05] font-semibold tracking-[-0.03em] break-words text-(--_accent) rtl:tracking-normal',
										size
									]}
								>
									<!-- Isolated so a Latin figure and its unit keep their order inside right-to-left text. -->
									<bdi dir="auto"
										>{stat.value}{stat.unit ? ' ' : ''}{#if stat.unit}<span
												class="results-statistics__unit text-[0.5em] font-medium tracking-[-0.01em] whitespace-nowrap"
												>{stat.unit}</span
											>{/if}</bdi
									>{#if number}&#8288;<sup
											class="results-statistics__marker relative top-[0.3rem] ms-2 align-top text-xs leading-none tracking-normal"
											><a
												class="relative inline-flex h-5 min-w-5 scroll-my-24 items-center justify-center rounded-full px-1 font-medium text-(--_accent) tabular-nums no-underline shadow-(--_marker-ring) transition-[color,background-color,box-shadow] duration-150 ease-(--_ease) target:bg-(--_accent) target:text-(--_on-accent) target:shadow-(--_marker-ring-filled) hover:bg-(--_accent) hover:text-(--_on-accent) hover:shadow-(--_marker-ring-filled) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--_accent)"
												id="{uid}-ref-{index}"
												href="#{uid}-note-{number}"
												aria-label="{noteLabel} {number}">{number}</a
											></sup
										>{/if}
								</dd>
								{#if stat.context}
									<dd class="max-w-md text-sm leading-5 text-pretty break-words text-(--_muted)">
										{stat.context}
									</dd>
								{/if}
							</div>
						{/each}
					</dl>

					{#if notes.length > 0}
						<ol
							class={['results-statistics__notes mt-12 grid gap-x-6 gap-y-2', !split && columns]}
							role="list"
							aria-label={notesLabel}
						>
							{#each notes as note, index (index)}
								<li
									id="{uid}-note-{index + 1}"
									class="results-statistics__note flex max-w-[30em] min-w-0 scroll-my-24 gap-2 rounded-sm text-[0.8125rem] leading-5 text-(--_muted) transition-shadow duration-150 ease-(--_ease) target:bg-(--_note-tint) target:shadow-(--_note-halo)"
								>
									<span
										class="w-4 shrink-0 font-medium text-(--_ink) tabular-nums"
										aria-hidden="true">{index + 1}</span
									>
									<span class="min-w-0 text-pretty break-words">
										{note.text}&#8288;<span class="whitespace-nowrap"
											>{#each citedBy[index] as statIndex (statIndex)}<a
													class="results-statistics__back relative ms-1 inline-flex h-5 min-w-5 items-center justify-center gap-1 rounded-full px-0.5 align-[-0.3125rem] font-medium text-(--_muted) tabular-nums no-underline transition-[color,background-color,box-shadow] duration-150 ease-(--_ease) hover:bg-(--_back-fill) hover:text-(--_ink) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--_accent)"
													href="#{uid}-ref-{statIndex}"
													aria-label="{backLabel} {stats[statIndex].label}"
													><svg
														class="size-4 shrink-0 rtl:-scale-x-100"
														viewBox="0 0 16 16"
														fill="none"
														aria-hidden="true"
														><path
															d="M6 3.5 2.5 7 6 10.5M3 7h6.5a4 4 0 0 1 4 4v1.5"
															stroke="currentColor"
															stroke-width="1.75"
															stroke-linecap="round"
															stroke-linejoin="round"
														/></svg
													>{#if citedBy[index].length > 1}<span aria-hidden="true"
															>{stats[statIndex].value}{stats[statIndex].unit
																? ` ${stats[statIndex].unit}`
																: ''}</span
														>{/if}</a
												>{/each}</span
										>
									</span>
								</li>
							{/each}
						</ol>
					{/if}
				</div>
			</div>
		</div>
	</section>
{/if}

<style>
	/* Public tokens: set --results-statistics-* on this section or any ancestor to retone it. */
	.results-statistics {
		--_accent: var(--results-statistics-accent, #1d4ed8);
		--_on-accent: var(--results-statistics-on-accent, #ffffff);
		--_ink: var(--results-statistics-ink, #18181b);
		--_muted: var(--results-statistics-muted, #52525b);
		--_hairline: var(--results-statistics-hairline, rgb(0 0 0 / 0.1));
		--_surface: var(--results-statistics-surface, #fafafa);

		/*
		 * Formulas, read by utilities in the markup. They resolve here, so the inverted band's
		 * tokens tint them too.
		 */
		--_ease: cubic-bezier(0.2, 0, 0, 1);
		--_marker-ring: inset 0 0 0 1px color-mix(in oklab, var(--_ink) 24%, transparent);
		--_marker-ring-filled: inset 0 0 0 1px var(--_accent);
		--_back-fill: color-mix(in oklab, var(--_ink) 8%, transparent);
		--_note-tint: color-mix(in oklab, var(--_accent) 8%, transparent);
		--_note-halo: 0 0 0 0.25rem var(--_note-tint);
	}

	/*
	 * The inverted band reads its own variables, so a page retoned for the light section does not
	 * leak into the dark one, and a palette can pick an accent that clears contrast on each.
	 */
	.results-statistics[data-tone='inverted'] {
		--_accent: var(--results-statistics-inverted-accent, #93c5fd);
		--_on-accent: var(--results-statistics-inverted-on-accent, #18181b);
		--_ink: var(--results-statistics-inverted-ink, #fafafa);
		--_muted: var(--results-statistics-inverted-muted, #a1a1aa);
		--_hairline: var(--results-statistics-inverted-hairline, rgb(255 255 255 / 0.12));
		--_surface: var(--results-statistics-inverted-surface, #09090b);
	}

	/*
	 * Footnote markers and back links show a 20 px disc; the ::after extends each hit area to
	 * 44 px without moving anything.
	 */
	.results-statistics__marker a::after,
	.results-statistics__back::after {
		content: '';
		position: absolute;
		inset: -0.75rem;
	}

	/*
	 * A note cited by several figures names each destination by its figure; neighbouring back
	 * links sit 24 px apart so their 44 px hit areas never overlap.
	 */
	.results-statistics__back + .results-statistics__back {
		margin-inline-start: 1.5rem;
	}

	/* Japanese and Chinese have no spaces to break at: wrap at phrase boundaries instead. */
	.results-statistics:is(:lang(ja), :lang(zh)) {
		word-break: auto-phrase;
		line-break: strict;
	}

	/*
	 * Arabic and CJK scripts set small at 12 px and have no capitals to carry an eyebrow; use 14.
	 * Arabic and Hebrew are never letter-spaced, so the tracking resets under right-to-left.
	 */
	.results-statistics__eyebrow:is(:dir(rtl), :lang(ja), :lang(zh), :lang(ko)) {
		font-size: 0.875rem;
		line-height: 1.25rem;
		text-transform: none;
	}
	.results-statistics__eyebrow:dir(rtl) {
		letter-spacing: 0;
	}
</style>

Presentational only: every value is displayed exactly as supplied, already formatted. It does not compute, round, verify or animate figures, and it does not know whether a claim is current. Only publish figures you can substantiate, give each one a timeframe, and keep the notes accurate. No runtime packages beyond Svelte and Tailwind CSS, and no backend.

Suggested location
src/lib/components/results-statistics-01
Required props
stats

Limitations

  • Values are display strings; the component does not format, localise, round or animate them.
  • Notes are numbered in the order of the notes array, not the order figures cite them. A noteId with no matching note renders no marker; if two notes share an id, the first one takes the markers.
  • With an empty stats array the component renders nothing, heading included.
  • The split layout places the heading beside the figures from the lg breakpoint; below it the heading sits above them. A single figure with a heading always uses split, so it never strands across the band. Without any header content (eyebrow, title, description or intro), split lays out as a row.
  • Light appearance by default; the inverted tone is a dark band inside a light page. No dark mode is declared or selected automatically.

Example

Svelte
<!-- Illustrative figures: replace every value and note with ones you can substantiate. -->
<script lang="ts">
	import ResultsStatistics, {
		type ResultNote,
		type ResultStat
	} from '$lib/components/results-statistics-01/ResultsStatistics.svelte';

	const stats: ResultStat[] = [
		{ value: '12,400', label: 'Teams planning with us', context: 'Paid workspaces on 30 June 2026', noteId: 'teams' },
		{ value: '99.95%', label: 'Uptime in 2025', context: 'Checked every minute', noteId: 'uptime' },
		{ value: '4 h', label: 'Median support reply', context: 'Calendar year 2025' }
	];

	const notes: ResultNote[] = [
		{ id: 'teams', text: 'Workspaces on a paid plan at the end of 30 June 2026.' },
		{ id: 'uptime', text: 'Availability from 1 January to 31 December 2025, planned maintenance included.' }
	];
</script>

<ResultsStatistics eyebrow="In numbers" title="Three numbers, and how we counted them" {stats} {notes} />

Results statistics#

A band of headline figures. Each figure is paired with a label that says what it measures and a context line that says when, and can point at a numbered note that says how it was counted.

Writing figures that hold up#

  • Pass value exactly as it should read, with its unit: "12,400", "99.95%", "14 ms". The component never formats, rounds or animates it.
  • Give every figure a context line with a date or period. An undated figure goes stale without anyone noticing.
  • Use a note for anything a reader could reasonably ask about: what was counted, what was left out, the period, and who checked it. A note qualifies a figure; it cannot rescue a misleading one.
  • One note can explain several figures. Each figure that cites it gets its own back link.

Layouts and tones#

Prop Value Result
layout row Up to four figures across from lg; they pair up below it
grid Two by two
split Heading on the left, figures and notes on the right from lg
tone plain No background; sits on the page
panel Content on a raised surface with a hairline edge
inverted A full-width dark band

intro is a snippet rendered under the description, for a link to the full report:

Svelte
<ResultsStatistics layout="split" title="What your donations did last year" {stats} {notes}>
	{#snippet intro()}
		<a href="/annual-report-2025">Read the 2025 annual report</a>
	{/snippet}
</ResultsStatistics>

Retoning for a tinted page#

CSS
.results-statistics {
	--results-statistics-surface: #f5f0e6;
	--results-statistics-ink: #292524;
	--results-statistics-muted: #57534e;
	--results-statistics-hairline: rgb(41 37 36 / 0.12);
	--results-statistics-accent: #9a3412;
}

The inverted band reads --results-statistics-inverted-* instead, so a light retone never leaks into it. Set those separately if you use tone="inverted".

Other languages#

Translate noteLabel, backLabel and notesLabel with the page. Under dir="rtl" the layout mirrors, tracking resets and the back-link glyph flips.

Props and content inputs#

On this page
NameTypeRequiredDefaultDescription
statsResultStat[]YesNoneFigures in reading order. Each has value (pre-formatted string), optional unit (set at half size after the value), label, optional context (what and when) and optional noteId pointing at a note.
notesResultNote[]No[]Methodology or source notes, each { id, text }. Numbered in order, listed under the figures, and linked from each figure that cites them and back.
eyebrowstringNoNoneShort uppercase label above the title.
titlestringNoNoneSection heading; the section is labelled by it.
labelstringNoNoneAccessible name for the section when there is no visible title.
descriptionstringNoNoneOne or two sentences under the title, held to a readable measure.
introSnippetNoNoneExtra content under the description, such as a link to the full report. Most useful in the split layout.
layout'row' | 'grid' | 'split'No'row'row runs up to four figures across; grid sets them two by two; split puts the heading on the left and the figures on the right from lg. A single figure with a heading always uses split.
tone'plain' | 'panel' | 'inverted'No'plain'plain has no background; panel sets the content on a raised surface; inverted is a full-width dark band.
headingLevel2 | 3No2Level of the section heading.
noteLabelstringNo'Note'Accessible name of a footnote marker, followed by its number ("Note 1"). Translate with the page.
backLabelstringNo'Back to'Accessible name of a note's back link, followed by the figure's label. Translate with the page.
notesLabelstringNo'Notes'Accessible name of the list of notes. Translate with the page.

Customization#

On this page

Change content through props, retone the plain and panel tones through six --results-statistics-* variables and the inverted band through six --results-statistics-inverted-* variables, and edit Tailwind classes in the source for spacing or columns.

  • Figures: pass value as the exact string you want read ("12,400", "99.95%", "£6.20") and put a word unit in unit ("ms", "days", "hours"), which is set at half size so the number leads. Give every figure a context line that says when it was measured.
  • Notes: give each note an id and set noteId on the figures it explains. One note can serve several figures; it gets one back link per figure.
  • Accent: --results-statistics-accent colours the figures, the note markers and the focus ring; --results-statistics-on-accent is the marker's number on its hovered fill. The neutral accent matches the ink, so the default is monochrome.
  • Text: --results-statistics-ink sets the title, labels and note numbers; --results-statistics-muted sets the description, context lines and notes. Keep muted at 4.5:1 against the background.
  • Surfaces: --results-statistics-hairline draws the rule above each figure and the panel's edge; --results-statistics-surface fills the panel tone.
  • Inverted band: the --results-statistics-inverted-* variables (accent, on-accent, ink, muted, hairline, surface) retone the dark tone on their own, so a light retone never leaks into it. Pick an inverted accent that clears 4.5:1 on the dark surface; the blue palette uses #93c5fd.
  • Tinted page retone (cream): surface #f5f0e6, ink #292524, muted #57534e, hairline rgb(41 37 36 / 0.12), accent #9a3412 (all --results-statistics-*). usage.md has the snippet.
  • Columns: the columns value in the script decides the grid. Four figures run across from lg and pair up below it, three run across from lg, and two, or a grid or split layout, pair up. Change those classes to alter the count.
  • Headings: set headingLevel so the section fits the page outline. Translate noteLabel, backLabel and notesLabel with the rest of the page.

Public CSS variables

VariableToken
--results-statistics-accentaccent
--results-statistics-on-accentonAccent
--results-statistics-inkink
--results-statistics-mutedmuted
--results-statistics-hairlinehairline
--results-statistics-surfacesurface
--results-statistics-inverted-accentinvertedAccent
--results-statistics-inverted-on-accentinvertedOnAccent
--results-statistics-inverted-inkinvertedInk
--results-statistics-inverted-mutedinvertedMuted
--results-statistics-inverted-hairlineinvertedHairline
--results-statistics-inverted-surfaceinvertedSurface

Accessibility#

On this page
  • Figures are a definition list: each label is a dt and its value and context are dd elements after it, so a screen reader announces what is measured before the number. The value is moved above the label with CSS order only.
  • Values are static text. There is no count-up animation, so the number on screen and the number read aloud are always the final one.
  • Each footnote marker is a link named "Note 1", "Note 2" and so on (noteLabel), pointing at its note. Each note ends with one back link per figure that cites it, named "Back to" plus that figure's label (backLabel).
  • Notes are an ordered list named by notesLabel. A followed note is marked with a faint halo, and both targets keep a scroll margin so a sticky header does not cover them.
  • Markers and back links show a two-pixel focus outline in the accent, offset by two pixels, on :focus-visible only. Their visible discs are 20 px; an invisible extension gives each marker a 44 px hit area, and each back link a 44 px tall, 24 px wide one so neighbouring back links never overlap.
  • Neutral figures (#18181b) measure 17.7:1 on white; blue figures (#1d4ed8) 6.7:1. On the inverted band (#09090b) figures (#fafafa) measure 19:1, muted text (#a1a1aa) 7.8:1 and the blue palette's #93c5fd about 11:1. Muted text (#52525b) measures 7.7:1 on white and 7.4:1 on the panel surface.
  • The layout uses logical properties, so it mirrors under dir="rtl"; tracking resets for right-to-left scripts and the back-link glyph flips.
  • Element IDs come from $props.id(), so two sections on one page never share marker or note IDs.

Known limitations

  • Contrast ratios are computed for the shipped palettes only; re-check any changed token.
  • The consumer is responsible for the truth of every figure and note; the component cannot tell a stale or unsupported claim from a current one.

Release details#

On this page
Integration
  • Presentational
  • Works without client-side JavaScript
  • Server-side rendering supported
Dependencies
No additional runtime packages beyond Svelte and Tailwind CSS
License

MIT. Default license approval is pending; see the license status before adopting the source.

Version history
  • 1.0.0 (Published) Current release · 30 September 2026

Only the current release is available. Keep downloaded source and its receipt if you need to use it again later.