Урок 17. Решения
⚡ Кратко
Готовые команды, разбор полей конфигурации и собранная форма входа.
Решение задания 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: false | CLI будет создавать .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 — она выглядит ссылкой, но остаётся кнопкой.