10 дизайн-пресетов для AI-презентаций: от идеи до деплоя
Знаешь это ощущение, когда продукт технически работает, но выглядит так, будто его дизайнил бухгалтер в 2009 году? Именно с этим я столкнулся в проекте kp-generator. Презентации генерировались, Google Slides API пахал, Claude сочинял контент — но визуально всё это было... мягко говоря, непрезентабельно. Шесть тем в обычном <select> дропдауне: auto, dark, light, blue, green, gradient. Без превью, без характера, без души. Для продукта, который должен генерировать «лучшую в мире КП-презентацию» — это было недопустимо.
В этой статье расскажу, как за один сессионный спринт мы прошли путь от «блядь, сделай нормальные пресеты» до деплоя 10 полноценных дизайн-стилей с живыми CSS-превью, интеграцией в Google Slides API и обновлённым Claude-промптом.
Контекст проекта
kp-generator — это Next.js-приложение, которое генерирует коммерческие предложения в формате Google Slides. Пользователь вводит данные о клиенте и задаче, Claude (через Anthropic API) разбивает контент на слайды, Google Slides API собирает финальную презентацию.
Архитектура примерно такая:
Пользователь → Next.js UI → API Route → Claude (структура контента) → Google Slides API (рендер) → Ссылка на презентацию
Основной стек:
- Next.js 14 с App Router
- TypeScript строгий режим
- Anthropic Claude API для генерации контента
- Google Slides API для сборки слайдов
- Деплой через Dokploy на VPS
Проблема: дизайн как антипродажник
Представь: ты открываешь инструмент для создания КП, нажимаешь «Выбрать тему» и видишь выпадающий список:
<select name="theme">
<option value="auto">auto</option>
<option value="dark">dark</option>
<option value="light">light</option>
<option value="blue">blue</option>
<option value="green">green</option>
<option value="gradient">gradient</option>
</select>И это всё. Никакого превью. Никакого визуального контекста. Ты выбираешь вслепую, генеришь, ждёшь 30 секунд, открываешь Slides — и видишь что-то среднее между корпоративным шаблоном из PowerPoint 2007 и скучным отчётом.
Проблема была не только эстетическая. Для B2B-продаж визуальная подача — это часть доверия. Презентация, которая выглядит как черновик, подрывает ценность предложения ещё до того, как клиент прочитает первый слайд. Исследования Gong (67 тысяч записей переговоров) показывают, что первые 90 секунд презентации критичны — и визуал здесь играет огромную роль.
Ещё одна системная проблема: текущий подход передавал Claude только текстовый hint ("dark", "light"), и модель сама решала палитру. Результат был непредсказуемым — одна и та же тема могла выглядеть по-разному при каждой генерации.
Решение: пресеты с характером
Шаг 1: Определить типы
Первым делом — строгие TypeScript-типы. Пресет должен быть детерминированным объектом, а не набором туманных хинтов.
// lib/theme-presets.ts
export interface ThemePreset {
id: string;
name: string;
description: string;
palette: {
background: string;
primary: string;
accent: string;
text: string;
textSecondary: string;
border: string;
};
fonts: {
heading: string;
body: string;
mono?: string;
};
style: {
borderRadius: string;
borderWeight: number;
decorativeElements: string[];
layoutCharacter: string;
};
claudeHint: string; // подсказка для модели
}Ключевое решение здесь — поле claudeHint. Вместо того чтобы передавать просто "dark" или "neon", теперь Claude получает развёрнутое описание стиля: какой характер у презентации, как расставлять акценты, какой tone of voice соответствует визуалу.
Шаг 2: 10 пресетов с реальным характером
Вместо скучных цветовых вариаций — 10 стилистически разных вселенных:
| Пресет | Характер | Для кого | |--------|----------|----------| | Brutalism | Кремовый фон, красный акцент, Space Grotesk, толстые рамки | Стартапы, дизайн-агентства | | Neon Cyber | Чёрный фон, неоновый пурпурный, светящиеся элементы | IT, SaaS, tech-продукты | | Glassmorphism | Тёмно-синий, белое с прозрачностью, размытые слои | Fintech, премиум-сервисы | | Gradient Wave | Фиолетово-синий градиент, плавные переходы | Креативные агентства | | Dark Executive | Графитовый, золотой акцент, Inter | Консалтинг, B2B-enterprise | | Corporate Clean | Белый, корпоративный синий, минимализм | Традиционные B2B | | Retro Pop | Жёлтый, красный, Playfair Display | FMCG, розница | | Editorial | Белый, чёрный, акцент-оранжевый | Медиа, публичные компании | | Nature Organic | Тёплый бежевый, зелёный, мягкие формы | Экологичные продукты, HoReCa | | Mono Sharp | Чёрно-белый строгий, минимум цвета | Юридические услуги, аудит |
Каждый пресет — детальный объект с реальными hex-кодами, именами Google Fonts и списком декоративных элементов. Никакой неопределённости.
Шаг 3: CSS-превью в реальном времени
Самая важная UX-часть — визуальное превью перед генерацией. Сделал компонент ThemePreview, который рисует миниатюрный слайд средствами CSS, точно отражая финальный результат в Google Slides.
// components/ThemePreview.tsx
export function ThemePreview({ preset, size = 'sm' }: ThemePreviewProps) {
const { palette, fonts, style } = preset;
return (
<div
className={cn('relative overflow-hidden', sizeClasses[size])}
style={{
background: palette.background,
borderRadius: style.borderRadius,
border: `${style.borderWeight}px solid ${palette.border}`,
fontFamily: fonts.body,
}}
>
{/* Заголовок слайда */}
<div
style={{
color: palette.primary,
fontFamily: fonts.heading,
borderBottom: `2px solid ${palette.accent}`,
}}
className="text-xs font-bold px-2 pt-2 pb-1"
>
Заголовок слайда
</div>
{/* Акцентный блок */}
<div
style={{ background: palette.accent }}
className="mx-2 mt-1 h-1 rounded-full"
/>
{/* Текст-заглушка */}
<div className="px-2 pt-1 space-y-0.5">
{[80, 65, 75].map((width, i) => (
<div
key={i}
style={{
background: palette.textSecondary,
width: `${width}%`,
}}
className="h-1 rounded opacity-40"
/>
))}
</div>
</div>
);
}Важный момент: CSS-превью намеренно упрощённое, но цвета и шрифты — один в один с тем, что уйдёт в Google Slides. Пользователь видит именно ту палитру и типографику, которая будет в финальной презентации.
Шаг 4: Обновить Google Slides renderer
Самая объёмная часть — интеграция пресетов в Google Slides API. Раньше renderer использовал захардкоженные цвета. Теперь он получает объект ThemePreset и применяет палитру к каждому элементу слайда.
// Было (до рефакторинга)
function createTitleSlide(slidesService: slides_v1.Slides, presentationId: string) {
// Захардкоженные цвета
const TITLE_COLOR = { red: 0.1, green: 0.1, blue: 0.4 };
const ACCENT_COLOR = { red: 0.9, green: 0.5, blue: 0 };
// ...
}
// Стало (после рефакторинга)
function createTitleSlide(
slidesService: slides_v1.Slides,
presentationId: string,
theme: ThemePreset
) {
const titleColor = hexToRgb(theme.palette.primary);
const accentColor = hexToRgb(theme.palette.accent);
const bgColor = hexToRgb(theme.palette.background);
// ...
}Вспомогательная функция hexToRgb конвертирует hex в формат, который принимает Google Slides API (значения от 0 до 1):
function hexToRgb(hex: string): { red: number; green: number; blue: number } {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
if (!result) return { red: 0, green: 0, blue: 0 };
return {
red: parseInt(result[1], 16) / 255,
green: parseInt(result[2], 16) / 255,
blue: parseInt(result[3], 16) / 255,
};
}Шаг 5: Обновить Claude-промпт
LLM получает теперь не просто "dark" или "neon", а полный контекст стиля:
const themeContext = `
Визуальный стиль презентации: ${theme.name}
${theme.claudeHint}
Палитра:
- Фон: ${theme.palette.background}
- Основной цвет: ${theme.palette.primary}
- Акцент: ${theme.palette.accent}
Характер контента для этого стиля:
${theme.style.layoutCharacter}
Адаптируй тон и структуру контента под этот визуальный стиль.
`;Теперь для Corporate Clean Claude пишет сдержанно и по делу, для Neon Cyber — с энергией и технологическими отсылками, для Dark Executive — с весомостью и конкретикой. Визуал и контент работают в связке.
Пайплайн разработки: subagent-driven
Отдельно хочу отметить подход к реализации. Весь процесс шёл через Claude с командой /pipeline — последовательные этапы: brainstorming → план → subagent-driven development.
Каждая из 12 задач выполнялась отдельным subagent'ом с нуля (без контекста предыдущих) и проходила двухэтапное ревью: сначала проверка соответствия спецификации, затем code quality review. Подход дал неожиданно хорошие результаты — несовпадения между CSS-превью и Google Slides были пойманы именно на этапе финального code review, а не после деплоя.
Всего 6 коммитов от идеи до продакшена:
86dfa86 feat: ThemePreset type + 10 presets
09bc00b feat: ThemePreview CSS component + ThemeSelector
1a50155 feat: Create/Edit pages use ThemeSelector
f1770e7 feat: slide layouts renderer + Google Slides integration
d84991e feat: Claude prompt + API routes update
eb3e0ed fix: color sync CSS-preview vs Google Slides, edit page fallback
Результат
После деплоя на Dokploy:
- 10 дизайн-пресетов с реальными именами и характером вместо 6 безликих слов
- CSS-превью — пользователь видит миниатюру слайда до генерации
- Детерминированный рендер — один пресет = один предсказуемый визуальный результат
- Claude знает контекст стиля — контент адаптируется под выбранный визуал
- Шрифты Google Fonts подключены через
next/fontдля корректного рендера в UI - Билд проходит без ошибок, TypeScript strict режим доволен
Субъективно: разница огромная. Открываешь селектор — видишь живые миниатюры. Выбираешь Brutalism — получаешь грубую, мощную презентацию с характером. Выбираешь Dark Executive — получаешь строгий, весомый документ.
Выводы
Дизайн-система начинается с типов. Самое важное решение в этом проекте — строгий ThemePreset интерфейс с явными полями для каждого аспекта стиля. Когда у тебя есть типизированный объект вместо строки-хинта, автоматически исчезает целый класс проблем: несогласованность между превью и результатом, непредсказуемое поведение AI, сложность добавления новых тем. Инвестиция в типы окупается многократно.
Превью — это не «приятная фича», это обязательная часть UX. Пользователь, который выбирает визуальный стиль вслепую, рано или поздно разочаруется — не потому что результат плохой, а потому что он не совпал с ожиданием. CSS-превью, которое честно отражает финальный результат (даже в упрощённом виде), снимает это напряжение и повышает доверие к инструменту. Для B2B-продукта, где каждое КП создаётся под конкретного клиента, это критично.
LLM как часть дизайн-системы требует контекста, а не хинтов. Когда ты передаёшь Claude строку "dark" — он строит предположения. Когда ты передаёшь claudeHint с описанием характера стиля, целевой аудитории и tone of voice — он адаптирует контент осознанно. Разница в качестве вывода — ощутимая. Думай о системном промпте не как о команде, а как о брифе для дизайнера.
Subagent-driven development работает лучше монолитных сессий. Изначально казалось контрпродуктивным: зачем запускать новый subagent без контекста предыдущего? Но именно это «незнание» даёт чистый взгляд на задачу. Финальный code review поймал несовпадение цветов между CSS-превью и Google Slides — баг, который я сам пропустил бы, потому что был «внутри» процесса. Свежий взгляд без накопленного контекста — неожиданно мощный инструмент quality control.
Итог по kp-generator: инструмент перешёл из категории «работает технически» в категорию «хочется использовать». Следующий шаг — A/B тест, какие пресеты дают лучшую конверсию в реальных КП. Данные собирать будем.

Фулстек-разработчик, строю SaaS-продукты и автоматизации на Next.js, Python и AI. Пишу о реальных кейсах из продакшена.
Связанный проект
Смотреть в портфолио →