feat: соц-кнопки и кнопка «Наверх» внизу + русская плюрализация
- Под лентой контента появилась панель из трёх pill-кнопок: Telegram (t.me/anotherreflections), ВКонтакте (vk.com/anotherreflections) и «Наверх» (smooth scroll to top) - Универсальный plural() в consts.ts — больше нет «8 мира», hero показывает «8 миров / 50 публикаций / 20 лет онлайн» с автоматически правильной формой при любом значении - Уточнено описание Ренессанса: «фантастическими допущениями мира Сумрака»
This commit is contained in:
23
src/components/SocialLinks.astro
Normal file
23
src/components/SocialLinks.astro
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
import { SOCIAL } from '../consts';
|
||||||
|
---
|
||||||
|
<div class="social-bar">
|
||||||
|
<a class="social-btn" href={SOCIAL.telegram.url} target="_blank" rel="noopener" aria-label="Telegram-канал «Иные Отражения»" title="Telegram-канал">
|
||||||
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||||
|
<path d="M21.5 4.5 18.5 19.7c-.22 1-.82 1.25-1.66.78l-4.58-3.38-2.21 2.13c-.24.24-.45.45-.92.45l.33-4.65 8.48-7.66c.37-.33-.08-.51-.57-.18L6.93 13.28l-4.52-1.41c-.98-.31-1-.98.21-1.45L20.22 3.1c.82-.3 1.53.19 1.28 1.4Z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
<span>Telegram</span>
|
||||||
|
</a>
|
||||||
|
<a class="social-btn" href={SOCIAL.vk.url} target="_blank" rel="noopener" aria-label="Сообщество ВКонтакте" title="ВКонтакте">
|
||||||
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||||
|
<path d="M12.785 16.241s.288-.032.435-.19c.135-.146.131-.42.131-.42s-.018-1.305.587-1.497c.596-.189 1.361 1.258 2.172 1.815.614.42 1.079.328 1.079.328l2.17-.03s1.135-.07.597-.962c-.044-.073-.314-.66-1.611-1.864-1.359-1.261-1.176-1.057.46-3.236.997-1.327 1.396-2.137 1.271-2.485-.119-.331-.852-.244-.852-.244l-2.44.015s-.181-.024-.315.056c-.13.078-.214.26-.214.26s-.388 1.033-.905 1.911c-1.09 1.85-1.526 1.948-1.705 1.832-.415-.268-.311-1.072-.311-1.643 0-1.783.27-2.526-.527-2.719-.265-.064-.46-.107-1.137-.114-.87-.009-1.605.003-2.022.207-.277.135-.49.437-.36.454.16.022.524.099.717.362.249.34.24 1.103.24 1.103s.143 2.094-.335 2.355c-.328.18-.778-.187-1.75-1.87a15.426 15.426 0 0 1-.871-1.797s-.072-.176-.2-.27c-.155-.115-.371-.151-.371-.151L4.65 7.32s-.348.01-.476.161c-.114.135-.009.413-.009.413s1.815 4.247 3.87 6.388c1.886 1.962 4.028 1.833 4.028 1.833l.722.126Z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
<span>ВКонтакте</span>
|
||||||
|
</a>
|
||||||
|
<button class="social-btn social-btn-top" type="button" aria-label="Наверх" title="Наверх" onclick="window.scrollTo({top: 0, behavior: 'smooth'})">
|
||||||
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||||
|
<path d="M12 5v14M5 12l7-7 7 7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
<span>Наверх</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
@@ -1,3 +1,18 @@
|
|||||||
|
export const SOCIAL = {
|
||||||
|
telegram: { url: 'https://t.me/anotherreflections', label: 'Telegram' },
|
||||||
|
vk: { url: 'https://vk.com/anotherreflections', label: 'ВКонтакте' },
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Русская плюрализация: [1, 2-4, 5+]. Пример: plural(8, ['мир', 'мира', 'миров']) → 'миров' */
|
||||||
|
export function plural(n: number, forms: [string, string, string]): string {
|
||||||
|
const a = Math.abs(n) % 100;
|
||||||
|
const b = a % 10;
|
||||||
|
if (a > 10 && a < 20) return forms[2];
|
||||||
|
if (b > 1 && b < 5) return forms[1];
|
||||||
|
if (b === 1) return forms[0];
|
||||||
|
return forms[2];
|
||||||
|
}
|
||||||
|
|
||||||
export const SITE_TITLE = 'Иные Отражения';
|
export const SITE_TITLE = 'Иные Отражения';
|
||||||
export const SITE_DESCRIPTION = 'Ролевой проект по современной фантастике. Дозоры С. Лукьяненко, Амбер Р. Желязны, А. Пехов.';
|
export const SITE_DESCRIPTION = 'Ролевой проект по современной фантастике. Дозоры С. Лукьяненко, Амбер Р. Желязны, А. Пехов.';
|
||||||
export const SITE_URL = 'https://anotherreflections.ru';
|
export const SITE_URL = 'https://anotherreflections.ru';
|
||||||
@@ -69,7 +84,7 @@ export const WORLDS: World[] = [
|
|||||||
{
|
{
|
||||||
name: 'Ренессанс',
|
name: 'Ренессанс',
|
||||||
tag: 'Историческая фантастика',
|
tag: 'Историческая фантастика',
|
||||||
desc: 'Эпоха возрождения с фантастическими допущениями.',
|
desc: 'Эпоха Возрождения с фантастическими допущениями мира Сумрака.',
|
||||||
url: 'https://renessans.anotherreflections.ru/',
|
url: 'https://renessans.anotherreflections.ru/',
|
||||||
color: 'var(--c-roleplay)',
|
color: 'var(--c-roleplay)',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import '../styles/global.css';
|
import '../styles/global.css';
|
||||||
import { SITE_TITLE, SITE_DESCRIPTION, SITE_URL, SITE_LANG, MAIN_NAV } from '../consts';
|
import { SITE_TITLE, SITE_DESCRIPTION, SITE_URL, SITE_LANG, MAIN_NAV } from '../consts';
|
||||||
import BrandMark from '../components/BrandMark.astro';
|
import BrandMark from '../components/BrandMark.astro';
|
||||||
|
import SocialLinks from '../components/SocialLinks.astro';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title?: string;
|
title?: string;
|
||||||
@@ -60,6 +61,7 @@ const year = new Date().getFullYear();
|
|||||||
|
|
||||||
<main>
|
<main>
|
||||||
<slot />
|
<slot />
|
||||||
|
<SocialLinks />
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer class="site-footer">
|
<footer class="site-footer">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
import { getCollection } from 'astro:content';
|
import { getCollection } from 'astro:content';
|
||||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||||
import { CATEGORY_COLORS } from '../../consts';
|
import { CATEGORY_COLORS, plural } from '../../consts';
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const posts = await getCollection('posts');
|
const posts = await getCollection('posts');
|
||||||
@@ -32,7 +32,7 @@ const fmtDate = (d: Date) =>
|
|||||||
<section class="hero" style={`padding: 3rem 1rem 2rem; --cat-color: ${catColor};`}>
|
<section class="hero" style={`padding: 3rem 1rem 2rem; --cat-color: ${catColor};`}>
|
||||||
<span class="hero-eyebrow" style={`color: ${catColor}; border-color: ${catColor};`}>Категория</span>
|
<span class="hero-eyebrow" style={`color: ${catColor}; border-color: ${catColor};`}>Категория</span>
|
||||||
<h1 style={`background: linear-gradient(180deg, #ffffff 0%, ${catColor} 100%); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;`}>{name}</h1>
|
<h1 style={`background: linear-gradient(180deg, #ffffff 0%, ${catColor} 100%); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;`}>{name}</h1>
|
||||||
<p class="hero-tagline">{sorted.length} {sorted.length === 1 ? 'публикация' : sorted.length < 5 ? 'публикации' : 'публикаций'} · <a href={`/category/${Astro.params.slug}/feed.xml`}>RSS этой категории</a></p>
|
<p class="hero-tagline">{sorted.length} {plural(sorted.length, ['публикация', 'публикации', 'публикаций'])} · <a href={`/category/${Astro.params.slug}/feed.xml`}>RSS этой категории</a></p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
import { getCollection } from 'astro:content';
|
import { getCollection } from 'astro:content';
|
||||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||||
import { WORLDS, CATEGORY_COLORS, HERO_TAGLINE, SITE_FOUNDED } from '../consts';
|
import { WORLDS, CATEGORY_COLORS, HERO_TAGLINE, SITE_FOUNDED, plural } from '../consts';
|
||||||
|
|
||||||
const posts = (await getCollection('posts'))
|
const posts = (await getCollection('posts'))
|
||||||
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
|
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
|
||||||
@@ -19,9 +19,9 @@ const fmtDate = (d: Date) =>
|
|||||||
<h1>Иные<br/>Отражения</h1>
|
<h1>Иные<br/>Отражения</h1>
|
||||||
<p class="hero-tagline">{HERO_TAGLINE}</p>
|
<p class="hero-tagline">{HERO_TAGLINE}</p>
|
||||||
<div class="hero-meta">
|
<div class="hero-meta">
|
||||||
<span><strong>{totalWorlds}</strong> мира</span>
|
<span><strong>{totalWorlds}</strong> {plural(totalWorlds, ['мир', 'мира', 'миров'])}</span>
|
||||||
<span><strong>{totalPosts}</strong> публикаций</span>
|
<span><strong>{totalPosts}</strong> {plural(totalPosts, ['публикация', 'публикации', 'публикаций'])}</span>
|
||||||
<span><strong>{new Date().getFullYear() - SITE_FOUNDED}</strong> лет онлайн</span>
|
<span><strong>{new Date().getFullYear() - SITE_FOUNDED}</strong> {plural(new Date().getFullYear() - SITE_FOUNDED, ['год', 'года', 'лет'])} онлайн</span>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -519,6 +519,40 @@ pre {
|
|||||||
background: var(--bg-elev-2);
|
background: var(--bg-elev-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================== SOCIAL BAR ============================== */
|
||||||
|
.social-bar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: .75rem;
|
||||||
|
margin: 4rem auto 1rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.social-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: .55em;
|
||||||
|
padding: .65em 1.25em;
|
||||||
|
background: var(--bg-elev);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 100px;
|
||||||
|
color: var(--fg-muted);
|
||||||
|
font-size: .88rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: inherit;
|
||||||
|
transition: border-color .2s, color .2s, background .2s, transform .2s;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
.social-btn:hover {
|
||||||
|
border-color: var(--accent);
|
||||||
|
color: var(--accent);
|
||||||
|
background: var(--bg-elev-2);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
border-bottom-color: var(--accent);
|
||||||
|
}
|
||||||
|
.social-btn svg { flex-shrink: 0; }
|
||||||
|
|
||||||
/* ============================== FOOTER ============================== */
|
/* ============================== FOOTER ============================== */
|
||||||
.site-footer {
|
.site-footer {
|
||||||
border-top: 1px solid var(--border);
|
border-top: 1px solid var(--border);
|
||||||
|
|||||||
Reference in New Issue
Block a user