Add astro layouts

This commit is contained in:
2026-03-07 11:08:32 +03:00
parent 08a160a6a1
commit 50d032ce62
8 changed files with 206 additions and 12 deletions

View File

@@ -0,0 +1,34 @@
---
interface Article {
slug: string;
date: Date;
title: string;
}
interface Props {
articles: Article[];
}
const { articles } = Astro.props;
const sorted = [...articles].sort((a, b) => b.date.getTime() - a.date.getTime());
const byYear = new Map<number, Article[]>();
for (const article of sorted) {
const year = article.date.getFullYear();
if (!byYear.has(year)) {
byYear.set(year, []);
}
byYear.get(year)!.push(article);
}
---
{[...byYear.entries()].map(([year, items]) => (
<div>
<h2 class="font-bold">{year}</h2>
{items.map((article) => (
<h3>
<a href={`/articles/${article.slug}/`}>{article.title}</a>
</h3>
))}
</div>
))}

View File

@@ -0,0 +1,9 @@
---
---
<nav class="my-4">
<ul class="flex gap-4 list-none p-0 m-0">
<li><a href="/">Главная</a></li>
<li><a href="/articles/">Блог</a></li>
<li><a href="/gallery/">Галерея</a></li>
</ul>
</nav>

View File

@@ -0,0 +1,29 @@
---
---
<script is:inline>
(function (d, w, c) {
(w[c] = w[c] || []).push(function() {
try {
w.yaCounter41913764 = new Ya.Metrika({
id: 41913764,
clickmap: true,
trackLinks: true,
accurateTrackBounce: true,
trackHash: true
});
} catch(e) { }
});
var n = d.getElementsByTagName("script")[0],
s = d.createElement("script"),
f = function () { n.parentNode.insertBefore(s, n); };
s.type = "text/javascript";
s.async = true;
s.src = "https://cdn.jsdelivr.net/npm/yandex-metrica-watch/watch.js";
if (w.opera == "[object Opera]") {
d.addEventListener("DOMContentLoaded", f, false);
} else { f(); }
})(document, window, "yandex_metrika_callbacks");
</script>
<noscript><div><img src="https://mc.yandex.ru/watch/41913764" style="position:absolute; left:-9999px;" alt="" /></div></noscript>

View File

@@ -0,0 +1,25 @@
---
import InternalLayout from './InternalLayout.astro';
interface Props {
title: string;
description?: string;
keywords?: string[];
date: Date;
}
const { title, description, keywords, date } = Astro.props;
const formattedDate = date.toLocaleDateString('ru-RU', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
});
---
<InternalLayout title={title} description={description} keywords={keywords}>
<h1 class="text-4xl font-bold mt-3 mb-4">{title}</h1>
<slot />
<div class="border-t border-rule mt-8 pt-4">
<p>{formattedDate},&nbsp;<a href="mailto:anton@vakhrushev.me">anton@vakhrushev.me</a></p>
</div>
</InternalLayout>

View File

@@ -0,0 +1,39 @@
---
import '../styles/global.css';
import YandexMetrika from '../components/YandexMetrika.astro';
interface Props {
title?: string;
description?: string;
keywords?: string[];
}
const siteTitle = 'Антон Вахрушев';
const siteUrl = Astro.site?.origin ?? 'https://vakhrushev.me';
const { title, description, keywords } = Astro.props;
const pageTitle = title ? `${title} - ${siteTitle}` : siteTitle;
const pageDescription = description ?? title ?? '';
const pageUrl = `${siteUrl}${Astro.url.pathname}`;
---
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{pageTitle}</title>
{pageDescription && <meta name="description" content={pageDescription} />}
{keywords && keywords.length > 0 && <meta name="keywords" content={keywords.join(',')} />}
<meta name="yandex-verification" content="eb6443fccb57d7d2" />
<meta property="og:site_name" content={siteTitle} />
<meta property="og:title" content={title ?? siteTitle} />
<meta property="og:description" content={pageDescription} />
<meta property="og:url" content={pageUrl} />
<meta property="og:locale" content="ru_RU" />
<link href="https://fonts.googleapis.com/css2?family=PT+Serif:wght@400;700&family=Source+Code+Pro:wght@400&display=swap&subset=cyrillic" rel="stylesheet" />
</head>
<body>
<slot />
<YandexMetrika />
</body>
</html>

View File

@@ -0,0 +1,20 @@
---
import BaseLayout from './BaseLayout.astro';
import Navigation from '../components/Navigation.astro';
interface Props {
title?: string;
description?: string;
keywords?: string[];
}
const { title, description, keywords } = Astro.props;
---
<BaseLayout title={title} description={description} keywords={keywords}>
<div class="max-w-content mx-auto px-3 sm:px-0">
<Navigation />
<main class="mb-12">
<slot />
</main>
</div>
</BaseLayout>

View File

@@ -1,13 +1,25 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<html lang="ru">
<head>
<meta charset="utf-8" />
<title>Антон Вахрушев</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<h1>Антон Вахрушев</h1>
<p>Сайт в процессе обновления.</p>
</body>
</html>
<BaseLayout description="Личный сайт Антона Вахрушева">
<main class="max-w-content mx-auto px-3 sm:px-0">
<h1 class="text-4xl font-bold mt-3 mb-4">Антон Вахрушев</h1>
<p>
Я веб-программист.
Работаю в <a href="https://playkot.com/" target="_blank">Playkot</a>.
Пишу на PHP.
Разбираюсь в back-end, экспериментирую с front-end, интересуюсь функциональным программированием.
</p>
<div class="border-t border-rule my-8"></div>
<ul class="list-none p-0 m-0">
<li>
<a href="mailto:anton@vakhrushev.me">anton@vakhrushev.me</a>
</li>
<li>
<a href="https://git.vakhrushev.me" target="_blank">Код на Гитхабе</a>
</li>
</ul>
</main>
</BaseLayout>

View File

@@ -8,5 +8,31 @@
--color-link: #0366d6;
--color-rule: #e6e6e6;
--breakpoint-content: 740px;
--width-content: 740px;
}
@layer base {
html {
font-size: 20px;
@media (max-width: theme(--width-content)) {
font-size: 18px;
}
}
body {
@apply font-serif text-text;
}
h1, h2, h3, h4, h5, h6 {
font-weight: normal;
}
a {
@apply text-link no-underline;
}
a:hover, a:focus, a:active {
@apply underline;
}
}