This reference covers the feature toggle surfaces in the site configuration system. For site identity, contact info, branding, and structural config, see Site Configuration (DOC-00002). For styling overrides (typography, fluid tokens, core styles), see Site Styling Configuration (DOC-00089).
Who this is for
- Implementors enabling, disabling, or configuring site-level features for a client site
- Developers understanding how feature toggles flow through the config system
Theme Mode
Nested under theme: ThemeConfig. Controls the theme switcher UI variant ('icon', 'select', or 'none') and first-visit default ('system', 'light', or 'dark').
| Field | Type | Default | Description |
|---|---|---|---|
themeSwitcher | string | "icon" | Switcher UI variant: 'icon', 'select', or 'none' |
defaultTheme | string | "light" | First-visit default: 'system', 'light', or 'dark' |
themeColorLight | string | "#ffffff" | Hex color for <meta name="theme-color"> in light mode |
themeColorDark | string | "#020617" | Hex color for <meta name="theme-color"> in dark mode |
themeColorLight and themeColorDark control the browser chrome color (mobile address bar, PWA title bar). Set these to match your --st-color-bg-canvas values if you override the default surface colors in your site-level token files.
BaseLayout also emits a <meta name="color-scheme"> tag automatically: "light dark" when the switcher is enabled, or the single active scheme when themeSwitcher is 'none'.
Theme preference persistence (localStorage key theme-preference) and FOUC prevention are runtime concerns handled by the theme component, not config fields.
Theme-Docs
Nested under themeDocs: ThemeDocsConfig. Controls whether the theme-docs system is active and its listing page behavior.
| Field | Type | Default | Description |
|---|---|---|---|
enabled | boolean | true | Whether the theme-docs system generates routes |
rootLabel | string | "Documentation" | Breadcrumb label for the theme-docs root |
docsPerPage | number | 24 | Docs per page on the theme-docs index |
When enabled is false, no theme-docs routes, nav links, or build output are generated.
Article Listing
Configured in src/site/config/platform.config.ts and re-exported through siteConfig.articles. Controls pagination for article listing and taxonomy routes.
| Field | Type | Default | Description |
|---|---|---|---|
articlesPerPage | number | 12 | Number of articles per page on listing, category, and tag routes |
This value is the single source of truth for all article pagination — page-1 routes, page-2+ routes, taxonomy pagination, and validation scripts all derive from platformConfig.articles.
// src/site/config/platform.config.ts
export const platformConfig = {
// ...
articles: {
...DEFAULT_SITE_CONFIG.articles,
articlesPerPage: 9,
},
};
Analytics
Nested under analytics: AnalyticsConfig. Configures analytics providers, consent behavior, and custom third-party snippets.
Provider Configuration
| Field | Type | Default | Description |
|---|---|---|---|
googleTagManager.enabled | boolean | false | Enable GTM script injection |
googleTagManager.id | string | "" | GTM container ID (e.g., GTM-XXXXX) |
googleAnalyticsGA4.enabled | boolean | false | Enable GA4 script injection |
googleAnalyticsGA4.id | string | "" | GA4 measurement ID (e.g., G-XXXXX) |
GTM takes precedence — when both are enabled, only GTM loads (it typically manages GA4 as a tag).
Consent Configuration
| Field | Type | Default | Description |
|---|---|---|---|
consentRequired | boolean | false | When true, scripts only load after user accepts consent |
consent.message | string | (default text) | Banner message text |
consent.acceptLabel | string | "Accept" | Accept button label |
consent.rejectLabel | string | "Reject" | Reject button label |
consent.requiredOnlyLabel | string | "Required Only" | Required Only button label |
consent.policyUrl | string | "/privacy/" | Link to privacy policy page in the banner |
consent.policyLinkLabel | string | "Privacy Policy" | Visible text for the policy link |
consent.expirationDays | number | 365 | Days before consent expires and re-prompt appears |
consent.cookieClearingPatterns | string[] | ["_ga", "_gid", "_gat", "_gcl"] | Cookie name prefixes cleared on consent revocation |
The consent.policyUrl and forms.privacyPolicyUrl are independent settings that typically point to the same page. consent.policyUrl drives the banner’s policy link; forms.privacyPolicyUrl drives consent checkbox links on forms.
Custom Snippets
The customSnippets array supports non-Google tracking and marketing scripts, consent-gated with the same rules as GTM/GA4:
customSnippets: [
{ id: "hotjar", src: "https://static.hotjar.com/c/hotjar-XXXXX.js", location: "head" },
{ id: "inline-pixel", content: "console.log('pixel loaded')", location: "body-end" },
],
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for deduplication |
src | string (optional) | External script URL (mutually exclusive with content) |
content | string (optional) | Inline script content (mutually exclusive with src) |
location | "head" | "body-end" | Where the script is injected in the document |
Each entry requires exactly one of src or content. Duplicate id values are logged as warnings; only the first matching entry injects per page load.
For consent states, script injection lifecycle, event taxonomy, and trackEvent() usage, see Analytics & Consent.
Scroll-to-Top
| Field | Type | Default | Notes |
|---|---|---|---|
scrollToTop | boolean | true | Enables the fixed scroll-to-top button on all layouts except Blank. |
scrollToTopPosition | "start" | "end" | "end" | Horizontal position of the button (start = left, end = right). |
Per-page opt-out is available via the hideScrollToTop: true frontmatter field (articles and docs collections). See Interactive Components for the ScrollToTop component reference.
Lightbox
Configured in src/site/config/platform.config.ts and re-exported through siteConfig.lightbox. Controls which content collections enable lightbox by default.
| Field | Type | Default | Notes |
|---|---|---|---|
lightbox.collections | Partial<Record<"articles" | "pages", boolean>> | { articles: true } | Per-collection lightbox defaults. Omitted = false. |
Individual pages can override via the lightbox frontmatter field (true/false). When omitted, the collection default applies. Resolution order: frontmatter → collection default → false.
// src/site/config/platform.config.ts
export const platformConfig = {
// ...
lightbox: {
...DEFAULT_SITE_CONFIG.lightbox,
collections: { articles: true, pages: true },
},
};
See Image Lightbox (DOC-00088) for the full component reference.
Environment Variables
The project uses Astro/Vite environment variables via import.meta.env. No process.env is used.
Build-Mode Flags
import.meta.env.DEV—trueduring development,falsein production builds. Controls: draft content visibility (drafts shown in dev only), analytics script injection (disabled in dev), form submission behavior (console logging in dev).import.meta.env.PROD— Inverse of DEV.
Service Keys
| Variable | Purpose | Required | Used in |
|---|---|---|---|
TURNSTILE_SITE_KEY | Cloudflare Turnstile public key for CAPTCHA | For forms | CaptchaField.astro (client-side) |
TURNSTILE_SECRET_KEY | Cloudflare Turnstile secret for server verification | For forms | turnstile.ts (server-side) |
POSTMARK_API_KEY | Postmark transactional email API key | For forms | newsletter-provider.ts, email-provider.ts |
POSTMARK_FROM_EMAIL | Sender email address | For forms | email-provider.ts |
CONTACT_EMAIL_TO | Recipient email for contact forms | For forms | email-provider.ts |
Fallback Cascade
Environment variable → site config override (e.g., siteConfig.forms.contactEmailTo in src/site/config/site.ts — SITE-OWNED) → hardcoded default.
Dev-Mode Behavior
When service keys are absent, forms degrade gracefully:
- Turnstile verification is bypassed.
- Email submissions log to console instead of sending.
Note: Never commit real service keys to the repository. Use
.envfiles (gitignored) or your hosting platform’s environment variable configuration.