/**
* Shared frontmatter fragments.
*
* Every collection across the four sites composes its schema from these, so a
* field called `summary` means the same thing everywhere and the feed builder
* can treat all four sites identically.
*
* Zod comes from `astro/zod` (Astro 7 bundles Zod 4; importing `zod` directly
* would give you a second, incompatible copy).
*/
import { z } from 'astro/zod';
/**
* Present on every entry on every site.
*
* `summary` is mandatory on purpose. It is the meta description, the listing
* blurb, and the text that goes out in a feed when the full body cannot — see
* the note in lib/feeds.ts. Writing it is the small tax for the thing working
* everywhere.
*/
export const baseEntry = z.object({
title: z.string().min(1),
summary: z.string().min(1).max(400),
/** Accepts "2026-08-05" or a full ISO timestamp. */
pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
/**
* Drafts build locally but are excluded from listings, feeds and the
* sitemap, and are never deployed. Nothing dated in the future publishes
* either, so you can queue posts by setting pubDate ahead.
*/
draft: z.boolean().default(false),
tags: z.array(z.string()).default([]),
});
/**
* Per-entry design overrides.
*
* Any token from styles/tokens.css can be re-declared for a single article.
* The layout writes these onto the article element as inline custom
* properties, so they cascade to everything inside without a stylesheet edit.
*
* theme:
* accent: "oklch(62% 0.19 25)"
* surface: "oklch(97% 0.02 85)"
* font-prose: "'Fraunces Variable', serif"
* measure: "40rem"
*/
export const themeOverride = z
.record(z.string(), z.string())
.optional()
.describe('CSS custom property overrides, without the leading --');
/**
* How much chrome an entry wants around it.
*
* article — the default reading column, header, nav and footer
* wide — same, but the body starts in the wider track (photo essays)
* bare — a blank page: no header, no footer, no prose styles. For pieces
* where the article *is* the design. You get tokens and nothing
* else.
*/
export const canvas = z.enum(['article', 'wide', 'bare']).default('article');
/** An image with the alt text that makes it publishable. */
export const imageWithAlt = (image: () => z.ZodType) =>
z.object({
src: image(),
alt: z.string().min(1, 'Alt text is required. See docs/06-privacy.md'),
caption: z.string().optional(),
});