Skip to content

Content

Dimensions

One site across a matrix of versions or locales. An axis prefixes the slug; the outline is built per coordinate; an untranslated page falls back to the default instead of 404ing.

On this page

A version is not a second site. A locale is not a second site. They are coordinates on the same outline. dimensions() wraps N outlines (one per coordinate) into a single Outline that site() consumes, with the coordinate parsed off the front of the slug.

When to use

Reach for dimensions when the same page exists in several editions: v1 / v2 docs, en / de locales, a stable/next channel. If your versions are genuinely different content, they are just different collections; dimensions is for one structure read at several coordinates.

An axis over collections

You declare the axes and a resolve that sources the outline for a coordinate. Versions are just collections; hand resolve whichever one sources each:

import { site, dimensions } from 'ogygia/content';
import { v1, v2 } from './collections.server';

export const docs = site({
	base: '/docs',
	outline: dimensions({
		axes: { version: { values: ['v2', 'v1'], default: 'v2', label: 'Version' } },
		resolve: ({ version }) => (version === 'v1' ? v1 : v2)
	})
});
// /docs/routing        → v2 (the default serves bare)
// /docs/v1/routing     → v1
export const docs = site({
	base: '/docs',
	outline: dimensions({
		axes: {
			version: { values: ['v2', 'v1'], default: 'v2', label: 'Version' },
			locale: { values: ['en', 'de'], default: 'en', label: 'Language', fallback: true }
		},
		resolve: ({ version, locale }) => corpora[version][locale]
	})
});
// /docs/routing        → v2 × en
// /docs/de/routing     → v2 × de
// /docs/v1/de/routing  → v1 × de — the axes compose

The default coordinate serves at the bare URL; the others are prefixed. resolve is called once per distinct coordinate and memoized, so a version’s outline is built lazily on first visit.

Fallback, not 404

A missing translation is the common failure mode for versioned-docs sites. Dimensions handles it: an untranslated page renders the default coordinate’s content at its URL instead of 404ing. The switcher can always link the same slug across coordinates, because the address is guaranteed to resolve.

docs.page() reports what happened on its fallback field, so the page can show a “not yet translated” note:

<script>
	const view = await docs.page(page.params.slug);
	// view.coordinate → { locale: 'de' };  view.fallback → { axis: 'locale', from: 'de', to: 'en' }
	// fallback is null when the page is native to its coordinate
</script>

{#if view.fallback}
	<aside>This page isn't translated to {view.fallback.from} yet — showing the {view.fallback.to} original.</aside>
{/if}

Fallback is per axis, opt-in (fallback: true on the axis): a locale usually wants it, a version usually doesn’t (missing in v1 most often means didn’t exist yet, and that should 404).

The switcher

docs.switcher(slug) returns the version/locale picker for the coordinate in slug, hrefs baked. It is a serializable value (one SwitcherAxis per axis) the <Switcher> component renders as a dropdown. It stays on the same page across a switch, landing on that page’s equivalent in the chosen coordinate (or the default, by the fallback rule).

You rarely call it directly: docs.meta(slug) bundles { nav, switcher, data } in one call, and <DocsShell {meta}> renders the switcher automatically when it’s non-null.

  • nav(slug): pass the slug so the tree reflects that coordinate. The remote threads it through; a bare coordinate prefix like 'v1/' works too.
  • docs.entries(): bakes the union across the matrix, so a page that exists in only one coordinate still prerenders there.
  • search: indexes the default coordinate’s canonical addresses only, so fallback pages under other coordinates don’t show up as duplicate hits.

Coordinate as data

The coordinate is threaded as data (slug → coordinate), never as module-global state, so it is SSR-safe under concurrent requests. You never reach for it directly; nav, page, switcher, and entries all take the slug and derive it.

Do / don’t

  • Do model versions and locales as axes on one outline. One structure, read at coordinates, beats N parallel sites.
  • Do rely on the fallback for locales. A missing translation should degrade to the default, never break the URL.
  • Don’t encode the coordinate in module state. Pass the slug; the derivation is pure and request-safe.
  • Don’t index every coordinate for search. The default’s canonical set is the honest result; fallbacks are the same page.