Промты для генерации UI-кода: паттерны, которые понимают нейросети

Промты для генерации UI-кода: паттерны, которые понимают нейросети

Разница между «сделай кнопку» и production-ready компонентом — в структуре контекста. Разбираем шаблоны, которые превращают LLM в надежного frontend-инженера.

Atomic Components Composite Patterns Design Tokens Iterative Refinement

Почему 90% промтов дают мусор

LLM не видит ваш кодовую базу, не знает дизайн-систему и не понимает неявных требований доступности. Она предсказывает токены, а не проектирует архитектуру.

Типичный запрос «сделай карточку товара» выдает <div class="card"> с хардкодом пикселей, без TypeScript-интерфейсов, без стейтов загрузки и ошибок, с инлайн-стилями вместо CSS-переменных.

Рабочий промт — это техническое задание в сжатом виде. Он закрывает пробелы контекста до генерации, а не в code-review после.

Контекст > Инструкция

Нейросеть заполняет пробелы средним по датасету. Ваша задача — сузить пространство решений до одного правильного.

Формула «Контекст — Ограничения — Компонент»

1. Контекст среды

Стек (React/Vue/Svelte), версия, UI-кит (Tailwind, CSS Modules, Styled Components), настройка TypeScript (strict mode), линтеры. Укажите файл tsconfig.json при необходимости.

2. Жесткие ограничения

«Никаких инлайн-стилей», «Используй только токены из theme.colors», «Совместимость с SSR», «Покрый доступность по WCAG 2.1 AA», «Макс. 150 строк». Запреты работают лучше пожеланий.

3. Спецификация компонента

Props-интерфейс, варианты (variant/size), стейты (loading/disabled/error), слоты для композиции, forwardRef, побочные эффекты. Опишите API, который хотите получить, а не верстку.

Шаблон промта: Атомарный компонент (Button)

Копируйте, подставляйте свои токены и стек. Работает в Cursor, Copilot, v0, Bolt.new, ChatGPT.

button.prompt.ts
// === КОНТЕКСТ ===
Stack: React 18 + TypeScript (strict: true) + Tailwind CSS v3
Design System: @my-org/ds-tokens (colors, spacing, radius, transitions)
Architecture: Compound Components pattern, forwardRef, class-variance-authority (cva)

// === ОГРАНИЧЕНИЯ ===
- НЕТ инлайн-стилей, только Tailwind / CSS переменные из DS
- НЕТ any, строгая типизация пропсов через discriminated unions
- ОБЯЗАТЕЛЬНО: ARIA-атрибуты, focus-visible, loading/disabled states
- ОБЯЗАТЕЛЬНО: Экспорт типов Props и Variants отдельно
- Bundle size: tree-shakable, нет дефолтного экспорта класса

=== СПЕЦИФИКАЦИЯ КОМПОНЕНТА ===
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: 'primary' | 'secondary' | 'ghost' | 'destructive';
  size?: 'sm' | 'md' | 'lg';
  isLoading?: boolean;
  leftIcon?: React.ReactNode;
  rightIcon?: React.ReactNode;
  fullWidth?: boolean;
}

// Variants mapping (cva config)
// base: "inline-flex items-center justify-center font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none"
// variants: {
//   variant: {
//     primary: "bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm",
//     destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90 shadow-sm",
//     ...
//   },
//   size: { sm: "h-8 px-3 text-xs", md: "h-10 px-4 text-sm", lg: "h-12 px-8 text-base" }
// }}

Паттерн «Composite»: Форма авторизации с валидацией

Промт-шаблон
// Контекст: React Hook Form v7 + Zod + DS Components (Input, Button, Label) // Задача: Форма Login (email, password, remember, forgot link, submit) Требования: 1. Схема Zod: email (обязательно, формат), password (мин 8, 1 спецсимвол) 2. useForm + Controller для каждого поля 3. Валидация на blur и change (mode: "all") 4. Состояния: pristine, dirty, validating, submitting, errors 5. Доступность: aria-describedby для ошибок, autocomplete атрибуты 6. UI: Label + Input + ErrorMessage (красный токен) в вертикальном ритме 8px 7. Submit: disabled пока validating/submitting, спиннер внутри кнопки 8. "Запомнить меня" — чекбокс с кастомным стилем через DS 9. Ссылка "Забыли пароль" — variant="ghost" size="sm" выровнена вправо 10. Экспорт: FormComponent, LoginSchema, type LoginFormData
Ключевые детали, которые ловят ошибки
  • Discriminated Unions для пропсов вариантов — исключает невалидные комбинации на этапе типов.
  • Zod-схема отдельно — позволяет валидировать на бэкенде без дублирования логики.
  • Controller vs register — Controller нужен для сложных инпутов (Select, DatePicker), register — для нативных.
  • aria-live=»polite» на контейнере ошибок — скринридеры إعلان об ошибках без фокуса.
  • forwardRef на корне — критично для фокус-менеджмента в модалках и тултипах.

Versus: Плохой промт vs Архитектурный промт

✕

❌ Плохой промт

"Сделай таблицу пользователей с сортировкой и пагинацией. Используй React и Tailwind."

Что получится:

  • Массив данных захардкожен в компоненте
  • Сортировка только клиентская, ломается на 10k строк
  • Нет скелетонов загрузки, нет ошибок
  • Пагинация — просто кнопки Prev/Next без URL sync
  • Заголовки колонок не типизированы
  • Доступность: ноль, табы не работают
✓

✅ Архитектурный промт

"DataTable<T> generic component.
Props: columns: ColumnDef<T>[], data: T[],
  fetchFn: (params) => Promise<{data: T[], total: number}>,
  sortMode: 'client' | 'server',
  pageSize: number, pageSizeOptions: number[].

Features:
- TanStack Table v8 (headless)
- Server-side sort/pagination/filter via fetchFn
- URL sync (nuqs or searchParams)
- Row selection (checkbox) + bulk actions toolbar
- Skeleton rows (shadcn/ui pattern)
- Error boundary + retry button
- Column resizing, pinning, visibility toggle
- Full keyboard nav + ARIA grid pattern
- Virtualization @tanstack/react-virtual for >100 rows

Types: Export ColumnDef, DataTableProps, ToolbarActions."

Продвинутый уровень: Дизайн-токены как источник истины

JSON для LLM (не CSS)

Не просите «использовать переменные». Дайте токены в JSON — модель точно скопирует имена и значения.

{ "colors": { "primary": "#D80621", "primaryForeground": "#FFFFFF", "muted": "#F5F5F5", "mutedForeground": "#737373", "destructive": "#B50518", "border": "#E5E5E5" }, "radius": { "sm": "4px", "md": "8px", "lg": "12px", "full": "9999px" }, "spacing": { "1": "4px", "2": "8px", "3": "12px", "4": "16px", "6": "24px" } }

Промт для темизации

Добавьте в контекст — получите компоненты, готовые к dark mode и ребрендингу за минуты.

// Темизация через CSS Variables (Tailwind v3.4+) // Токены из JSON выше → tailwind.config.ts → CSS vars Добавь в промт: "Все цвета ТОЛЬКО через CSS variables: - bg-primary → var(--color-primary) - text-primary-foreground → var(--color-primary-foreground) - border → var(--color-border) Dark mode: data-theme="dark" на html. В dark токены меняются: primary → #E53348, muted → #262626. Компонент НЕ должен импортировать токены напрямую. Только через tailwind-классы или style={ { '--color-primary': tokens.primary } } для инлайн-критических мест."

Итеративный цикл: «Сгенерируй → Проверь → Исправь промт»

1

Генерация

Запускаете архитектурный промт. Получаете черновик.

2

Линт + Типы + Тесты

Прогоняете tsc --noEmit, ESLint, Prettier, юнит-тесты (Vitest/RTL). Собираете ошибки в список.

3

Промт-исправление

Не правьте код вручную. Пишите follow-up: «Ошибка TS2322 в строке 45: Prop ‘onClick’ ожидает MouseEvent, приходит void. Исправь тип handler’а в интерфейсе Props.»

4

Коммит промта

Успешный follow-up добавляете в базовый промт как новое ограничение. База знаний растет, качество следующего компонента выше.

Чек-лист готовности промта к production

Промт — это не запрос. Промт — это контракт.

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