Новые правки в компоенты
This commit is contained in:
parent
107b7f461f
commit
a14c18542e
15 changed files with 417 additions and 233 deletions
23
AGENTS.md
23
AGENTS.md
|
|
@ -114,6 +114,29 @@
|
||||||
- Использовать Lighthouse и WebPageTest для аудита производительности
|
- Использовать Lighthouse и WebPageTest для аудита производительности
|
||||||
- Реализовать performance budgets
|
- Реализовать performance budgets
|
||||||
|
|
||||||
|
## SEO рекомендации
|
||||||
|
|
||||||
|
### Оптимальное количество страниц
|
||||||
|
- **50-100 страниц** — хороший объём для тематики "автоюрист"
|
||||||
|
- Минимум 30-50 страниц с уникальным контентом
|
||||||
|
|
||||||
|
### Структура страниц
|
||||||
|
- Услуги (12 шт) — уже есть
|
||||||
|
- Кейсы — минимум 20-30 реальных примеров
|
||||||
|
- Блог — минимум 30-50 статей
|
||||||
|
- FAQ — 20-30 вопросов
|
||||||
|
- Городские страницы ("юрист в Сургуте", "возврат прав Сургут" и пр.)
|
||||||
|
|
||||||
|
### Требования к контенту
|
||||||
|
- Каждая страница с уникальным текстом (не шаблон)
|
||||||
|
- Длинные статьи в блог (3000-5000 слов)
|
||||||
|
- Реальные кейсы с описанием проблемы и решения
|
||||||
|
|
||||||
|
### Внутренние ссылки
|
||||||
|
- Связывать страницы между собой (услуги → кейсы → блог)
|
||||||
|
- Использовать sitemap для всех публичных страниц
|
||||||
|
|
||||||
|
|
||||||
## Ccылки на документацию
|
## Ccылки на документацию
|
||||||
- URL документации Astro: https://docs.astro.build/en/getting-started/
|
- URL документации Astro: https://docs.astro.build/en/getting-started/
|
||||||
- URL документации PocketBase: https://pocketbase.io/docs/
|
- URL документации PocketBase: https://pocketbase.io/docs/
|
||||||
|
|
|
||||||
|
|
@ -84,14 +84,29 @@ const { items } = Astro.props;
|
||||||
padding: 0 0 0.5rem;
|
padding: 0 0 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.breadcrumbs-list {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.breadcrumb-link,
|
.breadcrumb-link,
|
||||||
.breadcrumb-current {
|
.breadcrumb-current {
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 90px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumb-separator {
|
.breadcrumb-separator {
|
||||||
width: 0.875rem;
|
width: 0.875rem;
|
||||||
height: 0.875rem;
|
height: 0.875rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb-item {
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
---
|
---
|
||||||
import telegramIcon from '../../icons/telegram.svg?raw';
|
import { Icon } from 'astro-icon/components';
|
||||||
import vkIcon from '../../icons/vk.svg?raw';
|
|
||||||
import whatsappIcon from '../../icons/whatsapp.svg?raw';
|
|
||||||
import okIcon from '../../icons/ok.svg?raw';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
|
|
@ -27,7 +24,7 @@ const encodedUrl = encodeURIComponent(url);
|
||||||
class="social-icon telegram"
|
class="social-icon telegram"
|
||||||
aria-label="Поделиться в Telegram"
|
aria-label="Поделиться в Telegram"
|
||||||
>
|
>
|
||||||
<span class="icon-wrapper" set:html={telegramIcon} />
|
<span class="icon-wrapper"><Icon name="telegram" /></span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<!-- VK -->
|
<!-- VK -->
|
||||||
|
|
@ -38,7 +35,7 @@ const encodedUrl = encodeURIComponent(url);
|
||||||
class="social-icon vk"
|
class="social-icon vk"
|
||||||
aria-label="Поделиться в VK"
|
aria-label="Поделиться в VK"
|
||||||
>
|
>
|
||||||
<span class="icon-wrapper" set:html={vkIcon} />
|
<span class="icon-wrapper"><Icon name="vk" /></span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<!-- WhatsApp -->
|
<!-- WhatsApp -->
|
||||||
|
|
@ -49,7 +46,7 @@ const encodedUrl = encodeURIComponent(url);
|
||||||
class="social-icon whatsapp"
|
class="social-icon whatsapp"
|
||||||
aria-label="Поделиться в WhatsApp"
|
aria-label="Поделиться в WhatsApp"
|
||||||
>
|
>
|
||||||
<span class="icon-wrapper" set:html={whatsappIcon} />
|
<span class="icon-wrapper"><Icon name="whatsapp" /></span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<!-- Одноклассники -->
|
<!-- Одноклассники -->
|
||||||
|
|
@ -60,7 +57,7 @@ const encodedUrl = encodeURIComponent(url);
|
||||||
class="social-icon odnoklassniki"
|
class="social-icon odnoklassniki"
|
||||||
aria-label="Поделиться в Одноклассниках"
|
aria-label="Поделиться в Одноклассниках"
|
||||||
>
|
>
|
||||||
<span class="icon-wrapper" set:html={okIcon} />
|
<span class="icon-wrapper"><Icon name="ok" /></span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
---
|
---
|
||||||
import BlogCard from '@components/blog/BlogCard.astro';
|
|
||||||
import { getPostImageUrl } from '@lib/pb';
|
import { getPostImageUrl } from '@lib/pb';
|
||||||
|
|
||||||
interface Post {
|
interface Post {
|
||||||
|
|
@ -22,7 +21,6 @@ interface Props {
|
||||||
|
|
||||||
const { posts, currentSlug } = Astro.props;
|
const { posts, currentSlug } = Astro.props;
|
||||||
|
|
||||||
// Форматируем дату
|
|
||||||
const formatDate = (date: string) => {
|
const formatDate = (date: string) => {
|
||||||
return new Date(date).toLocaleDateString('ru-RU', {
|
return new Date(date).toLocaleDateString('ru-RU', {
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
|
|
@ -31,46 +29,51 @@ const formatDate = (date: string) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Фильтруем текущую статью
|
const filteredPosts = currentSlug
|
||||||
const filteredPosts = currentSlug
|
? posts.filter((post: Post) => post.slug !== currentSlug)
|
||||||
? posts.filter(post => post.slug !== currentSlug).slice(0, 3)
|
: posts;
|
||||||
: posts.slice(0, 3);
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<section class="related-posts">
|
<section class="related-posts">
|
||||||
<h3 class="section-title">Читайте также</h3>
|
<h3 class="section-title">Похожие статьи</h3>
|
||||||
|
|
||||||
<div class="related-grid">
|
<div class="related-grid">
|
||||||
{filteredPosts.map((post) => (
|
{filteredPosts.slice(0, 3).map((post: Post) => (
|
||||||
<BlogCard
|
<Fragment>
|
||||||
title={post.title}
|
<article class="blog-card-wrapper">
|
||||||
description={post.description}
|
<a href={`/blog/${post.slug}`} class="card-link">
|
||||||
category={post.category}
|
<div class="card-image">
|
||||||
categoryColor={post.categoryColor}
|
<img src={getPostImageUrl(post)} alt={post.title} loading="lazy" width="400" height="250" />
|
||||||
date={formatDate(post.date)}
|
<span class={`category-badge ${post.categoryColor || 'bg-gold'}`}>{post.category}</span>
|
||||||
readTime={post.readTime}
|
</div>
|
||||||
readmeTime={post.readmeTime}
|
<div class="card-content">
|
||||||
image={getPostImageUrl(post)}
|
<h3 class="card-title">{post.title}</h3>
|
||||||
slug={`/blog/${post.slug}`}
|
<p class="card-description">{post.description}</p>
|
||||||
/>
|
<div class="card-meta">
|
||||||
|
<span class="meta-item">{formatDate(post.date)}</span>
|
||||||
|
<span class="meta-item">{post.readTime} мин</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</article>
|
||||||
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style is:global>
|
<style>
|
||||||
.related-posts {
|
.related-posts {
|
||||||
margin-top: 3rem;
|
margin-top: 3rem;
|
||||||
padding-top: 2rem;
|
padding-top: 2rem;
|
||||||
border-top: 2px solid #f1f5f9;
|
border-top: 2px solid #f1f5f9;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Сбрасываем нумерацию заголовков от ArticleLayout */
|
.related-posts .blog-card-wrapper .card-title {
|
||||||
.related-posts .blog-card .card-title {
|
|
||||||
counter-increment: none !important;
|
counter-increment: none !important;
|
||||||
display: block !important;
|
display: block !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.related-posts .blog-card .card-title::before {
|
.related-posts .blog-card-wrapper .card-title::before {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,6 +83,11 @@ const filteredPosts = currentSlug
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
margin: 0 0 2rem;
|
margin: 0 0 2rem;
|
||||||
letter-spacing: -0.02em;
|
letter-spacing: -0.02em;
|
||||||
|
font-family: 'Merriweather', Georgia, serif;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.related-grid {
|
.related-grid {
|
||||||
|
|
@ -88,6 +96,80 @@ const filteredPosts = currentSlug
|
||||||
gap: 2rem;
|
gap: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.blog-card-wrapper {
|
||||||
|
border-radius: 1rem;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 4px 20px rgba(0,0,0,0.05);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-card-wrapper:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: 0 8px 30px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-card-wrapper .card-link {
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-card-wrapper .card-image {
|
||||||
|
position: relative;
|
||||||
|
aspect-ratio: 16/10;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-card-wrapper .card-image img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-card-wrapper:hover .card-image img {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-card-wrapper .category-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: 1rem;
|
||||||
|
left: 1rem;
|
||||||
|
padding: 0.25rem 0.75rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-card-wrapper .card-content {
|
||||||
|
padding: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-card-wrapper .card-title {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
color: #1e293b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-card-wrapper .card-description {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: #64748b;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-card-wrapper .card-meta {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: #94a3b8;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 1024px) {
|
@media (max-width: 1024px) {
|
||||||
.related-grid {
|
.related-grid {
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
|
@ -99,5 +181,15 @@ const filteredPosts = currentSlug
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: 1.5rem;
|
gap: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.related-posts {
|
||||||
|
margin-top: 2rem;
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ import { createSignal, onMount } from "solid-js";
|
||||||
export default function CommentLock() {
|
export default function CommentLock() {
|
||||||
const [currentPath, setCurrentPath] = createSignal("/auth/sign-in");
|
const [currentPath, setCurrentPath] = createSignal("/auth/sign-in");
|
||||||
|
|
||||||
|
const commentsSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg>`;
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const path = window.location.pathname;
|
const path = window.location.pathname;
|
||||||
const hash = window.location.hash;
|
const hash = window.location.hash;
|
||||||
|
|
@ -11,51 +13,82 @@ export default function CommentLock() {
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="max-w-4xl mx-auto mt-12 pt-8 border-t border-gray-200">
|
<>
|
||||||
<div class="flex items-center gap-3 mb-8">
|
<div class="max-w-4xl mx-auto mt-12 pt-8 border-t border-gray-200">
|
||||||
<h3 class="text-2xl font-bold text-gray-900">Комментарии</h3>
|
<div class="flex items-center justify-center gap-3 mb-8">
|
||||||
<span class="px-3 py-1 bg-blue-100 text-blue-700 text-sm font-medium rounded-full">
|
<span class="comment-icon" innerHTML={commentsSvg} />
|
||||||
0
|
<h3 class="text-2xl font-bold text-gray-900">Комментарии</h3>
|
||||||
</span>
|
<span class="px-3 py-1 bg-blue-100 text-blue-700 text-sm font-medium rounded-full">
|
||||||
</div>
|
0
|
||||||
|
</span>
|
||||||
<div
|
|
||||||
class="bg-linear-to-br from-gray-50 to-gray-100 rounded-2xl p-8 md:p-12 border border-gray-200 text-center"
|
|
||||||
style="background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="w-20 h-20 mx-auto mb-6 rounded-full flex items-center justify-center"
|
|
||||||
style="background: linear-gradient(135deg, rgba(37, 99, 235, 0.2) 0%, rgba(30, 64, 175, 0.3) 100%);"
|
|
||||||
>
|
|
||||||
<svg class="w-10 h-10 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h4 class="text-xl font-semibold text-gray-900 mb-3">
|
<div
|
||||||
Комментарии доступны только авторизованным пользователям
|
class="bg-linear-to-br from-gray-50 to-gray-100 rounded-2xl p-8 border border-gray-200 text-center"
|
||||||
</h4>
|
style="background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);"
|
||||||
<a
|
|
||||||
href={currentPath()}
|
|
||||||
class="inline-flex items-center justify-center font-semibold transition-all duration-300 rounded-md cursor-pointer px-6 py-3 text-lg"
|
|
||||||
style="background: linear-gradient(to bottom, #2563eb, #1e40af); color: white; box-shadow: 0 2px 4px rgba(37, 99, 235, 0.3);"
|
|
||||||
>
|
>
|
||||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<div
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1"/>
|
class="w-16 h-16 mx-auto mb-5 rounded-full flex items-center justify-center"
|
||||||
</svg>
|
style="background: linear-gradient(135deg, rgba(37, 99, 235, 0.2) 0%, rgba(30, 64, 175, 0.3) 100%);"
|
||||||
Войти в аккаунт
|
|
||||||
</a>
|
|
||||||
<p class="mt-4 text-sm text-gray-600">
|
|
||||||
Нет аккаунта?{" "}
|
|
||||||
<a
|
|
||||||
href={`/auth/sign-up?redirect=${encodeURIComponent(window.location.pathname)}`}
|
|
||||||
class="font-medium hover:underline"
|
|
||||||
style="color: #2563eb;"
|
|
||||||
>
|
>
|
||||||
Зарегистрироваться
|
<svg class="w-8 h-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4 class="text-lg font-semibold text-gray-900 mb-3">
|
||||||
|
Нужна авторизация для комментариев
|
||||||
|
</h4>
|
||||||
|
<a
|
||||||
|
href={currentPath()}
|
||||||
|
class="inline-flex items-center justify-center font-semibold transition-all duration-300 rounded-md cursor-pointer px-5 py-2.5 text-base"
|
||||||
|
style="background: linear-gradient(to bottom, #2563eb, #1e40af); color: white; box-shadow: 0 2px 4px rgba(37, 99, 235, 0.3);"
|
||||||
|
>
|
||||||
|
<svg class="w-4 h-4 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1"/>
|
||||||
|
</svg>
|
||||||
|
Войти в аккаунт
|
||||||
</a>
|
</a>
|
||||||
</p>
|
<p class="mt-3 text-sm text-gray-600">
|
||||||
|
Нет аккаунта?{" "}
|
||||||
|
<a
|
||||||
|
href={`/auth/sign-up?redirect=${encodeURIComponent(window.location.pathname)}`}
|
||||||
|
class="font-medium hover:underline"
|
||||||
|
style="color: #2563eb;"
|
||||||
|
>
|
||||||
|
Зарегистрироваться
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<style>{`
|
||||||
|
.comment-icon {
|
||||||
|
width: 1.75rem;
|
||||||
|
height: 1.75rem;
|
||||||
|
color: #2563eb;
|
||||||
|
}
|
||||||
|
.comment-icon svg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.max-w-4xl { max-width: 100% !important; }
|
||||||
|
.max-w-4xl .text-2xl { font-size: 1.25rem !important; }
|
||||||
|
.max-w-4xl .bg-blue-100 { padding: 0.25rem 0.5rem; font-size: 0.7rem; }
|
||||||
|
.max-w-4xl .rounded-2xl { padding: 1.5rem 1rem !important; margin-left: 0.5rem; margin-right: 0.5rem; }
|
||||||
|
.max-w-4xl .w-16 { width: 2.5rem !important; height: 2.5rem !important; }
|
||||||
|
.max-w-4xl .w-8 { width: 1.25rem !important; height: 1.25rem !important; }
|
||||||
|
.max-w-4xl .text-lg { font-size: 0.95rem !important; margin-bottom: 0.5rem !important; }
|
||||||
|
.max-w-4xl .text-base { font-size: 0.85rem !important; padding: 0.5rem 1rem !important; }
|
||||||
|
.max-w-4xl .w-4 { width: 0.875rem !important; height: 0.875rem !important; }
|
||||||
|
.max-w-4xl .text-sm { font-size: 0.75rem !important; }
|
||||||
|
.max-w-4xl .mt-3 { margin-top: 0.75rem !important; }
|
||||||
|
.max-w-4xl .mb-5 { margin-bottom: 1rem !important; }
|
||||||
|
.max-w-4xl .mb-8 { margin-bottom: 1.5rem !important; padding-top: 1.5rem !important; }
|
||||||
|
.max-w-4xl .pt-8 { padding-top: 2rem !important; }
|
||||||
|
.comment-icon { width: 1.25rem !important; height: 1.25rem !important; }
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1,38 +1,17 @@
|
||||||
import { createSignal, For, Show, onMount } from "solid-js";
|
import { createSignal, For, Show, onMount } from "solid-js";
|
||||||
import CommentLock from "./CommentLock";
|
import CommentLock from "./CommentLock";
|
||||||
import CommentForm from "./CommentForm";
|
import CommentForm from "./CommentForm";
|
||||||
import type { CommentWithReplies } from "../../../types/comments";
|
import type { CommentWithReplies, CommentRecord } from "../../../types/comments";
|
||||||
|
|
||||||
interface CommentsProps {
|
|
||||||
postSlug: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ApiComment {
|
|
||||||
id: string;
|
|
||||||
post_slug: string;
|
|
||||||
user: string;
|
|
||||||
content: string;
|
|
||||||
parent?: string | null;
|
|
||||||
is_verified: boolean;
|
|
||||||
status: "pending" | "published" | "spam";
|
|
||||||
created: string;
|
|
||||||
updated: string;
|
|
||||||
expand?: {
|
|
||||||
user?: {
|
|
||||||
id: string;
|
|
||||||
firstName?: string;
|
|
||||||
name?: string;
|
|
||||||
email: string;
|
|
||||||
avatar?: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ToastMessage {
|
interface ToastMessage {
|
||||||
type: "success" | "error";
|
type: "success" | "error";
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CommentsProps {
|
||||||
|
postSlug: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default function Comments(props: CommentsProps) {
|
export default function Comments(props: CommentsProps) {
|
||||||
const [isAuthenticated, setIsAuthenticated] = createSignal(false);
|
const [isAuthenticated, setIsAuthenticated] = createSignal(false);
|
||||||
const [currentUser, setCurrentUser] = createSignal<{
|
const [currentUser, setCurrentUser] = createSignal<{
|
||||||
|
|
@ -95,7 +74,7 @@ export default function Comments(props: CommentsProps) {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
const commentsWithReplies: CommentWithReplies[] = await Promise.all(
|
const commentsWithReplies: CommentWithReplies[] = await Promise.all(
|
||||||
data.items.map(async (comment: ApiComment) => {
|
data.items.map(async (comment: CommentRecord) => {
|
||||||
const repliesResponse = await fetch(
|
const repliesResponse = await fetch(
|
||||||
`/api/comments?post_slug=${encodeURIComponent(props.postSlug)}&parent=${comment.id}`,
|
`/api/comments?post_slug=${encodeURIComponent(props.postSlug)}&parent=${comment.id}`,
|
||||||
{
|
{
|
||||||
|
|
@ -289,6 +268,15 @@ export default function Comments(props: CommentsProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<style>{`
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.comments-wrapper { padding: 0 0.75rem; }
|
||||||
|
.comments-wrapper h3 { font-size: 1.5rem !important; }
|
||||||
|
.comment-card { padding: 1rem !important; }
|
||||||
|
.comment-avatar { width: 40px !important; height: 40px !important; }
|
||||||
|
.reply-form { margin-left: 0 !important; padding-left: 1rem !important; }
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
<Show when={toast()}>
|
<Show when={toast()}>
|
||||||
{(t) => (
|
{(t) => (
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@
|
||||||
import Logo from "./Logo.astro";
|
import Logo from "./Logo.astro";
|
||||||
import Navbar from "./Navbar.astro";
|
import Navbar from "./Navbar.astro";
|
||||||
import MobileMenu from "./MobileMenu.astro";
|
import MobileMenu from "./MobileMenu.astro";
|
||||||
import LoginButton from "./LoginButton.astro";
|
|
||||||
import UserMenu from "./UserMenu.astro";
|
|
||||||
import { COMPANY } from "@constants";
|
import { COMPANY } from "@constants";
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
1
frontend/src/icons/articles.svg
Normal file
1
frontend/src/icons/articles.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 20 20"><!-- Icon from OOUI by OOUI Team - https://github.com/wikimedia/oojs-ui/blob/master/LICENSE-MIT --><path fill="currentColor" d="M7 0a2 2 0 0 0-2 2h9a2 2 0 0 1 2 2v12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z"/><path fill="currentColor" d="M13 20a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2zM9 5h4v5H9zM4 5h4v1H4zm0 2h4v1H4zm0 2h4v1H4zm0 2h9v1H4zm0 2h9v1H4zm0 2h9v1H4z"/></svg>
|
||||||
|
After Width: | Height: | Size: 467 B |
1
frontend/src/icons/comments.svg
Normal file
1
frontend/src/icons/comments.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Stash Icons by Pingback LLC - https://github.com/stash-ui/icons/blob/master/LICENSE --><path fill="currentColor" fill-rule="evenodd" d="M9.5 3.75c-3.383 0-6.25 2.509-6.25 5.75a5.42 5.42 0 0 0 1.138 3.31l-.612 2.243c-.16.59.448 1.125 1.012.89l2.8-1.167a.75.75 0 0 0-.577-1.385l-1.384.577l.31-1.137a.75.75 0 0 0-.162-.695C5.127 11.405 4.75 10.49 4.75 9.5c0-2.282 2.058-4.25 4.75-4.25c2.08 0 3.791 1.183 4.454 2.772c-3.14.253-5.704 2.663-5.704 5.728c0 3.241 2.867 5.75 6.25 5.75c.766 0 1.503-.127 2.184-.36l2.528 1.052c.564.236 1.172-.3 1.012-.89l-.612-2.242a5.42 5.42 0 0 0 1.138-3.31c0-2.897-2.29-5.208-5.191-5.667c-.694-2.53-3.184-4.333-6.059-4.333m.25 10c0-2.281 2.058-4.25 4.75-4.25s4.75 1.969 4.75 4.25c0 .989-.377 1.905-1.025 2.636a.75.75 0 0 0-.162.695l.31 1.137l-1.384-.577a.75.75 0 0 0-.565-.004A5.2 5.2 0 0 1 14.5 18c-2.692 0-4.75-1.968-4.75-4.25" clip-rule="evenodd"/></svg>
|
||||||
|
After Width: | Height: | Size: 981 B |
|
|
@ -106,11 +106,9 @@ const {
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="article-actions">
|
<div class="article-actions">
|
||||||
<PostReactionButtons postId={postId} initialLikes={initialLikes} initialDislikes={initialDislikes} />
|
<PostReactionButtons postId={postId} initialLikes={initialLikes} initialDislikes={initialDislikes} />
|
||||||
<div class="share-wrapper">
|
<PostSocialShare title={postTitle} url={postUrl} />
|
||||||
<PostSocialShare title={postTitle} url={postUrl} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -123,9 +121,7 @@ const {
|
||||||
<div class="site-container">
|
<div class="site-container">
|
||||||
<div class="article-layout-grid">
|
<div class="article-layout-grid">
|
||||||
<div class="article-main">
|
<div class="article-main">
|
||||||
<article class="article-content-wrapper" id="post-content">
|
<slot />
|
||||||
<slot />
|
|
||||||
</article>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<aside class="article-sidebar" id="sticky-sidebar">
|
<aside class="article-sidebar" id="sticky-sidebar">
|
||||||
|
|
@ -137,6 +133,117 @@ const {
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* ЧЕРНАЯ НУМЕРАЦИЯ */
|
||||||
|
.article-content-wrapper { counter-reset: post-h2; }
|
||||||
|
.article-content-wrapper .post-content h2 { counter-reset: post-h3; counter-increment: post-h2; display: flex; align-items: baseline; gap: 0.6rem; color: #1e3050; font-weight: 800; }
|
||||||
|
.article-content-wrapper .post-content h2::before { content: counter(post-h2) "."; color: #1e3050 !important; flex-shrink: 0; }
|
||||||
|
.article-content-wrapper .post-content h3 { counter-increment: post-h3; display: flex; align-items: baseline; gap: 0.5rem; color: #1e3050; font-weight: 700; }
|
||||||
|
.article-content-wrapper .post-content h3::before { content: counter(post-h2) "." counter(post-h3) "."; color: #1e3050 !important; font-size: 0.9em; flex-shrink: 0; }
|
||||||
|
|
||||||
|
/* Исключаем заголовок "Читайте также" из нумерации */
|
||||||
|
.article-content-wrapper .related-posts .section-title {
|
||||||
|
counter-increment: none !important;
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
.article-content-wrapper .related-posts .section-title::before {
|
||||||
|
content: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Исключаем секцию комментариев из нумерации */
|
||||||
|
.article-content-wrapper .comments-wrapper h2,
|
||||||
|
.article-content-wrapper .comments-wrapper h2::before {
|
||||||
|
counter-increment: none !important;
|
||||||
|
content: none !important;
|
||||||
|
}
|
||||||
|
.article-content-wrapper .comments-wrapper h3,
|
||||||
|
.article-content-wrapper .comments-wrapper h3::before {
|
||||||
|
counter-increment: none !important;
|
||||||
|
content: none !important;
|
||||||
|
}
|
||||||
|
.article-content-wrapper .comments-wrapper .section-title,
|
||||||
|
.article-content-wrapper .comments-wrapper .section-title::before {
|
||||||
|
counter-increment: none !important;
|
||||||
|
content: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* СКРОЛЛ-ФИКС */
|
||||||
|
.article-content-wrapper h2, .article-content-wrapper h3 { scroll-margin-top: 125px; }
|
||||||
|
|
||||||
|
/* СТИЛЬ АКТИВНОГО ПУНКТА В TOC */
|
||||||
|
.toc-item.active .toc-link {
|
||||||
|
color: #eac26e !important;
|
||||||
|
font-weight: 700;
|
||||||
|
background: rgba(234, 194, 110, 0.1);
|
||||||
|
}
|
||||||
|
.toc-item.active .toc-link::before { color: #eac26e !important; }
|
||||||
|
|
||||||
|
html, body { overflow-x: clip !important; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.h-full { height: 100%; }
|
||||||
|
.main-content { padding-top: 0; overflow: visible; }
|
||||||
|
.breadcrumbs-wrapper { padding-top: 5.5rem; background: #f8fafc; }
|
||||||
|
|
||||||
|
/* HERO SECTION */
|
||||||
|
.article-hero { position: relative; width: 100%; height: 550px; background: #0a1a2e; overflow: hidden; }
|
||||||
|
.article-hero-image { position: absolute; inset: 0; z-index: 1; }
|
||||||
|
.article-hero-image img { width: 100%; height: 100%; object-fit: cover; filter: brightness(0.35) grayscale(0.2); transition: all 0.8s cubic-bezier(0.4, 0, 0.2, 1); transform: scale(1.04); }
|
||||||
|
.article-hero-overlay { position: absolute; inset: 0; background: linear-gradient(180deg, transparent 0%, rgba(10, 26, 46, 0.9) 100%); z-index: 2; transition: opacity 0.7s ease; }
|
||||||
|
|
||||||
|
.article-hero:hover .article-hero-image img { filter: brightness(1) grayscale(0); transform: scale(1); }
|
||||||
|
.article-hero:hover .article-hero-overlay { opacity: 0.4; }
|
||||||
|
|
||||||
|
.article-hero-content { position: relative; z-index: 10; height: 100%; }
|
||||||
|
.article-hero-inner { display: flex; flex-direction: column; height: 100%; padding: 3rem 0; }
|
||||||
|
.article-hero-top { margin-bottom: 2rem; }
|
||||||
|
|
||||||
|
.category-strip { background: #eac26e; padding: 0.5rem 1.5rem; display: inline-block; border-radius: 4px; margin-bottom: 2rem; }
|
||||||
|
.category-text { color: #0a1a2e; font-weight: 800; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 1px; font-family: 'Merriweather', Georgia, serif; }
|
||||||
|
.article-title { color: #fff; font-size: clamp(2rem, 5vw, 3.5rem); font-weight: 900; line-height: 1.15; max-width: 950px; text-shadow: 0 4px 15px rgba(0,0,0,0.4); font-family: 'Merriweather', Georgia, serif; }
|
||||||
|
|
||||||
|
.article-hero-bottom { margin-top: auto; display: flex; justify-content: space-between; align-items: center; padding-top: 2rem; border-top: 1px solid rgba(255, 255, 255, 0.15); }
|
||||||
|
.article-actions { display: flex; align-items: center; gap: 2rem; }
|
||||||
|
.article-meta { display: flex; gap: 2rem; justify-content: flex-start; width: auto; }
|
||||||
|
.meta-item { display: flex; align-items: center; gap: 0.6rem; color: #fff; font-size: 0.95rem; white-space: nowrap; }
|
||||||
|
.meta-icon { width: 1.1rem; height: 1.1rem; color: #eac26e; }
|
||||||
|
.meta-author { color: #eac26e; font-weight: 700; }
|
||||||
|
|
||||||
|
/* GRID & STICKY SIDEBAR */
|
||||||
|
.article-body { padding: 0 0 4rem; background: #f8fafc; overflow: visible; }
|
||||||
|
.article-layout-grid { display: flex; gap: 3.5rem; align-items: flex-start; }
|
||||||
|
.article-main { flex: 1; min-width: 0; }
|
||||||
|
.article-content-wrapper { background: #fff; padding: 1.5rem 2.5rem 2.5rem; border-radius: 2rem; box-shadow: 0 4px 30px rgba(0,0,0,0.02); }
|
||||||
|
|
||||||
|
.article-sidebar { width: 320px; flex-shrink: 0; position: sticky; top: 110px; z-index: 50; }
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.article-sidebar { display: none; }
|
||||||
|
.article-layout-grid { flex-direction: column; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.article-hero-bottom { flex-direction: column; align-items: flex-start; gap: 1.5rem; }
|
||||||
|
.article-actions { width: 100%; justify-content: flex-start; }
|
||||||
|
.article-meta { gap: 1.5rem; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.article-hero { height: auto; min-height: 385px; }
|
||||||
|
.article-title { font-size: 1.5rem; }
|
||||||
|
.article-hero-bottom { flex-direction: column; align-items: flex-start; gap: 2rem; }
|
||||||
|
.article-actions { width: 100%; justify-content: flex-start; align-items: flex-end; gap: 1.5rem; }
|
||||||
|
.share-wrapper { display: flex; align-items: flex-end; }
|
||||||
|
.article-content-wrapper { padding: 2.5rem 1.5rem; padding-top: 0; }
|
||||||
|
.article-hero-inner { padding: 2rem 0; }
|
||||||
|
.meta-item { font-size: 0.85rem; }
|
||||||
|
.article-meta { gap: 0.75rem; flex-wrap: wrap; }
|
||||||
|
.category-strip { margin-bottom: 1.5rem; padding: 0.4rem 1rem; }
|
||||||
|
.category-text { font-size: 0.7rem; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function setupArticleLogic() {
|
function setupArticleLogic() {
|
||||||
const sidebar = document.getElementById('sticky-sidebar');
|
const sidebar = document.getElementById('sticky-sidebar');
|
||||||
|
|
@ -199,103 +306,4 @@ const {
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", setupArticleLogic);
|
document.addEventListener("DOMContentLoaded", setupArticleLogic);
|
||||||
document.addEventListener("astro:page-load", setupArticleLogic);
|
document.addEventListener("astro:page-load", setupArticleLogic);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style is:global>
|
|
||||||
/* ЧЕРНАЯ НУМЕРАЦИЯ */
|
|
||||||
#post-content { counter-reset: post-h2; }
|
|
||||||
#post-content h2 { counter-reset: post-h3; counter-increment: post-h2; display: flex; align-items: baseline; gap: 0.6rem; color: #1e3050; font-weight: 800; }
|
|
||||||
#post-content h2::before { content: counter(post-h2) "."; color: #1e3050 !important; flex-shrink: 0; }
|
|
||||||
#post-content h3 { counter-increment: post-h3; display: flex; align-items: baseline; gap: 0.5rem; color: #1e3050; font-weight: 700; }
|
|
||||||
#post-content h3::before { content: counter(post-h2) "." counter(post-h3) "."; color: #1e3050 !important; font-size: 0.9em; flex-shrink: 0; }
|
|
||||||
|
|
||||||
/* Исключаем заголовок "Читайте также" из нумерации */
|
|
||||||
#post-content .related-posts .section-title {
|
|
||||||
counter-increment: none !important;
|
|
||||||
display: block !important;
|
|
||||||
}
|
|
||||||
#post-content .related-posts .section-title::before {
|
|
||||||
content: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Исключаем секцию комментариев из нумерации */
|
|
||||||
#post-content .comments-wrapper h2,
|
|
||||||
#post-content .comments-wrapper h2::before {
|
|
||||||
counter-increment: none !important;
|
|
||||||
content: none !important;
|
|
||||||
}
|
|
||||||
#post-content .comments-wrapper h3,
|
|
||||||
#post-content .comments-wrapper h3::before {
|
|
||||||
counter-increment: none !important;
|
|
||||||
content: none !important;
|
|
||||||
}
|
|
||||||
#post-content .comments-wrapper .section-title,
|
|
||||||
#post-content .comments-wrapper .section-title::before {
|
|
||||||
counter-increment: none !important;
|
|
||||||
content: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* СКРОЛЛ-ФИКС */
|
|
||||||
#post-content h2, #post-content h3 { scroll-margin-top: 125px; }
|
|
||||||
|
|
||||||
/* СТИЛЬ АКТИВНОГО ПУНКТА В TOC */
|
|
||||||
.toc-item.active .toc-link {
|
|
||||||
color: #eac26e !important;
|
|
||||||
font-weight: 700;
|
|
||||||
background: rgba(234, 194, 110, 0.1);
|
|
||||||
}
|
|
||||||
.toc-item.active .toc-link::before { color: #eac26e !important; }
|
|
||||||
|
|
||||||
html, body { overflow-x: clip !important; }
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.h-full { height: 100%; }
|
|
||||||
.main-content { padding-top: 0; overflow: visible; }
|
|
||||||
.breadcrumbs-wrapper { padding-top: 5.5rem; background: #f8fafc; }
|
|
||||||
|
|
||||||
/* HERO SECTION */
|
|
||||||
.article-hero { position: relative; width: 100%; height: 550px; background: #0a1a2e; overflow: hidden; }
|
|
||||||
.article-hero-image { position: absolute; inset: 0; z-index: 1; }
|
|
||||||
.article-hero-image img { width: 100%; height: 100%; object-fit: cover; filter: brightness(0.35) grayscale(0.2); transition: all 0.8s cubic-bezier(0.4, 0, 0.2, 1); transform: scale(1.04); }
|
|
||||||
.article-hero-overlay { position: absolute; inset: 0; background: linear-gradient(180deg, transparent 0%, rgba(10, 26, 46, 0.9) 100%); z-index: 2; transition: opacity 0.7s ease; }
|
|
||||||
|
|
||||||
.article-hero:hover .article-hero-image img { filter: brightness(1) grayscale(0); transform: scale(1); }
|
|
||||||
.article-hero:hover .article-hero-overlay { opacity: 0.4; }
|
|
||||||
|
|
||||||
.article-hero-content { position: relative; z-index: 10; height: 100%; }
|
|
||||||
.article-hero-inner { display: flex; flex-direction: column; height: 100%; padding: 3rem 0; }
|
|
||||||
.article-hero-top { margin-bottom: 2rem; }
|
|
||||||
|
|
||||||
.category-strip { background: #eac26e; padding: 0.5rem 1.5rem; display: inline-block; border-radius: 4px; margin-bottom: 2rem; }
|
|
||||||
.category-text { color: #0a1a2e; font-weight: 800; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 1px; }
|
|
||||||
.article-title { color: #fff; font-size: clamp(2rem, 5vw, 3.5rem); font-weight: 900; line-height: 1.15; max-width: 950px; text-shadow: 0 4px 15px rgba(0,0,0,0.4); }
|
|
||||||
|
|
||||||
.article-hero-bottom { margin-top: auto; display: flex; justify-content: space-between; align-items: center; padding-top: 2rem; border-top: 1px solid rgba(255, 255, 255, 0.15); }
|
|
||||||
.article-actions { display: flex; align-items: center; gap: 2rem; }
|
|
||||||
.article-meta { display: flex; gap: 2.5rem; }
|
|
||||||
.meta-item { display: flex; align-items: center; gap: 0.6rem; color: #fff; font-size: 0.95rem; }
|
|
||||||
.meta-icon { width: 1.1rem; height: 1.1rem; color: #eac26e; }
|
|
||||||
.meta-author { color: #eac26e; font-weight: 700; }
|
|
||||||
|
|
||||||
/* GRID & STICKY SIDEBAR */
|
|
||||||
.article-body { padding: 4rem 0; background: #f8fafc; overflow: visible; }
|
|
||||||
.article-layout-grid { display: flex; gap: 3.5rem; align-items: flex-start; }
|
|
||||||
.article-main { flex: 1; min-width: 0; }
|
|
||||||
.article-content-wrapper { background: #fff; padding: 1.5rem 2.5rem 2.5rem; border-radius: 2rem; box-shadow: 0 4px 30px rgba(0,0,0,0.02); }
|
|
||||||
|
|
||||||
.article-sidebar { width: 320px; flex-shrink: 0; position: sticky; top: 110px; z-index: 50; }
|
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
|
||||||
.article-sidebar { display: none; }
|
|
||||||
.article-layout-grid { flex-direction: column; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.article-hero { height: auto; min-height: 550px; }
|
|
||||||
.article-title { font-size: 1.75rem; }
|
|
||||||
.article-hero-bottom { flex-direction: column; align-items: flex-start; gap: 2rem; }
|
|
||||||
.article-actions { width: 100%; justify-content: space-between; }
|
|
||||||
.article-content-wrapper { padding: 2.5rem 1.5rem; }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -25,6 +25,9 @@ const { title, description, canonicalLink, breadcrumbs } = Astro.props;
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicons/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicons/favicon.svg" />
|
||||||
<link rel="icon" href="/favicons/favicon.ico" />
|
<link rel="icon" href="/favicons/favicon.ico" />
|
||||||
|
<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=Merriweather:ital,wght@0,300;0,400;0,700;0,900;1,300;1,400&display=swap" rel="stylesheet" />
|
||||||
<title>{title} {SITE_TITLE_SUFFIX}</title>
|
<title>{title} {SITE_TITLE_SUFFIX}</title>
|
||||||
<meta name="description" content={description} />
|
<meta name="description" content={description} />
|
||||||
{canonicalLink && <link rel="canonical" href={canonicalLink} />}
|
{canonicalLink && <link rel="canonical" href={canonicalLink} />}
|
||||||
|
|
|
||||||
|
|
@ -37,14 +37,13 @@ while ((match = headingRegex.exec(body)) !== null) {
|
||||||
const text = match[2].trim();
|
const text = match[2].trim();
|
||||||
tocItems.push({ level, id: `heading-${headingIndex++}`, text });
|
tocItems.push({ level, id: `heading-${headingIndex++}`, text });
|
||||||
}
|
}
|
||||||
|
console.log('[DEBUG] tocItems:', JSON.stringify(tocItems));
|
||||||
// Форматируем дату
|
|
||||||
const formatDate = (date: string) => {
|
const formatDate = (date: string) => {
|
||||||
return new Date(date).toLocaleDateString('ru-RU', {
|
const d = new Date(date);
|
||||||
day: 'numeric',
|
const day = d.getDate().toString().padStart(2, '0');
|
||||||
month: 'long',
|
const month = (d.getMonth() + 1).toString().padStart(2, '0');
|
||||||
year: 'numeric'
|
const year = new Date().getFullYear().toString().slice(-2);
|
||||||
});
|
return `${day}/${month}/${year}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Для related posts берем те же категории
|
// Для related posts берем те же категории
|
||||||
|
|
@ -77,22 +76,24 @@ const heroImage = getPostImageUrl(post);
|
||||||
initialLikes={likes}
|
initialLikes={likes}
|
||||||
initialDislikes={dislikes}
|
initialDislikes={dislikes}
|
||||||
>
|
>
|
||||||
<!-- Содержимое статьи -->
|
<!-- Содержимое статьи в article -->
|
||||||
<div class="post-content" set:html={contentHtml} />
|
<article class="article-content-wrapper" id="post-content">
|
||||||
|
<div class="post-content" set:html={contentHtml} />
|
||||||
|
</article>
|
||||||
|
|
||||||
<!-- Система комментариев -->
|
<!-- Комментарии и похожие статьи - ВНЕ article -->
|
||||||
<div class="comments-wrapper">
|
{/*
|
||||||
|
<div class="comments-wrapper">
|
||||||
<Comments postSlug={post.slug} client:load />
|
<Comments postSlug={post.slug} client:load />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<RelatedPosts posts={filteredRelated} currentSlug={slug} />
|
||||||
|
*/}
|
||||||
|
|
||||||
|
<!-- Оглавление - пробуем без slot -->
|
||||||
|
<div slot="sidebar">
|
||||||
|
<ArticleTableOfContents items={tocItems} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Похожие статьи -->
|
|
||||||
<RelatedPosts
|
|
||||||
posts={filteredRelated}
|
|
||||||
currentSlug={slug}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Оглавление в сайдбаре -->
|
|
||||||
<ArticleTableOfContents items={tocItems} slot="sidebar" />
|
|
||||||
</ArticleLayout>
|
</ArticleLayout>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
@ -100,7 +101,8 @@ const heroImage = getPostImageUrl(post);
|
||||||
.post-content {
|
.post-content {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
color: #334155;
|
color: #334155;
|
||||||
line-height: 1.8;
|
line-height: 1.85;
|
||||||
|
font-family: 'Merriweather', Georgia, serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-content :global(h2) {
|
.post-content :global(h2) {
|
||||||
|
|
@ -109,6 +111,10 @@ const heroImage = getPostImageUrl(post);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
margin: 2rem 0 1rem;
|
margin: 2rem 0 1rem;
|
||||||
letter-spacing: -0.02em;
|
letter-spacing: -0.02em;
|
||||||
|
font-family: 'Merriweather', Georgia, serif;
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 0.6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-content :global(h3) {
|
.post-content :global(h3) {
|
||||||
|
|
@ -116,6 +122,10 @@ const heroImage = getPostImageUrl(post);
|
||||||
font-size: 1.375rem;
|
font-size: 1.375rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
margin: 1.75rem 0 1rem;
|
margin: 1.75rem 0 1rem;
|
||||||
|
font-family: 'Merriweather', Georgia, serif;
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-content :global(p) {
|
.post-content :global(p) {
|
||||||
|
|
@ -148,11 +158,19 @@ const heroImage = getPostImageUrl(post);
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-content :global(h2) {
|
.post-content :global(h2) {
|
||||||
font-size: 1.5rem;
|
font-size: 1.15rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-content :global(h3) {
|
.post-content :global(h3) {
|
||||||
font-size: 1.25rem;
|
font-size: 1.05rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-content :global(p) {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-content :global(li) {
|
||||||
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
import Layout from '@layouts/Layout.astro';
|
|
||||||
import { SITE_URL } from '@constants';
|
import { SITE_URL } from '@constants';
|
||||||
|
import Layout from '@layouts/Layout.astro';
|
||||||
import PageHero from "@components/base/PageHero.astro";
|
import PageHero from "@components/base/PageHero.astro";
|
||||||
import CasesList from "@components/cases/CasesList.astro";
|
import CasesList from "@components/cases/CasesList.astro";
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -26,4 +26,10 @@ html, body {
|
||||||
.site-container {
|
.site-container {
|
||||||
padding: 0 2rem;
|
padding: 0 2rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* НУМЕРАЦИЯ ЗАГОЛОВКОВ */
|
||||||
|
.post-content { counter-reset: post-h2; }
|
||||||
|
.post-content h2 { counter-reset: post-h3; }
|
||||||
|
.post-content h2::before { counter-increment: post-h2; content: counter(post-h2) "."; margin-right: 0.5rem; color: #1e3050; font-weight: 700; }
|
||||||
|
.post-content h3::before { counter-increment: post-h3; content: counter(post-h2) "." counter(post-h3) "."; margin-right: 0.4rem; color: #1e3050; font-weight: 600; font-size: 0.9em; }
|
||||||
|
|
@ -15,7 +15,8 @@
|
||||||
"@layouts/*": ["src/layouts/*"],
|
"@layouts/*": ["src/layouts/*"],
|
||||||
"@assets/*": ["src/assets/*"],
|
"@assets/*": ["src/assets/*"],
|
||||||
"@pages/*": ["src/pages/*"],
|
"@pages/*": ["src/pages/*"],
|
||||||
"@lib/*": ["src/lib/*"]
|
"@icons/*": ["src/icons/*"],
|
||||||
|
"@lib/*": ["src/lib/*"],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue