Skip to content

Repository files navigation

Zavarka39 web

Одностраничное приложение на React + Vite (TypeScript), работает поверх публичного API https://app-stage.zavarka39.ru/api.

Быстрый старт

# установить зависимости
npm install
# dev-режим
npm run dev
# прод-сборка
npm run build
# прод-сборка с pre-rendering (для SEO)
npm run build:prerender
# предпросмотр
npm run preview

Структура проекта

src/
├── main.tsx                # Entry point
├── App.tsx                 # Корневой компонент, роутинг
├── api.ts                  # HTTP-клиент (JWT + auto-refresh)
├── config.ts               # Константы (URL, ключи)
│
├── components/             # Переиспользуемые UI-компоненты
├── pages/                  # Страницы приложения
├── hooks/                  # React-хуки (auth, cart, route, theme)
├── types/                  # TypeScript-типы
├── utils/                  # Утилиты (format, seo, categories)
│
└── styles/                 # CSS (ITCSS + CSS Layers)
    ├── global.css           # Точка входа (@import + @layer)
    ├── _tokens.css          # Design tokens
    ├── _reset.css           # Browser reset + a11y
    ├── _animations.css      # Все @keyframes
    ├── _utilities.css       # Утилитарные классы
    ├── components/          # Стили компонентов (BEM)
    └── pages/               # Стили страниц

Pre-rendering для SEO

Для улучшения индексации поисковыми системами используется pre-rendering. Скрипт генерирует статические HTML-файлы для всех страниц продуктов с правильными <title> тегами.

Как это работает:

  1. После обычной сборки (npm run build) запускается скрипт pre-rendering
  2. Скрипт получает список всех продуктов из API
  3. Используя Puppeteer, рендерит каждую страницу и сохраняет HTML с обновлённым title
  4. Результат сохраняется в dist/
# Полная сборка с pre-rendering
npm run build:prerender

# Только pre-rendering (после build)
npm run prerender

Pre-rendering занимает время (зависит от количества продуктов). Для быстрой разработки используйте обычный npm run build.

Основные страницы

  • Главная — каталог с поиском, фильтрами по категориям и быстрым добавлением товара.
  • Страница товара — описание, выбор варианта фасовки, добавление в корзину.
    • Галерея изображений с лайтбоксом, свайпом (мобильные), pinch-to-zoom и навигацией клавиатурой.
    • Для категории «чай»: блок заваривания (профили с тултипами), вкусовой профиль (шкалы + чипсы), эффект.
    • Кнопки «Поделиться» (Telegram, WhatsApp, VK, Email) и «Копировать ссылку».
  • Корзина — изменение количества, удаление, итоговая сумма.
  • Оформление заказа — контактные данные, способ доставки, отправка в API.
  • Переключатель темы (светлая / тёмная) с сохранением в localStorage.

Корзина и оформление заказа доступны только авторизованным пользователям.

CSS-архитектура

Стили организованы по принципу ITCSS (от общих к специфичным) с явным управлением каскадом через CSS @layer.

Слои (порядок каскада)

Слой Файл(ы) Назначение
tokens _tokens.css CSS custom properties: цвета, тени, радиусы, переходы, z-index, типографика
reset _reset.css Browser reset, focus-visible, prefers-reduced-motion
animations _animations.css Все @keyframes (единственное место)
utilities _utilities.css Переиспользуемые классы (.button, .pill, .input, .alert)
components components/_*.css Стили компонентов (BEM)
pages pages/_*.css Стили страниц

Как добавлять стили

Новый компонент:

  1. Создать src/styles/components/_name.css
  2. Добавить @import './components/_name.css' layer(components); в global.css

Новая страница:

  1. Создать src/styles/pages/_name.css
  2. Добавить @import './pages/_name.css' layer(pages); в global.css

Правила

  • Все значения — через токены: var(--text-sm), var(--radius-md), var(--transition-fast), var(--shadow-sm), var(--z-overlay)
  • Полупрозрачные цвета: color-mix(in srgb, var(--primary) 50%, transparent) вместо rgba()
  • CSS nesting (&) — только для :hover, :focus, .modifier, [data-theme='dark'] &
  • BEM-элементы (__element) и модификаторы (--modifier) — плоские селекторы (нативный nesting не поддерживает конкатенацию строк)
  • Логические свойства: margin-inline-start вместо margin-left
  • Динамические значения из TSX — через CSS custom properties: style={{ '--delay': '0.1s' }} + animation-delay: var(--delay)
  • @keyframes — определять только в _animations.css
  • Новые токены — добавлять в _tokens.css

Доступность

  • prefers-reduced-motion: reduce — глобальный сброс анимаций/переходов
  • color-scheme: light dark — нативная поддержка тёмной темы
  • accent-color: var(--primary) — стилизация нативных form controls
  • :focus-visible — видимый фокус для клавиатурной навигации

Размышления о выборе стека

Почему React + Vite — не лучший выбор для SEO

При выборе стека для этого проекта была допущена стратегическая ошибка: чистый React (SPA) плохо подходит для e-commerce сайтов, где SEO критически важен.

Проблемы, с которыми столкнулся:

  1. Client-Side Rendering (CSR) — React рендерится на клиенте, поэтому поисковые боты изначально видят пустой <div id="root">. Хотя Googlebot умеет исполнять JavaScript, это:

    • Замедляет индексацию (ботам нужно ждать рендеринга)
    • Не гарантирует корректную индексацию (бот может уйти раньше)
    • Другие поисковики (Яндекс, Bing) хуже справляются с JS-рендерингом
  2. Динамические meta-теги<title>, <meta description>, Open Graph теги должны быть в исходном HTML. В SPA они обновляются через JavaScript, что поисковики могут не увидеть.

  3. Проблема с 404 страницами — SPA всегда отдаёт 200 OK для любого URL (nginx делает fallback на index.html), что мешает корректной обработке удалённых страниц.

Костыли, которые пришлось добавить:

  • Pre-rendering через Puppeteer (scripts/prerender.js) — отдельный шаг сборки, который рендерит каждую страницу в headless Chrome и сохраняет статический HTML. Медленно, хрупко, требует поддержки.

  • Детекция ботов в nginx — специальные правила для определения поисковых ботов и редирект их на pre-rendered версии. Дополнительная сложность в конфигурации.

  • Ручное управление meta-тегами — кастомный хук useSEO, который через document.title и манипуляции с DOM обновляет теги. Работает, но это обходной путь.

  • Статические 404 страницы — отдельная логика для возврата 404 статуса при обращении к несуществующим страницам.

  • Генерация sitemap.xml — отдельный скрипт, который должен знать о всех возможных URL.

Планы на будущее

Цель: миграция на SEO-friendly фреймворк.

Рассматриваемые варианты:

Фреймворк Преимущества Недостатки
Next.js SSR/SSG из коробки, App Router, React Server Components, отличная экосистема Привязка к Vercel для лучшего опыта, может быть избыточен
Astro Islands architecture, минимум JS на клиенте, отличный для контентных сайтов Меньше интерактивности, новый подход
Remix SSR, прогрессивное улучшение, хорошая работа с формами Менее популярен, меньше экосистема
Nuxt 3 (Vue) SSR/SSG, отличный DX, хорошая документация Переход с React на Vue

Наиболее вероятный выбор: Next.js — позволит переиспользовать React-компоненты, обеспечит SSR/SSG из коробки, имеет встроенную генерацию sitemap и обработку meta-тегов.

Уроки на будущее

Для любого проекта, где SEO важен (e-commerce, лендинги, контентные сайты), с самого начала выбирайте фреймворк с SSR/SSG: Next.js, Nuxt, Astro, SvelteKit. Чистый SPA (Create React App, Vite + React) подходит только для приложений за авторизацией (админки, дашборды, внутренние инструменты).

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages