Base components are document-level utilities used by the layout shell. SEO, Analytics, and Favicon are rendered inside BaseLayout <head>. ConsentBanner is rendered in <body> after page content. SkipLink is integrated by the site header component. ConsentReset is placed by the site operator via MDX import. All files live in src/core/components/base/ and are CORE-OWNED.
Who this is for
- Implementors creating pages that need SEO or analytics customization
- Developers extending the document shell
- AI agents wiring content collection routes through
BaseLayout
SEO
File: src/core/components/base/SEO.astro
Emits <title>, <meta description>, <link rel="canonical">, Open Graph tags, and Twitter Card metadata. Used inside BaseLayout’s <head>.
Props
| Prop | Type | Default | Notes |
|---|---|---|---|
title | string | required | Page title. Rendered as {title} | {siteTitle} |
description | string | required | Meta description and OG description |
canonical | string | auto from URL + config | Override canonical URL |
noindex | boolean | false | Emits noindex,nofollow robots tag |
ogImage | string | siteConfig.ogImage | OG image URL or path |
type | 'website' | 'article' | 'website' | OG type |
datePublished | Date | — | Reserved for structured data output |
dateModified | Date | — | Reserved for structured data output |
Behavior
- Title format:
{title} | {siteConfig.siteTitle} - Canonical URL: Defaults to
siteConfig.siteUrl + Astro.url.pathname. Override withcanonicalprop for cross-posted content. - OG image resolution chain (PLN-23): Explicit
ogImageprop → generated/og/{path}/(for eligible routes) →siteConfig.ogImage(for non-eligible routes). Non-eligible routes are those withnoindex: true. For generated OG routes, also emitsog:image:type(image/png),og:image:width(1200), andog:image:height(630) meta tags. Relative paths are resolved againstsiteConfig.siteUrl. Absolute URLs pass through unchanged. - noindex: When
true, emits<meta name="robots" content="noindex,nofollow" />. Also changes OG image resolution — noindex pages fall back tositeConfig.ogImageinstead of the generated OG route. Use for utility pages, drafts, and internal docs. - Structured data: The
datePublishedanddateModifiedprops are accepted for structured-data output.
Analytics
File: src/core/components/base/Analytics.astro
Consent-gated dynamic injection of Google Tag Manager, Google Analytics GA4, and custom third-party snippets. Rendered in BaseLayout’s <head>. Also provides window.trackEvent() for consent-aware event dispatch.
Injection Logic
- When
consentRequiredisfalse→ scripts inject immediately on page load. - When
consentRequiredistrue→ scripts inject only after the user accepts consent via ConsentBanner. - GTM takes precedence — when both GTM and GA4 are enabled, only GTM loads.
- Custom snippets inject after GTM/GA4 in config array order.
- In dev mode, no scripts inject regardless of consent state.
For full configuration reference, see Analytics & Consent.
Favicon
File: src/core/components/base/Favicon.astro
Emits four favicon <link> tags from siteConfig.favicons. Rendered in BaseLayout’s <head>.
Output
<link rel="icon" href="/favicon.ico" sizes="any" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="manifest" href="/site.webmanifest" />
Filenames come from siteConfig.favicons (faviconIco, faviconSvg, faviconApple, siteManifest). Place the actual files in public/.
SkipLink
File: src/core/components/base/SkipLink.astro
Accessibility skip-to-content link component. It is intended to render inside SiteHeader (the site navigation bar), not in BaseLayout <body>.
Behavior
- Targets
#main-content(the<main>element id inPageGridLayout). - Visually hidden by default using
transform: translateY(-100%). - Becomes visible on keyboard focus with a smooth transition.
- Uses
--pt-z-skip-link(200) to layer above page content. - Focus state styled with
--st-color-focus-ringand--pt-focus-ring-width.
No props — the component is self-contained.
ConsentBanner
File: src/core/components/base/ConsentBanner.astro
Cookie consent banner that gates analytics script loading. Renders as a fixed bottom bar with three equally-weighted buttons: Accept, Reject, and Required Only. Uses vanilla TypeScript for visibility and consent state management.
Behavior
- Appears on first visit when
consentRequiredistrueand at least one analytics provider or custom snippet is configured. - Stays hidden when
consentRequiredisfalse, when no providers are configured, or when valid (non-expired) consent exists. - Writes consent state to
localStorage(analytics-consentkey) on button click. - Dispatches a
consent-grantedCustomEvent onwindowwhen Accept is clicked, triggering the injection engine. - Sets
data-consent-bannerattribute on<html>for body padding clearance while visible. - Transitions respect
prefers-reduced-motion.
Configuration
Banner copy, button labels, policy link, and expiration period are configured via siteConfig.analytics.consent in site.ts. See Analytics & Consent for the full field reference.
No props — the component reads all configuration from siteConfig.
ConsentReset
File: src/core/components/base/ConsentReset.astro
Consent revocation control for placement on the privacy policy page. Renders a “Reset Cookie Preferences” button that clears consent state, removes analytics cookies, and triggers a page reload.
Usage
Import and place in any MDX page where consent revocation should be available:
import ConsentReset from "@core/components/base/ConsentReset.astro";
## Manage Your Consent
<ConsentReset />
Behavior
- Renders only when
consentRequiredistrue(server-side gate). - Clears
localStorageconsent state, clears cookies matchingcookieClearingPatterns, and reloads the page. - After reload, the consent banner re-appears and no analytics scripts are present.
No props — the component reads siteConfig.analytics for consent state and cookie clearing patterns.
Ownership: CORE-OWNED component, SITE-OWNED placement. The theme ships the component; the site operator places it via MDX import.