Strengthen reusable Astro site foundation
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
---
|
||||
const { eyebrow, heading, copy, href, label } = Astro.props;
|
||||
---
|
||||
<section class="section surface"><div class="shell centered"><p class="eyebrow">{eyebrow}</p><h2>{heading}</h2><p class="lede">{copy}</p><a class="button" href={href}>{label} <span aria-hidden="true">→</span></a></div></section>
|
||||
@@ -3,7 +3,7 @@ import { isBlog, site } from '../lib/site-data.js';
|
||||
---
|
||||
<footer class="site-footer">
|
||||
<div class="shell footer-grid">
|
||||
<div><a class="brand" href="/"><span class="brand-mark" aria-hidden="true">✦</span><span>{isBlog ? 'Happy Tails Journal' : 'Happy Tails'}</span></a><p>{site.description}</p></div>
|
||||
<div><a class="brand" href="/">{site.branding.logo ? <img class="brand-logo" src={site.branding.logo} alt="" /> : <span class="brand-mark" aria-hidden="true">{site.branding.mark}</span>}<span>{site.branding.wordmark}</span></a><p>{site.description}</p></div>
|
||||
<div><p class="eyebrow">Explore</p>{site.nav.map((item) => <a class="footer-link" href={item.href}>{item.label}</a>)}</div>
|
||||
{!isBlog && <address><p class="eyebrow">Say hello</p><a class="footer-link" href={`tel:${site.contact.phoneHref}`}>{site.contact.phone}</a><a class="footer-link" href={`mailto:${site.contact.email}`}>{site.contact.email}</a></address>}
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@ const path = Astro.url.pathname;
|
||||
---
|
||||
<header class="site-header">
|
||||
<div class="shell nav-wrap">
|
||||
<a class="brand" href="/" aria-label={`${site.name} home`}><span class="brand-mark" aria-hidden="true">✦</span><span>{isBlog ? 'Happy Tails Journal' : 'Happy Tails'}</span></a>
|
||||
<a class="brand" href="/" aria-label={`${site.name} home`}>{site.branding.logo ? <img class="brand-logo" src={site.branding.logo} alt="" /> : <span class="brand-mark" aria-hidden="true">{site.branding.mark}</span>}<span>{site.branding.wordmark}</span></a>
|
||||
<button class="menu-button" aria-expanded="false" aria-controls="main-menu">Menu <span aria-hidden="true">+</span></button>
|
||||
<nav id="main-menu" aria-label="Main navigation">
|
||||
{site.nav.map((item) => <a href={item.href} aria-current={path === item.href ? 'page' : undefined}>{item.label}</a>)}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
const { eyebrow, heading, copy, split = false } = Astro.props;
|
||||
---
|
||||
<div class:list={['section-heading', { split }]}><div><p class="eyebrow">{eyebrow}</p><h2>{heading}</h2></div>{copy && <p>{copy}</p>}</div>
|
||||
@@ -2,9 +2,11 @@
|
||||
import '../styles/global.css';
|
||||
import Header from '../components/Header.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
import { deployment, isProduction, site } from '../lib/site-data.js';
|
||||
import { deployment, isBlog, isProduction, site } from '../lib/site-data.js';
|
||||
const { title = site.name, description = site.description } = Astro.props;
|
||||
const canonical = `https://${deployment.hostname}${Astro.url.pathname}`;
|
||||
const socialImage = new URL(site.seo.socialImage, `https://${deployment.hostname}`).href;
|
||||
const structuredData = isBlog ? { '@context': 'https://schema.org', '@type': 'WebSite', name: site.name, url: `https://${deployment.hostname}` } : { '@context': 'https://schema.org', '@type': 'LocalBusiness', name: site.name, url: `https://${deployment.hostname}`, email: site.contact.email, telephone: site.contact.phoneHref, address: { '@type': 'PostalAddress', streetAddress: site.contact.address, addressLocality: site.seo.locality } };
|
||||
---
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
@@ -17,12 +19,17 @@ const canonical = `https://${deployment.hostname}${Astro.url.pathname}`;
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content={canonical} />
|
||||
<meta property="og:image" content={socialImage} />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||
<title>{title}</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=DM+Serif+Display:ital@0;1&display=swap" rel="stylesheet" />
|
||||
{isProduction && site.analyticsId && <script async src={`https://www.googletagmanager.com/gtag/js?id=${site.analyticsId}`}></script>}
|
||||
{isProduction && site.analyticsId && <script define:vars={{ analyticsId: site.analyticsId }}>window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); gtag('config', analyticsId);</script>}
|
||||
<script type="application/ld+json" set:html={JSON.stringify(structuredData)} />
|
||||
</head>
|
||||
<body style={`--accent:${site.theme.accent};--ink:${site.theme.ink};--surface:${site.theme.surface}`}>
|
||||
<Header />
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
---
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import CallToAction from '../components/CallToAction.astro';
|
||||
import PageIntro from '../components/PageIntro.astro';
|
||||
import { isBlog, site } from '../lib/site-data.js';
|
||||
if (isBlog) return Astro.redirect('/');
|
||||
---
|
||||
<BaseLayout title={`Services | ${site.name}`}><PageIntro eyebrow="Services" title="Everything your dog needs for a brighter day." lede="Flexible, personal care—with no one-size-fits-all routines." image="/images/dog-grooming.jpg" imageAlt="A dog receiving gentle grooming care" /><section class="section"><div class="shell service-grid">{site.services.map((service, index) => <article class:list={['service-card', { featured: index === 1 }]}><p class="eyebrow">{service.price}</p><h2>{service.title}</h2><p>{service.description}</p><ul>{service.items.map((item) => <li>{item}</li>)}</ul><a class="text-link" href="/contact">Ask about {service.title.toLowerCase()} →</a></article>)}</div></section><section class="section surface"><div class="shell centered"><p class="eyebrow">A simple start</p><h2>Let's meet your dog first.</h2><p class="lede">Tell us a little about their routine, personality, and what would make their day better.</p><a class="button" href="/contact">Book a meet and greet →</a></div></section></BaseLayout>
|
||||
<BaseLayout title={`Services | ${site.name}`}><PageIntro eyebrow="Services" title="Everything your dog needs for a brighter day." lede="Flexible, personal care—with no one-size-fits-all routines." image="/images/dog-grooming.jpg" imageAlt="A dog receiving gentle grooming care" /><section class="section"><div class="shell service-grid">{site.services.map((service, index) => <article class:list={['service-card', { featured: index === 1 }]}><p class="eyebrow">{service.price}</p><h2>{service.title}</h2><p>{service.description}</p><ul>{service.items.map((item) => <li>{item}</li>)}</ul><a class="text-link" href="/contact">Ask about {service.title.toLowerCase()} →</a></article>)}</div></section><CallToAction eyebrow="A simple start" heading="Let's meet your dog first." copy="Tell us a little about their routine, personality, and what would make their day better." href="/contact" label="Book a meet and greet" /></BaseLayout>
|
||||
|
||||
@@ -13,6 +13,7 @@ h3 { font-size: 1.2rem; margin-bottom: .5rem; }
|
||||
.nav-wrap { min-height: 82px; display: flex; align-items: center; justify-content: space-between; gap: 2rem; }
|
||||
.brand { display: inline-flex; align-items: center; gap: .5rem; text-decoration: none; font-size: 1.15rem; font-weight: 700; letter-spacing: -.03em; }
|
||||
.brand-mark { display: grid; place-items: center; width: 28px; height: 28px; color: #fff; background: var(--accent); border-radius: 50%; font-size: .85rem; }
|
||||
.brand-logo { width: 32px; height: 32px; object-fit: contain; }
|
||||
nav { display: flex; align-items: center; gap: 1.5rem; }
|
||||
nav a { color: #52616b; text-decoration: none; font-size: .92rem; font-weight: 600; }
|
||||
nav a[aria-current='page'], nav a:hover { color: var(--ink); }
|
||||
|
||||
Reference in New Issue
Block a user