Files
anotherreflections-website-v2/src/layouts/BaseLayout.astro
Dmitry Gusev f78145e70a
All checks were successful
deploy / deploy (push) Successful in 1m1s
feat: Speculation Rules API in BaseLayout (prerender on hover/pointerdown)
2026-05-23 23:56:37 +03:00

146 lines
4.9 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
import '../styles/global.css';
import { SITE_TITLE, SITE_DESCRIPTION, SITE_URL, SITE_LANG, SITE_FOUNDED, MAIN_NAV, SOCIAL } from '../consts';
import BrandMark from '../components/BrandMark.astro';
import SocialLinks from '../components/SocialLinks.astro';
import Analytics from '../components/Analytics.astro';
import CookieConsent from '../components/CookieConsent.astro';
interface Props {
title?: string;
description?: string;
ogType?: 'website' | 'article';
}
const { title, description, ogType = 'website' } = Astro.props;
const pageTitle = title ? `${title} — ${SITE_TITLE}` : SITE_TITLE;
const pageDesc = description || SITE_DESCRIPTION;
const canonical = new URL(Astro.url.pathname, SITE_URL).toString();
const year = new Date().getFullYear();
const ogImage = new URL('/og-image.png', SITE_URL).toString();
const logoUrl = new URL('/logo.svg', SITE_URL).toString();
const jsonLd = [
{
'@context': 'https://schema.org',
'@type': 'WebSite',
name: SITE_TITLE,
url: SITE_URL,
inLanguage: SITE_LANG,
description: SITE_DESCRIPTION,
potentialAction: {
'@type': 'SearchAction',
target: `${SITE_URL}/?s={query}`,
'query-input': 'required name=query',
},
},
{
'@context': 'https://schema.org',
'@type': 'NewsMediaOrganization',
name: SITE_TITLE,
url: SITE_URL,
logo: logoUrl,
image: ogImage,
description: SITE_DESCRIPTION,
foundingDate: String(SITE_FOUNDED),
sameAs: Object.values(SOCIAL).map((s) => s.url),
},
];
---
<!doctype html>
<html lang={SITE_LANG}>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{pageTitle}</title>
<meta name="description" content={pageDesc} />
<meta name="theme-color" content="#07090f" />
<link rel="canonical" href={canonical} />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta property="og:type" content={ogType} />
<meta property="og:title" content={pageTitle} />
<meta property="og:description" content={pageDesc} />
<meta property="og:url" content={canonical} />
<meta property="og:site_name" content={SITE_TITLE} />
<meta property="og:locale" content="ru_RU" />
<meta property="og:image" content={new URL('/og-image.png', SITE_URL).toString()} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content={`${SITE_TITLE} — ${SITE_DESCRIPTION}`} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={pageTitle} />
<meta name="twitter:description" content={pageDesc} />
<meta name="twitter:image" content={new URL('/og-image.png', SITE_URL).toString()} />
<link rel="alternate" type="application/rss+xml" title={`${SITE_TITLE} — RSS`} href="/feed.xml" />
<script type="application/ld+json" is:inline set:html={JSON.stringify(jsonLd)} />
<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=Inter:wght@400;500;600&family=Cormorant+Garamond:wght@500;600;700&display=swap" rel="stylesheet" />
<Analytics />
<!--
Speculation Rules API (Chromium 122+) — prerender same-origin pages on
hover/pointerdown for near-instant navigation. Rules are static JSON,
no user input → safe inline.
-->
<script type="speculationrules" is:inline set:html={JSON.stringify({
prerender: [
{
where: {
and: [
{ href_matches: '/*' },
{ not: { href_matches: '/assets/*' } },
{ not: { href_matches: '/sitemap*' } },
{ not: { href_matches: '/feed*' } },
{ not: { href_matches: '/llms*' } }
]
},
eagerness: 'moderate'
}
]
})} />
</head>
<body>
<header class="site-header">
<div class="site-header-inner">
<div class="site-brand">
<a href="/" style="display:inline-flex;align-items:center;gap:.7rem;border-bottom:none;">
<BrandMark size={32} />
<h1 class="site-title"><span>{SITE_TITLE}</span></h1>
</a>
</div>
<nav>
<ul class="site-nav">
{MAIN_NAV.map((item) => (
<li><a href={item.href}>{item.label}</a></li>
))}
</ul>
</nav>
</div>
</header>
<main>
<slot />
<SocialLinks />
</main>
<footer class="site-footer">
<span class="footer-ornament"></span>
<p>© 2006{year} · {SITE_TITLE}</p>
<p>
<a href="/feed.xml">RSS</a> ·
<a href="/sitemap-index.xml">Sitemap</a> ·
<a href="/privacy/">Политика конфиденциальности</a> ·
<a href="/kontakty/">Контакты</a>
</p>
</footer>
<CookieConsent />
</body>
</html>