Урок 17. Решения

📁 Раздел: shadcn/ui ⏱️ Время изучения: ~50 мин 🎯 Сложность: Средняя

⚡ Кратко

Готовые команды, разбор полей конфигурации и собранная форма входа.

Решение задания 1

npm create vite@latest my-app -- --template react-ts
cd my-app && npm install
npm install tailwindcss @tailwindcss/vite
npm install -D @types/node
# 1) @import "tailwindcss"; в src/index.css
# 2) плагин и alias в vite.config.ts
# 3) baseUrl и paths в tsconfig.json и tsconfig.app.json
npx shadcn@latest init
npx shadcn@latest info

Порядок важен: init проверяет и Tailwind, и алиас. Если запустить его раньше настройки, CLI остановится с понятным сообщением — это нормально, просто выполните шаги и повторите.

Решение задания 2

ИзменениеПоследствие
baseColor: "slate"нейтральные цвета темы станут холоднее; влияет только на новые генерации токенов, уже записанные значения останутся
aliases.ui: "@/ui"следующие компоненты попадут в src/ui; ранее добавленные останутся на месте, и импорты разъедутся
tsx: falseCLI будет создавать .jsx-файлы без типов
Вывод: components.json — не «настройки внешнего вида», а запись договорённостей о структуре проекта. Менять их после того, как компоненты добавлены, нужно осознанно.

Решение задания 3

📄 src/components/login-form.tsx
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"

export function LoginForm() {
  return (
    <Card className="w-full max-w-sm">
      <CardHeader>
        <CardTitle>Вход</CardTitle>
        <CardDescription>Используйте рабочую почту</CardDescription>
      </CardHeader>
      <CardContent className="grid gap-3">
        <div className="grid gap-1.5">
          <Label htmlFor="email">Почта</Label>
          <Input id="email" type="email" placeholder="name@example.com" required />
        </div>
        <div className="grid gap-1.5">
          <Label htmlFor="password">Пароль</Label>
          <Input id="password" type="password" required />
        </div>
      </CardContent>
      <CardFooter className="justify-between">
        <Button variant="link" className="px-0">Забыли пароль?</Button>
        <Button type="submit">Войти</Button>
      </CardFooter>
    </Card>
  )
}
Вход
Используйте рабочую почту
<div class="w-full max-w-sm overflow-hidden rounded-xl bg-card py-4 text-sm text-card-foreground ring-1 ring-foreground/10">
  <div class="grid auto-rows-min gap-1 px-4">
    <div class="text-base leading-snug font-medium">Вход</div>
    <div class="text-sm text-muted-foreground">Используйте рабочую почту</div>
  </div>
  <div class="grid gap-3 px-4 pt-3">
    <div class="grid gap-1.5">
      <label class="text-sm leading-none font-medium" for="sol-mail">Почта</label>
      <input id="sol-mail" type="email" placeholder="name@example.com"
             class="h-8 w-full rounded-lg border border-input bg-transparent px-2.5 py-1 text-sm outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50">
    </div>
    <div class="grid gap-1.5">
      <label class="text-sm leading-none font-medium" for="sol-pass">Пароль</label>
      <input id="sol-pass" type="password" value="secret"
             class="h-8 w-full rounded-lg border border-input bg-transparent px-2.5 py-1 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50">
    </div>
  </div>
  <div class="mt-4 flex items-center justify-between rounded-b-xl border-t bg-muted/50 p-4">
    <button class="text-sm font-medium text-primary underline-offset-4 hover:underline">Забыли пароль?</button>
    <button class="inline-flex h-8 items-center justify-center rounded-lg bg-primary px-2.5 text-sm font-medium text-primary-foreground hover:bg-primary/80">Войти</button>
  </div>
</div>

Почему так: подписи связаны с полями через htmlFor и id, кнопка отправки имеет type="submit", а «Забыли пароль?» оформлена вариантом link — она выглядит ссылкой, но остаётся кнопкой.