Skip to main content
Layouts are Astro components that provide reusable page structures. Velaria uses a single layout component that wraps all pages with consistent HTML structure, navigation, and scripts.

Layout file

The project has one layout file:
src/layouts/
└── Layout.astro
This layout is used by all pages in the application.

Layout.astro structure

src/layouts/Layout.astro The main layout component that wraps all page content:
---
import Navbar from "../components/Navbar.astro";
import "../styles/global.css";
import Footer from "../components/Footer.astro";

interface Props {
  title: string;
}

const {title} = Astro.props;
---

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width" />
    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
    <meta name="generator" content={Astro.generator} />
    <title>{title}</title>
  </head>
  <body>
    <main>
      <Navbar />
      <slot />
      <Footer />
    </main>

    <script src="https://cdn.jsdelivr.net/npm/@tailwindplus/elements@1" type="module"></script>
    <script src="https://unpkg.com/[email protected]/dist/observer.min.js"></script>

    <script>
      // JavaScript for interactions
    </script>
  </body>
</html>

<style>
  html,
  body {
    margin: 0;
    font-family: "Glaciar Indifference", serif;
  }
</style>

Layout anatomy

1
Frontmatter imports
2
The layout imports shared components and styles:
3
---
import Navbar from "../components/Navbar.astro";
import "../styles/global.css";
import Footer from "../components/Footer.astro";

interface Props {
  title: string;
}

const {title} = Astro.props;
---
4
The Props interface defines that every page must pass a title prop to the layout.
5
HTML head
6
Meta tags and page configuration:
7
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width" />
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
  <meta name="generator" content={Astro.generator} />
  <title>{title}</title>
</head>
8
Head elements:
9
  • UTF-8 character encoding
  • Responsive viewport meta tag
  • SVG favicon reference
  • Astro generator meta tag
  • Dynamic page title from props
  • 10
    Body structure
    11
    The layout wraps page content with navigation and footer:
    12
    <body>
      <main>
        <Navbar />
        <slot />
        <Footer />
      </main>
    </body>
    
    13
    The <slot /> is where page content is inserted. Pages using this layout will have their content rendered between Navbar and Footer.
    14
    External scripts
    15
    Two external libraries loaded via CDN:
    16
    <script src="https://cdn.jsdelivr.net/npm/@tailwindplus/elements@1" type="module"></script>
    <script src="https://unpkg.com/[email protected]/dist/observer.min.js"></script>
    
    17
    Libraries:
    18
  • Tailwind Plus Elements - Pre-built UI components
  • Tailwind Intersect - Scroll-based animations and intersection observer utilities
  • Interactive scripts

    The layout includes JavaScript for three main features:

    1. Parallax effect

    Creates a parallax scrolling effect on the header background:
    let header = document.querySelector('.main-header');
    
    // Parallax effect on background
    window.addEventListener('scroll', () => {
      const offset = window.pageYOffset;
      // Move background at half scroll speed
      header.style.backgroundPositionY = offset * 0.3 + 'px';
    });
    
    The background moves at 30% of the scroll speed (offset * 0.3), creating a depth effect where the background appears to move slower than the foreground content.

    2. Scroll to top button

    Shows/hides the back-to-top button based on scroll position:
    const mybutton = document.getElementById("btn-back-to-top");
    
    const scrollFunction = () => {
      if (
        document.body.scrollTop > 20 ||
        document.documentElement.scrollTop > 20
      ) {
        mybutton.classList.remove("hidden");
      } else {
        mybutton.classList.add("hidden");
      }
    };
    
    const backToTop = () => {
      window.scrollTo({ top: 0, behavior: "smooth" });
    };
    
    mybutton.addEventListener("click", backToTop);
    window.addEventListener("scroll", scrollFunction);
    
    Behavior:
    • Button appears after scrolling 20px down
    • Smooth scroll animation when clicked
    • Hidden by default
    The button element is defined in the ScrollToTop.astro component with id="btn-back-to-top".

    3. Dynamic WhatsApp contact

    Changes WhatsApp number based on time of day:
    const fecha = new Date();
    const hora = fecha.getHours();
    
    if(hora > 12){
      let whatsapp = document.getElementById('num-whatsapp');
      whatsapp?.setAttribute('href', 'https://wa.me/5214777551754')
    }
    
    Logic:
    • Gets current hour
    • If after 12 PM (noon), updates WhatsApp link
    • Allows routing to different support numbers based on time
    This enables different team members to handle inquiries during different shifts.

    Global styles

    The layout includes base styles for the entire site:
    <style>
      html,
      body {
        margin: 0;
        font-family: "Glaciar Indifference", serif;
      }
    </style>
    
    Global CSS file: The layout also imports src/styles/global.css:
    @import "tailwindcss";
    @import "tailwindcss-animated";
    @import "tailwindcss-intersect";
    
    .main-header {
        position: relative;
        height: 100vh;
        display: grid;
        place-items: center;
        overflow: hidden;
        background-image: linear-gradient(rgba(0, 0, 0, 0.263), rgba(0,0,0,0.4)), 
                          url('../assets/images/header2.png');
        background-size: cover;
        background-position-y: 0.3px;
        color: white;
    }
    
    @media(max-width: 720px) {
        .main-header { 
            background-image: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0,0,0,0.6)), 
                              url('../assets/images/bolitas.jpg');
            position: sticky !important;
        }
    }
    
    The .main-header class uses different background images for mobile vs desktop, optimizing for each viewport size.

    Using the layout

    Pages use the layout by importing and wrapping content:
    ---
    import Layout from "../layouts/Layout.astro";
    import Header from "../components/Header.astro";
    ---
    
    <Layout title="VELARIA | Inicio">
      <Header title="Welcome" subtitle="to Velaria" />
      <!-- More page content -->
    </Layout>
    
    Every page renders as:
    <!doctype html>
    <html>
      <head>
        <title>VELARIA | Inicio</title>
        <!-- meta tags, favicon, etc -->
      </head>
      <body>
        <main>
          <Navbar />
          
          <!-- Page content goes here -->
          <Header title="Welcome" subtitle="to Velaria" />
          <!-- More components -->
          
          <Footer />
        </main>
        <!-- scripts -->
      </body>
    </html>
    

    Layout features summary

    Consistent structure

    Every page has the same HTML structure, meta tags, and navigation without repeating code.

    Global JavaScript

    Interactive features (parallax, scroll-to-top, dynamic contacts) work across all pages.

    Shared components

    Navbar and Footer appear on every page automatically.

    External libraries

    Tailwind extensions loaded once and available site-wide.

    Layout vs components

    Layouts:
    • Wrap entire pages
    • Provide document structure
    • Used via import and wrapping syntax
    • Located in src/layouts/
    Components:
    • Reusable UI pieces
    • Used within pages or layouts
    • Self-contained functionality
    • Located in src/components/
    Think of layouts as the “shell” that surrounds your content, while components are the “building blocks” inside that shell.

    DOMContentLoaded event

    All interactive scripts run inside a DOMContentLoaded listener:
    document.addEventListener("DOMContentLoaded", () =>{
      // All interactive code here
      let header = document.querySelector('.main-header');
      const mybutton = document.getElementById("btn-back-to-top");
      // ... etc
    })
    
    This ensures the DOM is fully loaded before JavaScript tries to access elements, preventing errors.

    Next steps

    1. Review components used within layouts
    2. Understand pages that use this layout
    3. Explore the full project structure

    Build docs developers (and LLMs) love