П/ВИН

Лендинг конференции за один день: Next.js + Dokploy

·8 мин чтения
Лендинг конференции за один день: Next.js + Dokploy

Иногда проект начинается с одного сообщения в чат. «Мне надо сделать сайт моего мероприятия» — и понеслось. Именно так появился 18ai.pashavin.ru — лендинг для конференции «Навстречу к AI», которую мы провели вживую, с реальными спикерами, площадкой «Калибр» и всеми радостями офлайн-ивента, включая техничку, которая подвела прямо перед стартом.

В этой статье расскажу, как за одну сессию разработки мы получили полноценный продакшн-сайт: с анимациями, галереей, секцией дресс-кода с реальными референсами, мобильной вёрсткой и автодеплоем через Dokploy. По пути решили несколько нетривиальных задач — от горизонтального скролла на мобилке до построения CI/CD-пайплайна через webhook.

Контекст: зачем вообще делать сайт после ивента

Конференция «Навстречу к AI» уже прошла. Спикеры отстрелялись, гости разошлись, площадка освободилась. Казалось бы — зачем сайт? Но у постфактум-лендингов есть своя ценность:

  • SEO и органика. Люди гуглят «AI конференция [город]» — и находят тебя, узнают о следующем событии.
  • Социальное доказательство. Фотогалерея, спикеры, программа — всё это работает как портфолио организатора.
  • База для следующего ивента. Шаблон уже готов, следующий раз — просто обновить контент.
  • Дресс-код и атмосфера. Люди хотят знать, как одеваться. Хороший лендинг снимает этот вопрос заранее.

Так что сайт — это не «запоздалый пиар», а инструмент с долгосрочной отдачей.

Стек и архитектурные решения

Выбор стека был очевиден — он диктуется общим дизайн-системой pashavin.ru:

  • Next.js 14 — App Router, статическая генерация, оптимизация изображений из коробки
  • Tailwind CSS — утилитарные классы, нет необходимости изобретать велосипед
  • GSAP — анимации заголовка, плавные появления секций
  • Dokploy — собственная PaaS-платформа для деплоя через Docker
  • GitHub Webhook — автодеплой при пуше в master

Дизайн строго выдержан в фирменном стиле: тёмный фон, оранжевый акцент #FF4F00, шрифт Syne. Никаких Bootstrap, никаких готовых UI-китов — всё кастомное.

Hero-секция: GSAP и посимвольная анимация

Главный заголовок «НАВСТРЕЧУ К AI» анимируется посимвольно при загрузке страницы. Каждая буква появляется с небольшой задержкой, создавая эффект «набора текста» в обратном направлении.

useEffect(() => {
  if (!titleRef.current) return;
  const chars = titleRef.current.querySelectorAll('.char');
  gsap.fromTo(
    chars,
    { opacity: 0, y: 40 },
    {
      opacity: 1,
      y: 0,
      duration: 0.6,
      stagger: 0.05,
      ease: 'power3.out',
    }
  );
}, []);

Первая проблема возникла на мобилке: заголовок не помещался в одну строку и переносился. Решение — clamp() с меньшим минимальным значением:

/* До */
font-size: clamp(2.5rem, 12vw, 10rem);
 
/* После */
font-size: clamp(1.8rem, 9vw, 10rem);

Простая правка, но она кардинально меняет восприятие на экранах шириной 375–430px.

Секция дресс-кода: от идеи до референсов

Одна из самых интересных задач — секция дресс-кода. Изначально стиль назывался «Фестивальный», но в процессе итераций его переименовали в Smart Casual — более точное определение того, что ожидается от гостей.

Требования к фото были чёткими: качественные, дорогие на вид, соответствующие тёмному дизайну сайта. Первая попытка — Unsplash. Результат не устроил: фото были слишком generic, не передавали нужную атмосферу.

Вторая итерация — Pinterest. Организатор сам подобрал референсы и прислал прямые ссылки. Это оказалось правильным решением: человек, который организует мероприятие, лучше понимает нужную атмосферу, чем алгоритм поиска стоков.

const menPhotos = [
  'https://i.pinimg.com/736x/be/07/78/be077825a1f8fd206bb3d7b8d57a829c.jpg',
  'https://i.pinimg.com/736x/78/af/5d/78af5ddc996ed1df4c318cd4e64b2a82.jpg',
  'https://i.pinimg.com/736x/f2/9e/6c/f29e6ccde7b26bc5537ac9200303bc46.jpg',
];
 
const womenPhotos = [
  'https://i.pinimg.com/736x/bd/93/46/bd934626fc87ff67274a1b5defa96040.jpg',
  'https://i.pinimg.com/1200x/e6/4c/e2/e64ce2ff92006fdb85e7e98c05668194.jpg',
  // ...
];

Тексты тоже переработали под новый стиль:

До (Фестивальный стиль):

Пиджак с рубашкой, галстук по желанию. Нарядный, но не официальный — фестивальный дух.

После (Smart Casual):

Чиносы или брюки, рубашка или поло, пиджак по желанию. Аккуратно, но без галстука — расслабленная элегантность.

Мелочь? Нет. Дресс-код — это первое, что многие читают перед ивентом. Точность формулировок напрямую влияет на то, как выглядит зал.

Мобильная вёрстка: горизонтальный скролл вместо стопки

Первая версия мобильной адаптации была предсказуемой: всё в одну колонку, огромные отступы, бесконечный скролл вниз. Это работает, но скучно и неэффективно по использованию экрана.

Решение — горизонтальный скролл со scroll-snap для всех фото-секций. Паттерн выглядит так:

{/* Мобилка — горизонтальный скролл */}
<div className="flex md:hidden gap-4 overflow-x-auto pb-4 photo-scroll">
  {photos.map((src, i) => (
    <div key={i} className="flex-none w-[70vw] h-[280px] relative rounded-xl overflow-hidden">
      <Image src={src} alt="" fill className="object-cover" />
    </div>
  ))}
</div>
 
{/* Десктоп — грид */}
<div className="hidden md:grid grid-cols-3 gap-6">
  {/* ... */}
</div>
.photo-scroll {
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
 
.photo-scroll > * {
  scroll-snap-align: start;
}
 
.photo-scroll::-webkit-scrollbar {
  display: none;
}

То же самое применили к карточкам спикеров (80vw на карточку) и галерее мероприятия (65vw). Результат — мобильная версия ощущается как нативное приложение, а не как мобильная web-страница.

Проблема с воздухом на десктопе решилась просто — уменьшили вертикальные отступы:

// До
<section className="py-32">
 
// После  
<section className="py-20">

py-32 — это 128px сверху и снизу. Для десктопа с большим монитором это выглядело как пустота между блоками. py-20 (80px) — более плотная и профессиональная вёрстка.

Галерея без лайтбокса: осознанное решение

Лайтбокс — стандартная фича для фотогалерей. Кликаешь на фото — оно открывается на весь экран. Но в этом проекте от него отказались сознательно и дважды.

Первый раз лайтбокс убрали по дизайнерским соображениям. Потом он вернулся при рефакторинге — и его убрали снова, уже окончательно. Причина простая: галерея служит настроению, а не просмотру. Гостям не нужно разглядывать каждое фото в деталях — им нужно поймать атмосферу. Горизонтальный свайп справляется с этим лучше, чем модальное окно.

Технически это означало полное удаление:

  • State для активного фото (selectedPhoto, isOpen)
  • Обработчиков событий (onClick, onClose)
  • Компонента модального окна
  • Счётчика в лайтбоксе

Очистка кода — тоже разработка.

Деплой: Dokploy + GitHub Webhook

Пожалуй, самая интересная часть с технической точки зрения. Вместо ручного деплоя настроили полноценный CI/CD через Dokploy — собственную PaaS-платформу на VPS.

Схема работы:

  1. Пуш в master на GitHub
  2. GitHub отправляет webhook на Dokploy API
  3. Dokploy клонирует репозиторий, собирает Docker-образ
  4. Контейнер поднимается, Let's Encrypt выдаёт HTTPS-сертификат
  5. Сайт доступен по домену

Dockerfile для Next.js приложения:

FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
 
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 3000
CMD ["node", "server.js"]

Первый деплой споткнулся о приватный репозиторий — Dokploy не имел SSH-ключа с доступом. Решение: репо сделали публичным (лендинг не содержит секретов, всё в .env файлах).

Вторая проблема — Dokploy не мог найти Dockerfile, потому что искал его в code/Dockerfile вместо корня. Решилось указанием правильного dockerfilePath через API.

После этих двух правок — всё заработало. Wildcard DNS уже был настроен (*.pashavin.ru → VPS IP), поэтому поддомен 18ai.pashavin.ru поднялся сразу.

Автоматизация: скилл и агент для Dokploy

После десятка задеплоенных проектов на одной и той же платформе стало понятно: паттерны повторяются. Создание проекта в Dokploy, настройка git-источника, добавление домена, запуск деплоя — это одна и та же последовательность API-вызовов.

Решение — создать скилл dokploy-deploy и агента dokploy-deployer для Claude Code:

~/.claude/skills/dokploy-deploy/SKILL.md     # документация паттернов
~/.claude/agents/dokploy-deployer.md          # автономный агент

Скилл содержит:

  • Полный референс API endpoints и их параметры
  • Типичные pitfalls и их решения (таблицей)
  • Шаблон Dockerfile для Next.js
  • Quick reference с ключевыми константами

Агент работает по 9-шаговому workflow:

  1. Анализ проекта (определение стека, порта, переменных)
  2. Инициализация git-репозитория
  3. Создание проекта в Dokploy
  4. Создание приложения с buildType=dockerfile
  5. Настройка git-источника и домена
  6. Добавление webhook на GitHub
  7. Добавление проекта в git-auto-sync
  8. Первый пуш и запуск деплоя
  9. Мониторинг и диагностика

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

Результат

За одну сессию разработки получили:

  • Продакшн-сайт на https://18ai.pashavin.ru с HTTPS
  • 7 полноценных секций: Hero, Marquee, О мероприятии, Спикеры, Программа, Дресс-код, Фотогалерея
  • Мобильная вёрстка с горизонтальным скроллом на всех фото-блоках
  • GSAP-анимации на заголовке и при появлении секций
  • Автодеплой — пуш в git автоматически обновляет сайт
  • Скилл и агент для ускорения следующих деплоев

Время от «мне надо сделать сайт» до работающего продакшна — несколько часов.

Выводы

Первый и главный урок: контентные решения важнее технических. Мы поменяли «Фестивальный стиль» на «Smart Casual», переписали два абзаца текста, заменили стоковые фото на Pinterest-референсы. Это не код — но это то, что гость запомнит и на что будет ориентироваться при выборе одежды. Технологии доставляют контент; контент создаёт опыт.

Второй урок: итеративность быстрее перфекционизма. Лайтбокс убирали дважды. Фото дресс-кода меняли трижды. Мобильная вёрстка прошла через несколько итераций. Каждый раз это было быстрое изменение → деплой → фидбек → следующая итерация. Такой подход даёт результат быстрее, чем попытка сделать всё идеально с первого раза.

Третий урок: горизонтальный скролл недооценён. На мобилках мы привыкли к бесконечному вертикальному скроллу. Но для галерей и карточек горизонтальный свайп со scroll-snap — это качественно другой UX. Он компактнее, интерактивнее и ощущается как нативное приложение. Стоит добавить в арсенал и использовать осознанно.

Четвёртый урок: документируй паттерны, пока они свежие. Скилл для Dokploy появился после десятого проекта на этой платформе. Стоило сделать это после третьего. Каждый раз, когда решаешь одну и ту же проблему во второй раз — это сигнал: пора написать документацию или создать инструмент. Время, вложенное в автоматизацию рутины, возвращается с процентами уже на следующем проекте.

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

«Навстречу к AI» получилась мясной и крутой — по словам самого организатора. Техничка подвела, онлайн-трансляция пошла в брак, но контент был живым. Лендинг теперь сохраняет эту историю в сети.

Паша Вин
Паша Вин

AI-инженер, предприниматель, маркетолог. Основатель feberra.com и x10seo.ru. 13 лет в перфоманс-маркетинге, 3 года в системной интеграции AI в бизнес.

Связанный проект

AI Content Aggregator