+
{article.title}
-
+
{article.author}
- ·
+ ·
{formatDate(article.published_at)}
{article.reading_time && (
<>
- ·
+ ·
{article.reading_time} мин чтения
>
)}
+
+
+
{article.cover_url && (
-
+
)}
-
+
Статья сгенерирована ИИ под редакторским присмотром.
+
+ {related.length > 0 && (
+
+
+ Похожее
+
+
+ {related.map(a => )}
+
+
+ )}
+
+
>
);
diff --git a/app/globals.css b/app/globals.css
index 7dd9c8a..1c54faa 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -4,15 +4,15 @@
/* === Theme tokens === */
:root {
- --bg: 250 250 249; /* почти белый, тёплый */
- --surface: 255 255 255; /* карточки */
- --surface-2: 245 245 244; /* приглушённый фон */
+ --bg: 250 250 249;
+ --surface: 255 255 255;
+ --surface-2: 245 245 244;
--border: 231 229 228;
- --ink: 28 25 23; /* основной текст */
- --mute: 120 113 108; /* приглушённый текст */
- --accent: 16 185 129; /* emerald-500 */
- --accent-2: 5 150 105; /* emerald-600 */
- --accent-soft: 209 250 229; /* emerald-100 */
+ --ink: 28 25 23;
+ --mute: 120 113 108;
+ --accent: 16 185 129;
+ --accent-2: 5 150 105;
+ --accent-soft: 209 250 229;
}
.dark {
@@ -31,28 +31,39 @@
html {
background: rgb(var(--bg));
color: rgb(var(--ink));
+ scroll-behavior: smooth;
+ /* лучше для мобильных WebKit */
+ -webkit-text-size-adjust: 100%;
+ -webkit-tap-highlight-color: transparent;
+ }
+ body {
+ @apply font-sans antialiased;
+ /* safe areas на iOS */
+ padding-left: env(safe-area-inset-left);
+ padding-right: env(safe-area-inset-right);
}
- body { @apply font-sans antialiased; }
::selection { background: rgb(var(--accent) / 0.25); }
+
+ /* tap target минимум 44px для кнопок и ссылок в навигации/действиях */
+ button, a.btn {
+ min-height: 40px;
+ }
+
+ /* плавный скролл к якорям с учётом sticky header */
+ :where(h1, h2, h3, h4, [id]) {
+ scroll-margin-top: 80px;
+ }
}
@layer components {
.btn {
@apply inline-flex items-center justify-center gap-2 px-4 py-2 rounded-lg font-medium transition-all disabled:opacity-50 disabled:cursor-not-allowed;
}
- .btn-primary {
- background: rgb(var(--accent));
- color: white;
- }
+ .btn-primary { background: rgb(var(--accent)); color: white; }
.btn-primary:hover { background: rgb(var(--accent-2)); }
- .btn-ghost {
- color: rgb(var(--mute));
- }
- .btn-ghost:hover {
- background: rgb(var(--surface-2));
- color: rgb(var(--ink));
- }
+ .btn-ghost { color: rgb(var(--mute)); }
+ .btn-ghost:hover { background: rgb(var(--surface-2)); color: rgb(var(--ink)); }
.container-narrow { @apply max-w-3xl mx-auto px-4; }
.container-wide { @apply max-w-6xl mx-auto px-4; }
@@ -70,10 +81,15 @@
.dark .article-card:hover {
box-shadow: 0 10px 25px -10px rgb(0 0 0 / 0.5);
}
+ /* на мобилке hover-эффект transform не нужен (тач) */
+ @media (hover: none) {
+ .article-card:hover { transform: none; box-shadow: none; }
+ .article-card:active { transform: scale(0.99); }
+ }
.tag {
display: inline-block;
- @apply text-xs px-2.5 py-1 rounded-full transition-colors;
+ @apply text-xs px-2.5 py-1 rounded-full transition-colors whitespace-nowrap;
background: rgb(var(--surface-2));
color: rgb(var(--mute));
}
@@ -91,3 +107,13 @@
.border-b-soft { border-bottom: 1px solid rgb(var(--border)); }
.border-t-soft { border-top: 1px solid rgb(var(--border)); }
}
+
+/* Уважаем системную настройку "меньше анимаций" */
+@media (prefers-reduced-motion: reduce) {
+ html { scroll-behavior: auto; }
+ *, *::before, *::after {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ }
+}
diff --git a/app/layout.js b/app/layout.js
index 7b5e54c..888e3d8 100644
--- a/app/layout.js
+++ b/app/layout.js
@@ -12,9 +12,22 @@ export const metadata = {
locale: 'ru_RU',
siteName: 'ZeroPost',
},
+ alternates: {
+ types: { 'application/rss+xml': '/rss.xml' },
+ },
+};
+
+export const viewport = {
+ width: 'device-width',
+ initialScale: 1,
+ maximumScale: 5,
+ themeColor: [
+ { media: '(prefers-color-scheme: light)', color: '#fafaf9' },
+ { media: '(prefers-color-scheme: dark)', color: '#0a0a0a' },
+ ],
+ viewportFit: 'cover',
};
-// Защита от FOUC: ставим тему до первого рендера
const themeInitScript = `
(function() {
try {
@@ -36,6 +49,7 @@ export default function RootLayout({ children }) {
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400&display=swap"
rel="stylesheet"
/>
+
{children}
Статья сгенерирована ИИ под редакторским присмотром.