В прошлом уроке мы вывели данные из базы Convex в виде простого текста. Чтобы превратить этот список в профессиональный интерфейс стартапа, не нужно быть дизайнером или писать сотни строк CSS. Мы воспользуемся инструментами, которые экономят недели разработки.
Почему мы не пишем CSS с нуля
Для MVP важна скорость проверки гипотез. В современной разработке принят компонентный подход: вы не рисуете каждую кнопку, а собираете интерфейс из готовых, качественных блоков.
Мы будем использовать shadcn/ui. Это не библиотека в привычном смысле, а UI-кит — набор исходного кода элементов. Вы копируете код компонента в свой проект (в папку components/ui) и получаете над ним полный контроль. Нужно изменить логику работы выпадающего списка? Просто откройте файл и поправьте его.
Инструменты дизайна
Для настройки внешнего вида используется Tailwind CSS. Это фреймворк, где стили задаются прямо в коде страницы через короткие классы:
flex— выравнивает элементы в ряд.gap-4— создает отступы.bg-blue-500— заливает фон синим.rounded-xl— закругляет углы.
В шаблонах Next.js Tailwind уже настроен. Вам остается только комбинировать классы, как конструктор.
Установка первой детали: Кнопка
Вместо копирования кода вручную, используем CLI (командную строку). Введите в терминале:
npx shadcn-ui@latest add button
В проекте появится файл components/ui/button.tsx. Теперь кнопку можно импортировать в любой экран.
Плохо (обычный HTML):
<button style={{backgroundColor: 'blue', borderRadius: '8px', padding: '10px'}}>
Отправить
</button>
Проблема: такой код трудно менять, он быстро превращается в «спагетти».
Хорошо (shadcn/ui + Tailwind):
import { Button } from "@/components/ui/button"
export default function Page() {
return (
<Button variant="default" className="shadow-lg">
Отправить
</Button>
)
}
Практикум: Создаем карточку товара или задачи
Свяжем данные из Convex с красивой версткой. Нам понадобятся компоненты Card (карточка) и Badge (статус-метка).
Добавьте их в проект:
npx shadcn-ui@latest add card badge
Создадим компонент TaskList.tsx, который отобразит данные из базы в виде сетки:
'use client'
import { useQuery } from "convex/react";
import { api } from "../convex/_generated/api";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
export function TaskList() {
const tasks = useQuery(api.tasks.get);
if (!tasks) return <p>Загрузка...</p>;
return (
<div className="grid gap-4 p-6">
{tasks.map((task) => (
<Card key={task._id} className="hover:shadow-md transition-shadow">
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle className="text-lg">{task.text}</CardTitle>
<Badge variant={task.isCompleted ? "default" : "secondary"}>
{task.isCompleted ? "Готово" : "В работе"}
</Badge>
</CardHeader>
<CardContent>
<Button size="sm" variant="outline">Редактировать</Button>
</CardContent>
</Card>
))}
</div>
);
}
Итоги урока
- Внешний вид: Приложение выглядит как современный SaaS-сервис благодаря выверенным отступам и шрифтам shadcn/ui.
- Связь с данными: Мы объединили
useQueryи визуальные компоненты. Как только данные в Convex изменятся, интерфейс обновится сам. - Управляемость: Любой элемент можно перекрасить или изменить за секунды через классы Tailwind.
Совет: Установите расширение Tailwind CSS IntelliSense для VS Code. Оно подсказывает названия классов и показывает цвет прямо в редакторе. 💡
Сейчас наше приложение открыто для всех. Чтобы превратить его в настоящий бизнес, нужно разграничить доступ. В следующем уроке мы внедрим авторизацию, чтобы пользователи могли входить через Google или GitHub и работать только со своими данными.