Customization
Everything below is one file. Start at src/config.ts, and keep colours and metrics in
src/styles/theme.css rather than editing the defaults.
Run pnpm dev and open /styleguide while you work: it prints every token, the type
scale, the plate grid with and without the signature, and the four palettes. The page is
a development tool and is never built into dist/.
Site settings — src/config.ts
| Setting | What it does |
|---|---|
site.name, site.description |
Nameplate, footer, feed and meta |
site.url |
Canonical URLs, sitemap, feed. Change it before you publish, or every absolute link points at example.com |
site.locale |
lang on <html> and og:locale |
site.author |
Footer copyright and the JSON-LD publisher |
site.defaultOgImage |
The share image for pages that have no plate of their own |
nav.header, nav.footer |
Link lists. Order is the order shown |
nav.social |
Icon links. icon is a Lucide name, e.g. lucide:instagram |
seo.titleTemplate |
%s is the page title |
seo.twitterHandle |
Omitted from the meta when empty |
seo.jsonLd |
Person or Organization, and the name shown as publisher |
contact.formEndpoint |
Any service that accepts a plain POST: Formspree, Web3Forms, Basin. The form posts straight to it, so there is no script to load |
contact.email, contact.responseTime |
Printed beside the form |
catalogue.platesPerPage |
Plates per page in the catalogue |
catalogue.relatedPlates |
How many entries follow an object page |
catalogue.showAccession |
Prints the accession number in the cartellino. Off hides it everywhere |
theme.preset |
null, 'vellum', 'slate' or 'verdigris' |
features.darkMode |
Off removes the switcher; the site then follows the reader’s system setting |
features.plate |
Off replaces the rule grid with a plain spaced grid, no rules, no cartellino |
features.search |
Off removes /search, the Pagefind index and the finder from the grids |
analytics |
provider ('plausible', 'ga4', 'umami') and id. Nothing is loaded while provider is null. host is for self-hosted Plausible or Umami |
ui |
Every word the theme prints: headings, labels, counts, the finder, the contact form. Translate it along with site.locale; see below |
Words and language
ui at the end of src/config.ts holds every word the theme prints, so a catalogue in another language is one block to translate (and site.locale). Counts are functions, so plurals stay right: objects: (count) => `${count} Objekte` . A few strings carry a link (<a href="/index">index</a>) and are printed as markup: keep the tag when you translate them. The colophon’s paragraphs and production table are the page itself, src/pages/colophon.astro. Decades print as 1780s and dates as written in each object’s dateText.
Colours
The palette is CSS custom properties in src/styles/tokens.css. Do not edit that file;
override what you want in src/styles/theme.css, which is loaded last and unlayered:
:root {
--primary: light-dark(#1f4b6e, #8fc3e8);
--rule: light-dark(#111111, #4a4a4a);
}
Each colour is a light-dark() pair — light value first, dark second. Overriding with a
plain colour sets both modes at once, which is usually not what you want.
| Token | Where it shows |
|---|---|
--background, --surface |
Page and raised blocks |
--foreground, --muted |
Body text; captions, meta, bylines |
--border |
Hairlines that are not the plate rule |
--rule |
The plate rule and every rule that belongs to the grid |
--plate, --plate-ink, --plate-muted, --plate-primary |
Inside the plate cell. These stay paper in dark mode: a photograph shot on white would otherwise sit on a dark page as a white rectangle |
--primary, --primary-hover, --on-primary |
Links, buttons, the inverted button label |
--focus-ring |
Keyboard focus |
After any colour change run pnpm check:contrast. It measures every text token against
every background, in light and dark, across all palettes, and exits non-zero on anything
below WCAG AA (4.5:1).
Colour presets (Pro)
Three complete palettes ship beside the default in src/styles/pro/presets.css. Pick one
in src/config.ts:
export const theme = { preset: 'vellum' };
- vellum — warm paper, walnut ink, burnt umber accent.
- slate — cool grey paper, ink blue accent.
- verdigris — pale green-grey paper, bronze patina accent.
A preset moves the paper, the ink, the rules and the plate together, not just the accent.
Presets and hand overrides compose: theme.css still wins, so you can take slate and
change only --primary.
To add your own, copy a block in presets.css, give it a new [data-preset='…'] name and
add the name to the union type in src/config.ts. pnpm check:contrast picks it up from
the file — it does not have to be registered anywhere else.
The switcher on /styleguide previews all four on that page only.
Type
Webfonts are declared in the fonts: block of astro.config.mjs and served from your own
domain by Astro’s font provider. Replace a face by changing name and the weights; keep
cssVariable as --font-heading or --font-body and the rest of the theme follows.
For system fonts instead, delete the fonts: block and set the stacks in theme.css:
:root {
--font-heading: 'Iowan Old Style', Georgia, serif;
--font-body: Charter, Georgia, serif;
}
Do not add <link> tags to an external font host; it costs a connection on first paint and
puts your readers on someone else’s log.
The plate
The signature is four tokens, all overridable in theme.css:
| Token | Default | Effect |
|---|---|---|
--rule-width |
1px |
Thickness of the grid rule. 2px reads as a heavier, more printed sheet |
--plate-pad |
clamp(1rem, 3vw, 2rem) |
The air between the object and its rule. Lower it and the objects crowd the grid |
--caption-size |
0.78rem |
The caption floor. Going smaller fails the contrast and legibility gates |
--caption-tracking |
0.04em |
Letter-spacing on captions |
--radius |
0 |
Rounded corners. The theme is built on rules; rounding them mixes two shape languages |
Column counts are set per page, not in the grid component: PlateGrid takes a class, and
the pages pass Tailwind column utilities. To change how dense the catalogue is, edit the
class in src/pages/catalogue/[...page].astro.
span: 2 in an object’s frontmatter gives it a double-width cell. It is an editorial
decision, which is why it lives with the object rather than in a layout.
Logo and favicon
There is no logo image: the nameplate is site.name set in the heading face. To use a
mark instead, replace the <a href="/"> block in src/components/common/Header.astro and
the matching line in src/components/common/Footer.astro with an <Image> from
astro:assets.
Favicons are public/favicon.svg and public/favicon.ico.
Pages and layout
| To change | Edit |
|---|---|
| Home | src/pages/index.astro |
| Catalogue grid and pagination | src/pages/catalogue/[...page].astro |
| Object entry | src/pages/catalogue/[id].astro |
| Collections | src/pages/collections/index.astro, src/pages/collections/[id].astro |
| Index and chronology (Pro) | src/pages/index/index.astro, src/pages/index/[term].astro |
| Search (Pro) | src/pages/_search.astro — the underscore keeps it out of the routes; it is injected at /search from astro.config.mjs when features.search is on |
| Colophon, contact, legal, 404 | src/pages/colophon.astro, contact.astro, privacy.mdx, terms.mdx, 404.astro |
| Header, footer | src/components/common/Header.astro, Footer.astro |
<head> meta |
src/components/common/SEO.astro — pages pass title and description to Base |
A new page is a file in src/pages/ using PageLayout:
---
import PageLayout from '../layouts/PageLayout.astro';
---
<PageLayout title="About" description="Who keeps this archive.">
<p>…</p>
</PageLayout>
Add it to nav.header or nav.footer in config.ts to link it.
There is no /about on purpose: the colophon is where an archive says who keeps it,
what it is made of and how to reach it, and two pages saying that is one too many. If
the page is wanted under its own name, the snippet above is the whole of it — save it
as src/pages/about.astro and put { label: 'About', href: '/about' } in
nav.header.
Body copy
Notes, essays and the legal pages use one class, .prose, defined at the end of
src/styles/global.css. There is no typography plugin to configure: change the rules
there, and wrap anything that should keep its own look in .not-prose.
Footnotes (Pro) are ordinary GFM: text[^1] in the body and [^1]: the note at the end.
Their register is styled in src/styles/pro/notes.css.
Legal pages
privacy.mdx and terms.mdx are placeholders written for an archive that collects
nothing. Read them and replace them with your own before you publish.