Files
2026-04-10 18:41:58 +02:00

5.3 KiB

Purpose

Defines the BaseLayout.astro component that provides the complete HTML document shell for every page of the Qumo website, including all <head> meta tags, font preloading, hreflang alternates, JSON-LD support, and the Nav/Footer stub components.

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 title
  • description: string — page-specific meta description and OG description
  • locale?: string — BCP 47 language tag, default "en"
  • canonicalUrl: string — absolute canonical URL for this page
  • alternateUrl: string — absolute URL of the alternate-locale version of this page
  • ogImage?: string — absolute URL for og:image; tag is omitted when falsy
  • jsonLd?: 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 ogImage prop 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 classes bg-midnight text-snow font-archia applied

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 for archia-regular-webfont.woff2, archia-semibold-webfont.woff2, and archia-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-default SHALL always point to the EN URL (the alternateUrl on 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 jsonLd prop 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>

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.

  • 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