feat: initialize project with Vite + React + TypeScript + Tailwind CSS
- Set up Vite 7 + React 19 + TypeScript 5 + Tailwind CSS 4 - Configure path aliases (@/ -> src/) - Create Layout with Header and Footer components - Create HomePage with hero section and feature highlights - Set up cursor rules for agent-driven development - Create PLAN.md and LESSONS.md for progress tracking Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
28
.cursor/rules/agent-workflow.mdc
Normal file
28
.cursor/rules/agent-workflow.mdc
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
description: Agent workflow rules for 100% agent-driven development
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Agent Workflow
|
||||||
|
|
||||||
|
## Before Making Changes
|
||||||
|
1. Read PLAN.md to understand current phase and priorities
|
||||||
|
2. Check LESSONS.md for relevant past decisions
|
||||||
|
3. Read existing code before editing — never guess at file contents
|
||||||
|
|
||||||
|
## After Making Changes
|
||||||
|
1. Run `npm run build` to verify no errors
|
||||||
|
2. Update PLAN.md checkboxes when a feature is complete
|
||||||
|
3. Add important learnings to LESSONS.md
|
||||||
|
4. Commit with conventional message format
|
||||||
|
|
||||||
|
## When Stuck
|
||||||
|
- Check LESSONS.md for similar past issues
|
||||||
|
- Prefer simple solutions over clever ones
|
||||||
|
- If a decision has trade-offs, document the reasoning in LESSONS.md
|
||||||
|
|
||||||
|
## Code Quality
|
||||||
|
- Fix all TypeScript errors before committing
|
||||||
|
- Fix all linter warnings before committing
|
||||||
|
- No `console.log` in committed code (use only for debugging)
|
||||||
|
- Remove unused imports and variables
|
||||||
30
.cursor/rules/project-overview.mdc
Normal file
30
.cursor/rules/project-overview.mdc
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
description: Project overview and conventions for the Diet Reference App
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Diet & Healthy Weight Reference App
|
||||||
|
|
||||||
|
## Project
|
||||||
|
Static SPA — справочник методик похудения и здорового веса. Vite 6 + React 19 + TypeScript 5 + Tailwind CSS 4 + React Router 7.
|
||||||
|
|
||||||
|
## Key Conventions
|
||||||
|
- All code in TypeScript strict mode — no `any`, no `as` casts without justification
|
||||||
|
- Functional components only, prefer named exports
|
||||||
|
- Custom hooks for reusable logic (prefix `use`)
|
||||||
|
- Data lives in `src/data/` as typed TS modules
|
||||||
|
- All user state (theme, bookmarks) via localStorage through custom hooks
|
||||||
|
- Semantic HTML + ARIA attributes for accessibility
|
||||||
|
- Russian language for UI content, English for code (variable names, comments)
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
- `src/components/` — reusable UI components (Button, Card, Badge, etc.)
|
||||||
|
- `src/pages/` — route-level page components
|
||||||
|
- `src/data/` — static content data (methods, categories)
|
||||||
|
- `src/hooks/` — custom React hooks
|
||||||
|
- `src/utils/` — pure utility functions
|
||||||
|
- `src/types/` — shared TypeScript types and interfaces
|
||||||
|
|
||||||
|
## Commit Style
|
||||||
|
- Atomic commits per feature
|
||||||
|
- Format: `feat: short description` / `fix:` / `refactor:` / `style:` / `docs:`
|
||||||
31
.cursor/rules/tailwind-styling.mdc
Normal file
31
.cursor/rules/tailwind-styling.mdc
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
description: Tailwind CSS styling conventions
|
||||||
|
globs: "**/*.tsx"
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# Tailwind CSS Styling
|
||||||
|
|
||||||
|
## Approach
|
||||||
|
- Utility-first: use Tailwind classes directly in JSX
|
||||||
|
- Use `clsx` for conditional classes
|
||||||
|
- Extract repeated patterns into components, not CSS classes
|
||||||
|
- Dark mode via `dark:` variant (class strategy)
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
- Mobile-first: default styles for mobile, `sm:` / `md:` / `lg:` for larger
|
||||||
|
- Use `container mx-auto px-4` for page-level wrappers
|
||||||
|
- Flexbox and Grid via Tailwind utilities
|
||||||
|
|
||||||
|
## Colors
|
||||||
|
- Use semantic color names from theme (primary, secondary, accent)
|
||||||
|
- Never hardcode hex colors in className
|
||||||
|
|
||||||
|
## Example
|
||||||
|
```tsx
|
||||||
|
<article className={clsx(
|
||||||
|
"rounded-2xl border p-6 transition-shadow hover:shadow-lg",
|
||||||
|
"bg-white dark:bg-gray-800",
|
||||||
|
isBookmarked && "ring-2 ring-primary-500"
|
||||||
|
)}>
|
||||||
|
```
|
||||||
39
.cursor/rules/typescript-react.mdc
Normal file
39
.cursor/rules/typescript-react.mdc
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
description: TypeScript and React patterns for the project
|
||||||
|
globs: "**/*.{ts,tsx}"
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# TypeScript & React Standards
|
||||||
|
|
||||||
|
## Components
|
||||||
|
- Functional components with explicit return types
|
||||||
|
- Props as `interface` (not `type`), suffix with `Props`
|
||||||
|
- Destructure props in function signature
|
||||||
|
- Use `React.FC` is NOT allowed — use plain functions
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// GOOD
|
||||||
|
interface MethodCardProps {
|
||||||
|
method: DietMethod;
|
||||||
|
onBookmark?: (id: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MethodCard({ method, onBookmark }: MethodCardProps) {
|
||||||
|
return <article>...</article>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hooks
|
||||||
|
- Custom hooks return objects (not arrays) for >2 values
|
||||||
|
- Always handle loading/error states
|
||||||
|
- Prefix with `use`
|
||||||
|
|
||||||
|
## Types
|
||||||
|
- Prefer `interface` for object shapes, `type` for unions/intersections
|
||||||
|
- No enums — use `as const` objects + type inference
|
||||||
|
- Export shared types from `src/types/`
|
||||||
|
|
||||||
|
## Imports
|
||||||
|
- Absolute imports from `@/` (mapped to `src/`)
|
||||||
|
- Group: react → third-party → @/ imports → relative → types
|
||||||
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Build
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
16
LESSONS.md
Normal file
16
LESSONS.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Lessons Learned — Опыт разработки
|
||||||
|
|
||||||
|
Этот файл фиксирует важные уроки, решения и инсайты, полученные в ходе разработки.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2026-02-11 — Старт проекта
|
||||||
|
|
||||||
|
### Выбор стека
|
||||||
|
- **Vite + React + TS** выбран вместо Astro/Next.js: проект интерактивный (поиск, фильтры, BMI калькулятор), Astro избыточен для SPA, Next.js избыточен без SSR.
|
||||||
|
- **Tailwind CSS 4** — utility-first, быстрая стилизация, отличная поддержка dark mode.
|
||||||
|
- **React Router 7** — client-side routing для SPA, нет нужды в серверном роутинге.
|
||||||
|
|
||||||
|
### Решения по архитектуре
|
||||||
|
- Данные хранятся как TS-модули (не JSON) — это даёт типизацию на уровне данных.
|
||||||
|
- localStorage для пользовательских настроек (тема, закладки) — достаточно для статического приложения.
|
||||||
71
PLAN.md
Normal file
71
PLAN.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
# Diet & Healthy Weight Reference App — План разработки
|
||||||
|
|
||||||
|
## Описание проекта
|
||||||
|
|
||||||
|
Статическое веб-приложение — справочник методик похудения и поддержания здорового веса.
|
||||||
|
Полностью serverless, деплой на любой статик-хостинг (Vercel, Netlify, GitHub Pages).
|
||||||
|
|
||||||
|
## Стек технологий
|
||||||
|
|
||||||
|
- **Runtime**: Vite 6 + React 19 + TypeScript 5
|
||||||
|
- **Стили**: Tailwind CSS 4 + clsx
|
||||||
|
- **Роутинг**: React Router 7 (client-side)
|
||||||
|
- **Данные**: Статические JSON/TS модули (без API)
|
||||||
|
- **Хранение**: localStorage для закладок / настроек
|
||||||
|
- **Сборка**: Vite → статический бандл
|
||||||
|
|
||||||
|
## Архитектура
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
components/ — переиспользуемые UI-компоненты
|
||||||
|
pages/ — страницы приложения
|
||||||
|
data/ — контент: методики, категории
|
||||||
|
hooks/ — кастомные React-хуки
|
||||||
|
utils/ — утилиты (расчёт BMI и др.)
|
||||||
|
types/ — TypeScript типы
|
||||||
|
assets/ — изображения, иконки
|
||||||
|
```
|
||||||
|
|
||||||
|
## Фичи (в порядке приоритета)
|
||||||
|
|
||||||
|
### Phase 1 — Фундамент
|
||||||
|
- [x] Инициализация проекта (Vite + React + TS + Tailwind)
|
||||||
|
- [ ] Базовая структура приложения и роутинг
|
||||||
|
- [ ] Layout: header, footer, навигация
|
||||||
|
- [ ] Главная страница с обзором
|
||||||
|
|
||||||
|
### Phase 2 — Контент
|
||||||
|
- [ ] Модель данных для методик
|
||||||
|
- [ ] Данные: минимум 15 методик по категориям
|
||||||
|
- [ ] Страница списка методик с фильтрами
|
||||||
|
- [ ] Страница детальной информации о методике
|
||||||
|
|
||||||
|
### Phase 3 — Интерактивность
|
||||||
|
- [ ] Поиск по методикам (instant search)
|
||||||
|
- [ ] Фильтрация по категориям, сложности, эффективности
|
||||||
|
- [ ] BMI-калькулятор
|
||||||
|
- [ ] Закладки (favorites) через localStorage
|
||||||
|
- [ ] Сравнение методик (до 3 шт)
|
||||||
|
|
||||||
|
### Phase 4 — Полировка
|
||||||
|
- [ ] Тёмная / светлая тема
|
||||||
|
- [ ] Адаптивный дизайн (mobile-first)
|
||||||
|
- [ ] Анимации переходов
|
||||||
|
- [ ] SEO мета-теги
|
||||||
|
- [ ] Деплой на GitHub Pages / Vercel
|
||||||
|
|
||||||
|
## Прогресс
|
||||||
|
|
||||||
|
| Дата | Что сделано | Коммит |
|
||||||
|
|------|-------------|--------|
|
||||||
|
| 2026-02-11 | Создан план, cursor rules, инициализация проекта | — |
|
||||||
|
|
||||||
|
## Принципы разработки
|
||||||
|
|
||||||
|
1. **Коммит на каждую фичу** — атомарные, понятные коммиты
|
||||||
|
2. **100% агентная разработка** — cursor rules покрывают все паттерны
|
||||||
|
3. **TypeScript strict mode** — никаких `any`
|
||||||
|
4. **Переиспользуемые компоненты** — DRY
|
||||||
|
5. **Доступность (a11y)** — семантический HTML, ARIA
|
||||||
|
6. **Производительность** — lazy loading, минимум бандла
|
||||||
14
index.html
Normal file
14
index.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta name="description" content="Справочник методик похудения и поддержания здорового веса" />
|
||||||
|
<title>HealthyWeight — Справочник методик</title>
|
||||||
|
</head>
|
||||||
|
<body class="min-h-screen bg-gray-50 text-gray-900 antialiased dark:bg-gray-950 dark:text-gray-100">
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
2510
package-lock.json
generated
Normal file
2510
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
package.json
Normal file
27
package.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "diet",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc -b && vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"react": "^19.2.4",
|
||||||
|
"react-dom": "^19.2.4",
|
||||||
|
"react-router-dom": "^7.13.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@tailwindcss/vite": "^4.1.18",
|
||||||
|
"@types/node": "^25.2.3",
|
||||||
|
"@types/react": "^19.2.13",
|
||||||
|
"@types/react-dom": "^19.2.3",
|
||||||
|
"@vitejs/plugin-react": "^5.1.4",
|
||||||
|
"tailwindcss": "^4.1.18",
|
||||||
|
"typescript": "^5.9.3",
|
||||||
|
"vite": "^7.3.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
public/vite.svg
Normal file
1
public/vite.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y=".9em" font-size="90"><?</text></svg>
|
||||||
|
After Width: | Height: | Size: 108 B |
13
src/App.tsx
Normal file
13
src/App.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { Routes, Route } from "react-router-dom";
|
||||||
|
import { Layout } from "@/components/Layout";
|
||||||
|
import { HomePage } from "@/pages/HomePage";
|
||||||
|
|
||||||
|
export function App() {
|
||||||
|
return (
|
||||||
|
<Routes>
|
||||||
|
<Route element={<Layout />}>
|
||||||
|
<Route index element={<HomePage />} />
|
||||||
|
</Route>
|
||||||
|
</Routes>
|
||||||
|
);
|
||||||
|
}
|
||||||
14
src/components/Footer.tsx
Normal file
14
src/components/Footer.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
export function Footer() {
|
||||||
|
return (
|
||||||
|
<footer className="border-t border-gray-200 bg-white py-8 dark:border-gray-800 dark:bg-gray-950">
|
||||||
|
<div className="container mx-auto px-4 text-center text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
<p>
|
||||||
|
HealthyWeight — справочник методик похудения и здорового веса.
|
||||||
|
</p>
|
||||||
|
<p className="mt-1">
|
||||||
|
Информация носит ознакомительный характер. Проконсультируйтесь с врачом.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
55
src/components/Header.tsx
Normal file
55
src/components/Header.tsx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { Link, NavLink } from "react-router-dom";
|
||||||
|
import { clsx } from "clsx";
|
||||||
|
|
||||||
|
const navLinks = [
|
||||||
|
{ to: "/", label: "Главная" },
|
||||||
|
{ to: "/methods", label: "Методики" },
|
||||||
|
{ to: "/calculator", label: "Калькулятор BMI" },
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export function Header() {
|
||||||
|
return (
|
||||||
|
<header className="sticky top-0 z-50 border-b border-gray-200 bg-white/80 backdrop-blur-lg dark:border-gray-800 dark:bg-gray-950/80">
|
||||||
|
<div className="container mx-auto flex items-center justify-between px-4 py-4">
|
||||||
|
<Link to="/" className="flex items-center gap-2 text-xl font-bold text-primary-600">
|
||||||
|
<span aria-hidden="true" className="text-2xl">🌿</span>
|
||||||
|
HealthyWeight
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<nav className="hidden items-center gap-1 md:flex">
|
||||||
|
{navLinks.map(({ to, label }) => (
|
||||||
|
<NavLink
|
||||||
|
key={to}
|
||||||
|
to={to}
|
||||||
|
className={({ isActive }) =>
|
||||||
|
clsx(
|
||||||
|
"rounded-lg px-4 py-2 text-sm font-medium transition-colors",
|
||||||
|
isActive
|
||||||
|
? "bg-primary-50 text-primary-700 dark:bg-primary-900/30 dark:text-primary-300"
|
||||||
|
: "text-gray-600 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-100",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</NavLink>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<MobileMenuButton />
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MobileMenuButton() {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className="rounded-lg p-2 text-gray-600 hover:bg-gray-100 md:hidden dark:text-gray-400 dark:hover:bg-gray-800"
|
||||||
|
aria-label="Открыть меню"
|
||||||
|
>
|
||||||
|
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
15
src/components/Layout.tsx
Normal file
15
src/components/Layout.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { Outlet } from "react-router-dom";
|
||||||
|
import { Header } from "@/components/Header";
|
||||||
|
import { Footer } from "@/components/Footer";
|
||||||
|
|
||||||
|
export function Layout() {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen flex-col">
|
||||||
|
<Header />
|
||||||
|
<main className="flex-1">
|
||||||
|
<Outlet />
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
31
src/index.css
Normal file
31
src/index.css
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--color-primary-50: #f0fdf4;
|
||||||
|
--color-primary-100: #dcfce7;
|
||||||
|
--color-primary-200: #bbf7d0;
|
||||||
|
--color-primary-300: #86efac;
|
||||||
|
--color-primary-400: #4ade80;
|
||||||
|
--color-primary-500: #22c55e;
|
||||||
|
--color-primary-600: #16a34a;
|
||||||
|
--color-primary-700: #15803d;
|
||||||
|
--color-primary-800: #166534;
|
||||||
|
--color-primary-900: #14532d;
|
||||||
|
|
||||||
|
--color-accent-50: #fefce8;
|
||||||
|
--color-accent-100: #fef9c3;
|
||||||
|
--color-accent-200: #fef08a;
|
||||||
|
--color-accent-300: #fde047;
|
||||||
|
--color-accent-400: #facc15;
|
||||||
|
--color-accent-500: #eab308;
|
||||||
|
--color-accent-600: #ca8a04;
|
||||||
|
--color-accent-700: #a16207;
|
||||||
|
--color-accent-800: #854d0e;
|
||||||
|
--color-accent-900: #713f12;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark mode is managed via class on <html> */
|
||||||
13
src/main.tsx
Normal file
13
src/main.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { StrictMode } from "react";
|
||||||
|
import { createRoot } from "react-dom/client";
|
||||||
|
import { BrowserRouter } from "react-router-dom";
|
||||||
|
import { App } from "@/App";
|
||||||
|
import "@/index.css";
|
||||||
|
|
||||||
|
createRoot(document.getElementById("root")!).render(
|
||||||
|
<StrictMode>
|
||||||
|
<BrowserRouter>
|
||||||
|
<App />
|
||||||
|
</BrowserRouter>
|
||||||
|
</StrictMode>,
|
||||||
|
);
|
||||||
83
src/pages/HomePage.tsx
Normal file
83
src/pages/HomePage.tsx
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
|
const highlights = [
|
||||||
|
{
|
||||||
|
icon: "\u{1F4D6}",
|
||||||
|
title: "Подробные описания",
|
||||||
|
text: "Каждая методика с научным обоснованием, плюсами и минусами",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "\u{1F50D}",
|
||||||
|
title: "Удобный поиск",
|
||||||
|
text: "Быстрый поиск и фильтрация по категориям и параметрам",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "\u{1F4CA}",
|
||||||
|
title: "Калькулятор BMI",
|
||||||
|
text: "Рассчитайте индекс массы тела и получите рекомендации",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "\u{2696}\u{FE0F}",
|
||||||
|
title: "Сравнение методик",
|
||||||
|
text: "Сравните до 3 методик бок о бок для выбора подходящей",
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export function HomePage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Hero Section */}
|
||||||
|
<section className="bg-gradient-to-br from-primary-50 via-white to-accent-50 py-20 dark:from-gray-900 dark:via-gray-950 dark:to-gray-900">
|
||||||
|
<div className="container mx-auto px-4 text-center">
|
||||||
|
<h1 className="text-4xl font-extrabold tracking-tight text-gray-900 sm:text-5xl lg:text-6xl dark:text-white">
|
||||||
|
Методики похудения
|
||||||
|
<span className="block text-primary-600 dark:text-primary-400">и здорового веса</span>
|
||||||
|
</h1>
|
||||||
|
<p className="mx-auto mt-6 max-w-2xl text-lg text-gray-600 dark:text-gray-300">
|
||||||
|
Полный справочник научно обоснованных методик — от диет до физической активности.
|
||||||
|
Найдите подход, который подойдёт именно вам.
|
||||||
|
</p>
|
||||||
|
<div className="mt-10 flex flex-col items-center justify-center gap-4 sm:flex-row">
|
||||||
|
<Link
|
||||||
|
to="/methods"
|
||||||
|
className="rounded-xl bg-primary-600 px-8 py-3.5 text-base font-semibold text-white shadow-lg shadow-primary-500/25 transition hover:bg-primary-700 hover:shadow-xl"
|
||||||
|
>
|
||||||
|
Смотреть методики
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
to="/calculator"
|
||||||
|
className="rounded-xl border-2 border-gray-300 bg-white px-8 py-3.5 text-base font-semibold text-gray-700 transition hover:border-primary-300 hover:text-primary-700 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-200 dark:hover:border-primary-600"
|
||||||
|
>
|
||||||
|
Рассчитать BMI
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Highlights */}
|
||||||
|
<section className="py-20">
|
||||||
|
<div className="container mx-auto px-4">
|
||||||
|
<h2 className="text-center text-3xl font-bold text-gray-900 dark:text-white">
|
||||||
|
Что вас ждёт
|
||||||
|
</h2>
|
||||||
|
<div className="mt-12 grid gap-8 sm:grid-cols-2 lg:grid-cols-4">
|
||||||
|
{highlights.map(({ icon, title, text }) => (
|
||||||
|
<div
|
||||||
|
key={title}
|
||||||
|
className="rounded-2xl border border-gray-200 bg-white p-6 text-center transition-shadow hover:shadow-lg dark:border-gray-800 dark:bg-gray-900"
|
||||||
|
>
|
||||||
|
<div className="text-4xl" aria-hidden="true">{icon}</div>
|
||||||
|
<h3 className="mt-4 text-lg font-semibold text-gray-900 dark:text-white">
|
||||||
|
{title}
|
||||||
|
</h3>
|
||||||
|
<p className="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
{text}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
27
tsconfig.app.json
Normal file
27
tsconfig.app.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
1
tsconfig.app.tsbuildinfo
Normal file
1
tsconfig.app.tsbuildinfo
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"root":["./src/app.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/footer.tsx","./src/components/header.tsx","./src/components/layout.tsx","./src/pages/homepage.tsx"],"version":"5.9.3"}
|
||||||
7
tsconfig.json
Normal file
7
tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{ "path": "./tsconfig.app.json" },
|
||||||
|
{ "path": "./tsconfig.node.json" }
|
||||||
|
]
|
||||||
|
}
|
||||||
20
tsconfig.node.json
Normal file
20
tsconfig.node.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"lib": ["ES2023"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
||||||
1
tsconfig.node.tsbuildinfo
Normal file
1
tsconfig.node.tsbuildinfo
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"root":["./vite.config.ts"],"version":"5.9.3"}
|
||||||
13
vite.config.ts
Normal file
13
vite.config.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { defineConfig } from "vite";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
import tailwindcss from "@tailwindcss/vite";
|
||||||
|
import { fileURLToPath, URL } from "node:url";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react(), tailwindcss()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user