5.0 KiB
ADDED Requirements
Requirement: BaseLayout provides complete HTML document shell
src/layouts/BaseLayout.astro SHALL render a complete HTML document (<!DOCTYPE html>, <html>, <head>, <body>) that every page can wrap its content in. It SHALL accept the following props:
title: string— page-specific<title>and OG titledescription: string— page-specific meta description and OG descriptionlocale?: string— BCP 47 language tag, default"en"canonicalUrl: string— absolute canonical URL for this pagealternateUrl: string— absolute URL of the alternate-locale version of this pageogImage?: string— absolute URL forog:image; tag is omitted when falsyjsonLd?: Record<string, unknown>— structured data object to serialize as JSON-LD
Scenario: Page renders with all head meta tags
- WHEN a page wraps its content in
<BaseLayout>with all props provided - THEN the rendered HTML
<head>SHALL contain:<meta charset="utf-8">,<meta name="viewport">,<link rel="icon">for both SVG and ICO favicons,<link rel="canonical">,<link rel="alternate" hreflang>for both locales,<meta property="og:*">tags,<meta name="twitter:*">tags, and<title>
Scenario: OG image tag omitted when ogImage is falsy
- WHEN
ogImageprop is an empty string or not provided - THEN no
<meta property="og:image">tag SHALL appear in the rendered HTML
Scenario: Body has brand defaults
- WHEN any page uses BaseLayout
- THEN the
<body>element SHALL have Tailwind classesbg-midnight text-snow font-archiaapplied
Requirement: BaseLayout preloads critical Archia font weights
The <head> SHALL contain <link rel="preload" as="font" type="font/woff2" crossorigin> for Archia Regular (400), SemiBold (600), and Bold (700) woff2 files. Thin (100) and Light (300) SHALL NOT be preloaded.
Scenario: Correct preload tags rendered
- WHEN the page HTML is rendered
- THEN exactly three font preload links SHALL appear in
<head>: one each forarchia-regular-webfont.woff2,archia-semibold-webfont.woff2, andarchia-bold-webfont.woff2
Requirement: BaseLayout renders hreflang alternates
The <head> SHALL contain <link rel="alternate" hreflang> tags for both en and nl locales, plus an x-default pointing to the EN URL.
Scenario: EN page hreflang output
- WHEN a page passes
locale="en",canonicalUrl="https://qumo.io/about",alternateUrl="https://qumo.io/nl/about" - THEN the head SHALL contain:
<link rel="alternate" hreflang="en" href="https://qumo.io/about"><link rel="alternate" hreflang="nl" href="https://qumo.io/nl/about"><link rel="alternate" hreflang="x-default" href="https://qumo.io/about">
Scenario: NL page hreflang output
- WHEN a page passes
locale="nl",canonicalUrl="https://qumo.io/nl/about",alternateUrl="https://qumo.io/about" - THEN the head SHALL contain the same three hreflang tags with EN/NL values swapped appropriately, and
x-defaultSHALL always point to the EN URL (thealternateUrlon NL pages)
Requirement: BaseLayout supports JSON-LD injection via prop
When the jsonLd prop is provided, BaseLayout SHALL render a <script type="application/ld+json"> tag in <head> containing the JSON-serialized value.
Scenario: JSON-LD rendered from prop
- WHEN
jsonLd={{ "@context": "https://schema.org", "@type": "WebPage", "name": "About" }}is passed - THEN the head SHALL contain
<script type="application/ld+json">{"@context":"https://schema.org","@type":"WebPage","name":"About"}</script>
Scenario: No JSON-LD script when prop omitted
- WHEN
jsonLdprop is not provided - THEN no
<script type="application/ld+json">tag SHALL appear in the rendered head (beyond any injected via the named slot)
Requirement: BaseLayout supports additional JSON-LD via named slot
A <slot name="jsonld" /> SHALL be provided inside <head> for pages that need additional or multiple structured data blocks beyond the jsonLd prop.
Scenario: Named slot accepts additional script
- WHEN a page passes
<script slot="jsonld" type="application/ld+json">{"@type":"BreadcrumbList"}</script> - THEN that script tag SHALL appear in the rendered
<head>
Requirement: BaseLayout includes Nav and Footer stub components
BaseLayout.astro SHALL import src/components/Nav.astro and src/components/Footer.astro and render them at the top and bottom of <body> respectively. Both components are stubs that render nothing in this step.
Scenario: Nav and Footer positions in body
- WHEN any page uses BaseLayout
- THEN the
<body>structure SHALL be: Nav stub output →<slot />(page content) → Footer stub output
Requirement: BaseLayout content slot renders page content
A default <slot /> SHALL be provided between Nav and Footer for page-specific content.
Scenario: Page content appears in slot
- WHEN a page places markup inside
<BaseLayout>...</BaseLayout> - THEN that markup SHALL appear in the rendered body between Nav and Footer