Учебное руководство
Главы руководства
На этой странице

Сайт, App Router и Mantine

Оглавление · Далее: API

Маршруты и файлы

URLФайл в apps/web/src/appСодержимое
/page.tsxФотослайдер, последние новости, первые события, информационные блоки
/newsnews/page.tsxВсе новости
/news/[slug]news/[slug]/page.tsxПолный текст новости
/eventsevents/page.tsxВсе события
/events/[slug]events/[slug]/page.tsxПолный текст и параметры события
/api/trpc/*api/trpc/[trpc]/route.tsТипизированный API
/api/webhooks/strapiapi/webhooks/strapi/route.tsПриём изменения CMS
/[slug][slug]/page.tsxИнформационная страница из CMS
/api/media/[filename]api/media/[filename]/route.tsОграниченный proxy фотографий
/api/healthapi/health/route.tsПроверка ответа web

page.tsx создаёт страницу, layout.tsx — общую оболочку, route.ts — HTTP handler. Каталог [slug] обозначает динамический сегмент пути. В Next 16 params в этих страницах ожидается через await; типы PageProps/LayoutProps генерирует Next.

Что выполняется на сервере и в браузере

Страницы и layout — Server Components. Они могут обращаться к серверному caller и готовить HTML без загрузки всей серверной логики в браузер.

providers.tsx, site-shell.tsx, hero-carousel.tsx, information-sections.tsx, partner-sections.tsx, content-lists.tsx помечены "use client". Они используют клиентские компоненты Mantine, состояние карусели, меню и accordion. Серверная страница передаёт им сериализуемые данные.

"use client" задаёт границу клиентского модуля. Это не обещание, что компонент никогда не участвует в первоначальном серверном рендеринге. Секреты и серверные библиотеки не следует импортировать внутрь такой границы.

Документация Next.js о Server/Client Components.

Статическая генерация и обновление

Пошаговый учебный разбор — SSG, ISR и 404.

Главная, списки и опубликованные страницы генерируются при next build из Strapi. Детальные маршруты используют generateStaticParams и force-static: новый slug генерируется при первом посещении без пересборки. Для сборки нужны доступная CMS, опубликованный Site, STRAPI_INTERNAL_URL и read-only STRAPI_READ_TOKEN.

Запросы CMS используют force-cache, тег cms и страховочный revalidate: 3600. Webhook /api/webhooks/strapi проверяет Bearer-секрет и контракт, затем вызывает revalidateTag("cms", { expire: 0 }) и revalidatePath("/", "layout"). Следующий запрос обновляет страницу; повторные посещения получают статический HTML из кеша. Сбрасывается также кеш старых адресов и 404, поэтому работают смена slug, снятие с публикации и публикация ранее отсутствовавшей страницы. Для небольшого сайта намеренно сбрасывается весь редакционный кеш. Hatchet в этой цепочке не участвует.

server/cms/queries.ts дополнительно использует React cache для дедупликации одного render. API остаются динамическими; /api/content-health явно обходит кеш и проверяет живую CMS. По умолчанию файловый кеш принадлежит одному экземпляру Next.js; перед масштабированием понадобится общий cache handler.

Настоящее отсутствие опубликованного slug вызывает notFound(). Ошибка сети, авторизации, pagination или контракта — CmsUnavailableError, а не ложный 404. Неудачная сборка не выпускает пустой сайт. Error/global-error показывают отказ; React streaming не гарантирует HTTP 503. Media proxy отдельно возвращает 404/503.

node scripts/check-static-site.mjs собирает сайт на изолированной CMS и проверяет prerender manifest, cache HIT, обновление через webhook, новый slug, снятие публикации и динамическую проверку состояния. Внешняя CMS и её секреты для этой проверки не нужны.

Устройство Mantine

  1. Layout импортирует @mantine/core/styles.css, затем проектный index.css.
  2. ColorSchemeScript и mantineHtmlProps синхронизируют цветовую схему.
  3. Клиентский Providers создаёт тему и оборачивает дерево в MantineProvider.
  4. Принудительно выбрана светлая схема.

В теме заданы палитра festival, основной цвет, шрифт, радиус 5 px и defaults для Button/Container. CSS задаёт конкретную геометрию копируемого оформления. Это два уровня настройки: компоненты Mantine и композиция страницы.

Официальная инструкция Mantine для Next.js.

Карта компонентов

Файл в src/componentsОтветственность
providers.tsxОбщая тема и provider
site-shell.tsxШапка, desktop/mobile-навигация, Drawer и footer
content-layout.tsxPageContainer, PageHeading, ContentSection и ArticleLayout
content-lists.tsxНезависимые NewsCard/EventCard и списки, использующие эти карточки
hero-carousel.tsxКарусель FestivalHero, массив slides приходит через props
award-strip.tsxИсторические отметки, массив items приходит через props
partner-sections.tsxГруппы партнёров, массив groups приходит через props
information-sections.tsxImageTextSection, LinkCardGrid, DirectionsPanel и FaqSection
festival-home.tsxСерверная композиция информационных блоков главной
blocks-content.tsxБезопасный официальный BlocksRenderer и MediaGallery

CSS Modules и повторное использование

Стили компонентов находятся в соседних *.module.css: Next.js создаёт уникальные имена классов. Импортируйте объект styles и передавайте className={styles.root}. Все размеры и responsive-правила блока держите в его модуле, включая media queries. В index.css остаются шрифт, общие CSS variables, reset, базовая типографика, focus-visible и prefers-reduced-motion. Глобальных классов карточек/страниц нет.

Для внутренних элементов Mantine используйте Styles API. Например, FaqSection передаёт classNames={{ item: styles.faqItem, control: styles.faqControl, panel: styles.faqPanel }}. Не привязывайте проектный CSS к глобальным .mantine-Accordion-* классам. Тема задаёт цвета/default props, module — геометрию. Рекомендация Mantine.

Страница собирается из готовых блоков. Например:

<ContentSection title="Последние новости" action={{ href: "/news", label: "Все новости" }}>
  <NewsList items={news.slice(0, 3)} featured />
</ContentSection>

Для отдельной карточки используйте NewsCard/EventCard. Для FAQ передавайте записи { id, question, content }, для карусели — { title, description, image }. ArticleLayout объединяет ссылку назад, metadata, необязательную обложку и summary; текст Blocks передаётся через children, параметры события — через details. Получение данных и notFound остаются ответственностью серверной страницы.

lib/dates.ts содержит только форматирование. Клиентские карточки импортируют его и import type для NewsItem/EventItem; типы приходят из @gheilt/contracts, данные — из серверного repository. CSS Modules не добавляют runtime-зависимость Emotion и работают с серверными компонентами через стандартную сборку Next.js.

Слайдер не переключается автоматически. Это упрощает чтение и поведение при уменьшении анимации. Новости сортируются по убыванию даты, события — по возрастанию; главная берёт первые три записи из каждого списка. Прошедшие события не фильтруются.

Даты, статус, навигация, логотип, слайды, отметки, страницы и FAQ приходят из CMS. Расписание образца относится к 2025 году, последние исходные новости — к 2026. Архивные даты сохраняются; отсутствующий endsAt не превращается в выдуманное окончание.

Статические ресурсы и дизайн

public/reference содержит шрифт оформления. Логотип/фотографии/партнёры приходят из CMS и Garage; прежние статические копии удалены. В standalone-образе папку public нужно копировать явно — соответствующий шаг есть в Dockerfile.

Источники материалов описаны в DESIGN.md. Редакционные ресурсы загружаются в Strapi/Garage и читаются через /api/media/filename. Старые файлы reference больше не служат источником контента. Шрифт остаётся локальным. Приложение не запрашивает исходный сайт/API во время обычного запроса.

Текущая вёрстка переносит оформление образца, но не реализует его регистрацию, личный кабинет или весь внешний функционал. Скриншотная сверка не выполнена; отсутствие ошибок TypeScript не доказывает визуальную идентичность.

Адаптивность и доступность

Основные CSS breakpoints — 1200, 900 и 600 px. Меню меняется на Burger/Drawer, карточки переходят в одну колонку, изображения получают ограниченную высоту. Есть skip-link, видимый keyboard focus, подписи кнопок карусели и aria-live. Для prefers-reduced-motion отключены анимации и плавный скролл.

Проверьте вручную: узкий экран, увеличение текста, Tab/Shift+Tab, открытие и закрытие меню, переключение карусели, переходы к записи и возврат назад. Эти проверки не автоматизированы — UI-тестов в проекте нет.

Запрет индексации

  • public/robots.txt: User-agent: * и Disallow: /.
  • Layout metadata: index: false, follow: false.
  • next.config.ts: X-Robots-Tag: noindex, nofollow для всех путей.

Robots управляет поведением добросовестных роботов и не закрывает сайт от людей. Он не заменяет аутентификацию и не гарантирует удаление уже известного поисковику URL.

Где смотреть код

Layout, главная, CSS, тема, настройки Next.