Default Theme
The @indiepub/theme-default package (at themes/default in the monorepo) is the reference implementation of an IndiePub site. It’s a complete, deployable Astro project — not an installable library.
What’s included
Pages
| Route | Description |
|---|---|
/ | Home feed — recent articles and notes |
/posts/[slug] | Canonical entry URL for all post types |
/notes/ | Notes index |
/notes/[slug] | 301 redirect → /posts/[slug] |
/articles/ | Articles index |
/articles/[slug] | 301 redirect → /posts/[slug] |
/photos/ | Photos index |
/photos/[slug] | 301 redirect → /posts/[slug] |
/bookmarks/ | Bookmarks index |
/tags/[tag] | Entries filtered by tag (matches tag name or slug) |
/about | Author h-card page |
/now | ”What I’m doing now” page |
/subscribe | Email subscription form |
/login | Member magic-link login |
Components
| Component | Purpose |
|---|---|
EntryCard | Compact post preview — adapts layout to post type |
EntryDetail | Full post display with webmention counts |
Header | Site navigation with theme toggle |
Sidebar | Author h-card + tag cloud |
HCard | Microformats2 h-card for author identity |
NoteComposer | Inline quick-post form shown to logged-in admins |
SubscribeForm | Email subscription widget |
TagList | Tag cloud with entry counts |
FeedLinks | <link> discovery tags for RSS, Atom, JSON feeds |
ThemeToggle | Light/dark mode switch (persisted in localStorage) |
SocialIcon | Icon renderer for social link profiles |
Layouts
Base.astro— HTML shell,<head>(meta, OG tags, feed links, favicon), theme init scriptPage.astro— Two-column layout with sidebar
Member visibility
Entries have a visibility field:
- public — visible to everyone
- unlisted — accessible by direct URL but not listed in feeds or indexes
- members — requires a logged-in subscriber session; redirects to
/loginotherwise - private — returns 404 to all visitors including logged-in members
Pages pass memberTier (from Astro.locals.member?.tier) to content.getEntries() so the content API filters appropriately. The canonical entry page (/posts/[slug]) enforces visibility itself:
if (entry.visibility === 'private') return 404;if (entry.visibility === 'members' && !Astro.locals.member) redirect('/login');CSS theming
The theme uses a two-level CSS custom property system based on oklch:
/* Palette layer — tweak these in your site's CSS */:root { --brand-hue: 280; /* 0–360 */ --brand-chroma: 0.15; /* 0–0.4 */}
/* Semantic layer — derived from palette, used in components */:root { --color-accent: oklch(55% var(--brand-chroma) var(--brand-hue)); --color-bg: oklch(98% 0.005 var(--brand-hue)); --color-surface-1: oklch(95% 0.008 var(--brand-hue)); /* … */}Override --brand-hue and --brand-chroma in a <style> block or global CSS file to retheme the entire site without touching any component CSS.
Cache headers
Pages use smart Cache-Control headers for Cloudflare’s CDN:
- Public pages:
s-maxage=60, stale-while-revalidate=3600 - Logged-in admin:
no-store(bypass CDN cache so compose button appears) - OG images:
s-maxage=86400, stale-while-revalidate=604800
Getting started
The fastest path is cloning the monorepo and working from themes/default directly:
git clone https://github.com/your-org/indiepubcd indiepub/themes/defaultpnpm installpnpm db:migrate # applies migrations to local D1pnpm devVisit http://localhost:4321/admin/onboarding to finish setup.
Customising the theme
The default theme is meant to be forked and customised. Key files:
astro.config.mjs— integration config, syndication targets, media URLwrangler.toml— D1/R2 bindings and bucket namessrc/styles/— CSS custom properties, typography scalesrc/components/— UI componentssrc/layouts/Base.astro—<head>content, global structuresrc/pages/about.astro— your personal about pagesrc/pages/now.astro— your “now” page
TypeScript notes
src/env.d.tsmust explicitly declareApp.Locals.indiepub—/// <reference types="@indiepub/astro" />does not propagate global augmentations across packages@indiepub/typesbelongs indevDependencies; import types from there, not from@indiepub/coreexactOptionalPropertyTypes: trueis enabled — optional props must useT | undefinednot justT