Skip to main content

Overview

Layouts in Astro are components that wrap page content and provide consistent structure across the site. Chapinismos uses a single base layout that handles HTML structure, meta tags, and global elements.

Base Layout

The Base.astro layout is the foundation for all pages.

File Location

src/layouts/Base.astro

Full Layout Structure

---
import "../styles/global.css";
import Header from "../components/Header.astro";
import Footer from "../components/Footer.astro";
import { ClientRouter } from "astro:transitions";
import OrganizationSchema from "../components/schemas/OrganizationSchema.astro";
import WebsiteSchema from "../components/schemas/WebsiteSchema.astro";

const {
  title = "Chapinismos",
  description = "Glosario de chapinismos guatemaltecos",
  keywords = "chapinismos, diccionario guatemalteco",
  ogImage = "/og-image.svg",
  canonicalUrl = Astro.url.pathname,
  type = "website",
  lang = "es",
  alternateUrls = {},
} = Astro.props;

const siteUrl = Astro.site || "https://chapinismos.org";
const fullCanonicalUrl = new URL(canonicalUrl, siteUrl).toString();
const fullOgImage = new URL(ogImage, siteUrl).toString();

const ogLocale = lang === "en" ? "en_US" : "es_GT";
const alternateOgLocale = lang === "en" ? "es_GT" : "en_US";
---

<!doctype html>
<html lang={lang} data-theme="dark">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    <!-- Primary Meta Tags -->
    <title>{title}</title>
    <meta name="title" content={title} />
    <meta name="description" content={description} />
    <meta name="keywords" content={keywords} />
    <link rel="canonical" href={fullCanonicalUrl} />

    <!-- Hreflang tags for multilingual SEO -->
    {alternateUrls.es && (
      <link rel="alternate" hreflang="es" href={new URL(alternateUrls.es, siteUrl).toString()} />
    )}
    {alternateUrls.en && (
      <link rel="alternate" hreflang="en" href={new URL(alternateUrls.en, siteUrl).toString()} />
    )}

    <!-- Open Graph / Facebook -->
    <meta property="og:type" content={type} />
    <meta property="og:url" content={fullCanonicalUrl} />
    <meta property="og:title" content={title} />
    <meta property="og:description" content={description} />
    <meta property="og:image" content={fullOgImage} />
    <meta property="og:locale" content={ogLocale} />

    <!-- Twitter -->
    <meta property="twitter:card" content="summary_large_image" />
    <meta property="twitter:title" content={title} />
    <meta property="twitter:description" content={description} />
    <meta property="twitter:image" content={fullOgImage} />

    <!-- Favicon -->
    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
    <link rel="manifest" href="/site.webmanifest" />

    <!-- JSON-LD Structured Data -->
    <OrganizationSchema lang={lang} siteUrl={siteUrl} />
    <WebsiteSchema lang={lang} siteUrl={siteUrl} />
    <slot name="schema" />

    <!-- View Transitions -->
    <ClientRouter fallback="none" />
  </head>
  <body>
    <Header />
    <main id="main-content" role="main">
      <slot />
    </main>
    <Footer />
  </body>
</html>

Layout Props

The Base layout accepts the following props:
PropTypeDefaultDescription
titlestring”Chapinismos”Page title (shown in browser tab)
descriptionstringDefault descriptionMeta description for SEO
keywordsstringDefault keywordsMeta keywords for SEO
ogImagestring”/og-image.svg”Open Graph image path
canonicalUrlstringCurrent pathCanonical URL for SEO
typestring”website”Open Graph type (website/article)
langstring”es”Page language (es/en)
alternateUrlsobjectHreflang URLs for multilingual pages

Using the Layout

Basic Usage

---
import Base from "../../layouts/Base.astro";
import { useTranslations } from "../../utils/i18n";

const { lang } = Astro.params;
const t = useTranslations(lang);
---

<Base 
  title={t("home.title")} 
  description={t("home.description")}
  lang={lang}
>
  <h1>Welcome to Chapinismos</h1>
  <p>Content goes here...</p>
</Base>

With Alternate URLs (Multilingual)

---
import Base from "../../../layouts/Base.astro";

const { lang, slug } = Astro.params;

const alternateUrls = {
  es: `/es/palabras/${slug}/`,
  en: `/en/palabras/${slug}/`,
  xDefault: `/es/palabras/${slug}/`,
};
---

<Base
  title="Word Title"
  description="Word description"
  type="article"
  lang={lang}
  alternateUrls={alternateUrls}
>
  <!-- Page content -->
</Base>

With Custom Schema

Use the schema slot to inject page-specific structured data:
---
import Base from "../layouts/Base.astro";
import BreadcrumbSchema from "../components/schemas/BreadcrumbSchema.astro";
import WordSchema from "../components/schemas/WordSchema.astro";

const { lang, slug } = Astro.params;
const siteUrl = "https://chapinismos.org";
---

<Base title="Page Title" lang={lang}>
  <BreadcrumbSchema 
    slot="schema" 
    lang={lang} 
    siteUrl={siteUrl}
    items={[
      { name: "Index", url: `/${lang}/indice/` },
      { name: "Word", url: `/${lang}/palabras/${slug}/` },
    ]}
  />
  <WordSchema 
    slot="schema" 
    lang={lang} 
    siteUrl={siteUrl}
    word={wordData}
    slug={slug}
  />
  
  <!-- Page content -->
</Base>

Layout Features

Head Management

The layout automatically generates:
  • Page title and description
  • Canonical URL
  • Keywords
  • Robots directives
  • Author information

Favicon and Icons

The layout includes multiple favicon formats:
<link rel="icon" href="/favicon-96x96.png" sizes="96x96" />
<link rel="icon" href="/favicon-32x32.png" sizes="32x32" />
<link rel="icon" href="/favicon-16x16.png" sizes="16x16" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />

Structured Data (JSON-LD)

Every page includes base structured data:
{
  "@context": "https://schema.org",
  "@type": "Organization",
  "name": "Diccionario Chapín",
  "url": "https://chapinismos.org",
  "logo": "https://chapinismos.org/favicon.svg"
}
Pages can add more schemas via the schema slot.

View Transitions

The layout includes Astro’s View Transitions for smooth page navigation:
<ClientRouter fallback="none" />
Components can use transition:persist to maintain state across page transitions.

Global Styles

The layout imports global CSS:
import "../styles/global.css";
This includes:
  • CSS custom properties (theme variables)
  • Reset styles
  • Utility classes
  • Dark/light theme definitions

HTML Structure

<!doctype html>
<html lang="{lang}" data-theme="dark">
  <head>
    <!-- Meta tags, links, scripts -->
  </head>
  <body>
    <Header />              <!-- Global header -->
    <main role="main">
      <slot />              <!-- Page content -->
    </main>
    <Footer />              <!-- Global footer -->
  </body>
</html>

Theme Attribute

The data-theme attribute controls dark/light mode:
<html data-theme="dark">  <!-- Default -->
<html data-theme="light"> <!-- User preference -->
This is toggled by the ThemeToggle component.

Performance Optimizations

<link rel="preconnect" href="https://cloud.umami.is" />
<link rel="dns-prefetch" href="https://cloud.umami.is" />
<link rel="preload" href="/images/bg.webp" as="image" fetchpriority="low" />
<script async src="https://cloud.umami.is/script.js"></script>
Configured in astro.config.mjs:
build: {
  inlineStylesheets: "always",
}

Creating Custom Layouts

You can create specialized layouts for different page types:
1

Create layout file

touch src/layouts/ArticleLayout.astro
2

Extend Base layout

---
import Base from "./Base.astro";

interface Props {
  title: string;
  author: string;
  date: Date;
  lang: string;
}

const { title, author, date, lang } = Astro.props;
---

<Base title={title} type="article" lang={lang}>
  <article>
    <header>
      <h1>{title}</h1>
      <p>By {author} on {date.toLocaleDateString()}</p>
    </header>
    <slot />
  </article>
</Base>
3

Use the new layout

---
import ArticleLayout from "../layouts/ArticleLayout.astro";
---

<ArticleLayout 
  title="Article Title" 
  author="John Doe"
  date={new Date()}
  lang="es"
>
  <p>Article content...</p>
</ArticleLayout>

Best Practices

<Base lang={lang}>
This ensures correct <html lang> attribute and locale-specific schemas.
<Base title={`${word.word}${t("word.page.title")}`}>
Include context in page titles for better SEO and user experience.
const alternateUrls = {
  es: `/es/page/`,
  en: `/en/page/`,
  xDefault: `/es/page/`,
};
<Base type="article" lang={lang}>
This provides better Open Graph metadata for word definitions.
<BreadcrumbSchema slot="schema" ... />
<WordSchema slot="schema" ... />

Next Steps

Styling

Learn about global styles and theming

SEO

Deep dive into SEO features

Components

Explore reusable components

Internationalization

Work with translations

Build docs developers (and LLMs) love