diff --git a/.vitepress/config.ts b/.vitepress/config.ts index 2047ec6..dd3542e 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -1,11 +1,26 @@ import { defineConfig } from 'vitepress'; +const versions = Array.from({ length: 100 }, (_, i) => { + const major = Math.floor(i / 10) + 1; + const minor = i % 10; + return `v${major}.${minor}`; +}).reverse(); + export default defineConfig({ lang: 'ru-RU', title: 'Frontend Style Guide', description: 'Правила и стандарты разработки фронтенд-проектов на React/NextJS и TypeScript', themeConfig: { siteTitle: 'Frontend Style Guide', + nav: [ + { + text: versions[0], + items: versions.map((v) => ({ + text: v, + link: `/${v}/`, + })), + }, + ], sidebar: [ { text: 'Базовые правила', @@ -25,7 +40,8 @@ export default defineConfig({ { text: 'Компоненты', link: '/parts/8-0-components' }, { text: 'Шаблоны и генерация кода', link: '/parts/8-1-templates-generation' }, { text: 'Стили', link: '/parts/9-styles' }, - { text: 'Изображения/спрайты', link: '/parts/10-images-sprites' }, + { text: 'Изображения', link: '/parts/10-images-sprites' }, + { text: 'SVG-спрайты', link: '/parts/17-svg-sprites' }, { text: 'Видео', link: '/parts/11-video' }, { text: 'API', link: '/parts/12-api' }, { text: 'Stores', link: '/parts/13-stores' }, diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 7bc3026..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,38 +0,0 @@ -# Repository Guidelines - -## Назначение репозитория -Этот репозиторий хранит документацию и правила разработки фронтенд‑проектов (React/Next.js, TypeScript) и публикуется через VitePress. Контент пишется на русском языке. Сейчас ведётся рефакторинг документации: цель — краткие, чёткие и не дублирующиеся правила. - -## Project Structure & Module Organization -- `index.md` — главная страница документации. -- `parts/` — разделы документации, один файл на тему. Именование: `N-title.md` в `kebab-case` (например, `3-code-style.md`). -- `.vitepress/config.ts` — конфигурация VitePress и sidebar; добавляя новый раздел, обновляйте список ссылок. -- `RULES.md` — агрегированный документ, собирается из `parts/`. -- `concat-md.js` — скрипт сборки `RULES.md`. -- `OLD_parts/` — архив старой документации, используется только как справочник при переносе идей. - -## Build, Test, and Development Commands -- `npm install` — установка зависимостей. -- `npm run dev` — локальный сервер VitePress (обычно localhost:5173). -- `npm run build` — сборка статического сайта. -- `npm run serve` — предпросмотр собранной статики. -- `npm run docs` — пересборка `RULES.md` из `parts/`. - -## Coding Style & Naming Conventions -- Язык документации — русский. -- Для новых разделов придерживайтесь нумерации и `kebab-case` в именах файлов. -- В примерах кода ориентируйтесь на правила из `RULES.md`: отступ 2 пробела, одинарные кавычки в TS, двойные в JSX, `import type` для типов, избегать `default` экспортов. - -## Принципы рефакторинга документации -- Один смысл — один раздел. Не размазывайте правила по нескольким файлам. -- Если правило уже описано (например, нейминг), не повторяйте его в других разделах — добавляйте недостающее только в профильный файл. -- При переносе из `OLD_parts/` переписывайте кратко и по делу, исключая устаревшее и дубли. -- Новые правила добавляйте только в подходящий раздел; если такого нет — создайте его и обновите sidebar. - -## Testing Guidelines -Тестовая инфраструктура отсутствует. Если добавляете тесты или проверяющие скрипты, добавьте соответствующий `npm`‑скрипт и опишите его в этом документе. - -## Commit & Pull Request Guidelines -- История коммитов содержит короткие однословные сообщения (например, `sync`, `first`) — формального стандарта не видно. -- Для PR: укажите цель изменений, список затронутых разделов и отметьте, обновляли ли `RULES.md` и `sidebar`. -- Если меняется структура документации или навигация, приложите краткий скриншот/описание результата. diff --git a/RULES.md b/RULES.md index f3643f9..d56b4fa 100644 --- a/RULES.md +++ b/RULES.md @@ -44,7 +44,7 @@ # Архитектура -Архитектура построена на FSD (Feature‑Sliced Design) и строгих границах модулей. +Архитектура построена на FSD (`Feature‑Sliced Design`) и строгих границах модулей. Цель — разделить ответственность, упростить сопровождение и контроль зависимостей. ## Принципы @@ -54,16 +54,27 @@ - Открывать наружу только публичный API модулей. - Не допускать циклических зависимостей. -## Слои +## Слои (FSD) -- **app** — инициализация приложения, роутинг, конфигурации, глобальные провайдеры. -- **screens** — экраны и их композиция. +- **app** — инициализация приложения: провайдеры, глобальные стили. В Next.js эта же папка `app/` дополнительно содержит системные файлы роутинга (`layout.tsx`, `page.tsx`). +- **screens** — UI-компоненты страниц. Каждый экран — отдельный компонент, который собирает виджеты и фичи конкретной страницы. Роутинг только использует эти компоненты — он не является частью слоя `screens`. В Next.js файлы `page.tsx` остаются тонкими: импортируют экран и рендерят его. - **layouts** — каркас и шаблоны страниц. - **widgets** — крупные блоки интерфейса, собирающие несколько сценариев. - **features** — отдельные пользовательские действия и сценарии. - **entities** — бизнес-сущности и их модель. - **shared** — переиспользуемая инфраструктура, утилиты и базовые UI‑компоненты. +## Модули (FSD) + +- Модуль — это отдельная папка в слоях `screens`, `layouts`, `widgets`, `features`, `entities`, которая реализует один сценарий/блок. В корне модуля лежит главный файл (`*.screen.tsx`, `*.layout.tsx`, `*.widget.tsx`, `*.feature.tsx`, `*.entity.tsx`) и публичный API (`index.ts`). +- Внутри модуля используются подпапки (по необходимости): + - `ui/` — дочерние UI‑компоненты модуля. + - `model/` — состояние и бизнес‑логика модуля. + - `styles/` — локальные стили модуля. + - `helpers/` — локальные хелперы. + - `lib/` — утилиты модуля. + - `api/` — API‑вызовы модуля. + ## Правила зависимостей - Допустимые импорты идут сверху вниз: `app → screens → layouts → widgets → features → entities → shared`. @@ -167,20 +178,22 @@ const title = 'Привет, ' + name; - В именованных импортах использовать пробелы внутри фигурных скобок. - Типы импортировать через `import type`. -- `default` импорт и экспорт избегать, использовать именованные. +- `default` экспорт избегать, использовать именованные. `default` импорт допустим (например, стили CSS Modules, сторонние библиотеки). - Избегать импорта всего модуля через `*`. **Хорошо** ```ts import { MyComponent } from 'MyComponent'; import type { User } from '../model/types'; +import styles from './styles/button.module.css'; ``` **Плохо** ```ts -// Плохо: default импорт и отсутствие пробелов в именованном импорте. -import MyComponent from 'MyComponent'; +// Плохо: отсутствие пробелов в именованном импорте. import type {User} from '../model/types'; +// Плохо: default экспорт. +export default MyComponent; ``` ## Ранние возвраты (early return) @@ -314,8 +327,12 @@ src/ └── shared/ └── ui/ └── icon/ + ├── styles/ + │ └── icon.module.css + ├── types/ + │ └── icon.interface.ts ├── icon.ui.tsx - └── icon.module.css + └── index.ts ``` **Плохо** @@ -480,7 +497,7 @@ export enum TodoFilter { /** * Параметры кнопки. */ -interface IOwnProps extends HTMLAttributes { +interface ButtonProps extends HTMLAttributes { /** Текст кнопки. */ label: string; /** Обработчик клика по кнопке. */ @@ -490,7 +507,7 @@ interface IOwnProps extends HTMLAttributes { /** * Кнопка с пользовательскими стилями. */ -export const Button:FC = ({ className, label, onClick, ...htmlAttr }) => { +export const Button: FC = ({ className, label, onClick, ...htmlAttr }) => { return (
button @@ -560,40 +577,91 @@ const parse = (value: any) => value; ## Базовая структура проекта -**Хорошо** +Слои FSD не зависят от фреймворка. Различается только содержимое `app/` — в React SPA это конфигурация роутинга, в Next.js — системные файлы фреймворка (`layout.tsx`, `page.tsx`, route-сегменты). + ```text src/ -├── app/ # Инициализация приложения, роутинг, провайдеры -│ ├── config/ # Конфигурации и константы уровня приложения -│ ├── providers/ # Провайдеры и обёртки приложения -│ ├── routing/ # Конфигурация маршрутов -│ └── index.ts # Публичный API слоя -├── screens/ # Экраны приложения -│ └── Profile/ # Экран профиля -│ └── ... # ui/model/index.ts +├── app/ # Инициализация приложения (см. «Слой app/») +├── screens/ # UI-компоненты страниц +│ └── profile/ +│ ├── profile.screen.tsx +│ └── index.ts ├── layouts/ # Общие шаблоны и каркасы страниц -│ └── MainLayout/ # Основной layout -│ └── ... # ui/index.ts +│ └── main-layout/ +│ ├── main-layout.layout.tsx +│ └── index.ts ├── widgets/ # Крупные блоки интерфейса -│ └── Header/ # Виджет шапки -│ └── ... # ui/index.ts +│ └── header/ +│ ├── header.widget.tsx +│ └── index.ts ├── features/ # Пользовательские сценарии -│ └── auth-by-email/ # Авторизация по email -│ └── ... # ui/model/api/index.ts +│ └── auth-by-email/ +│ ├── ui/ +│ │ └── login-form.ui.tsx +│ ├── model/ +│ │ └── auth-by-email.store.ts +│ ├── auth-by-email.feature.tsx +│ └── index.ts ├── entities/ # Бизнес-сущности -│ └── user/ # Сущность пользователя -│ └── ... # ui/model/api/lib/index.ts +│ └── user/ +│ ├── model/ +│ │ └── user.store.ts +│ ├── user.entity.tsx +│ └── index.ts └── shared/ # Общие ресурсы проекта - ├── ui/ # Базовые UI-компоненты + ├── ui/ # Повторно используемые UI-элементы + │ └── icon/ + │ ├── styles/ + │ │ └── icon.module.css + │ ├── types/ + │ │ └── icon.interface.ts + │ ├── icon.ui.tsx + │ └── index.ts ├── lib/ # Утилиты и хелперы ├── services/ # Общие сервисы и клиенты - ├── config/ # Общие конфигурации - ├── styles/ # Глобальные стили и токены + ├── config/ # Общие конфигурации и константы └── assets/ # Ресурсы - ├── images/ # Изображения - ├── icons/ # Иконки - ├── fonts/ # Шрифты - └── video/ # Видео + ├── images/ + ├── icons/ + ├── fonts/ + └── video/ +``` + +## Слой app/ + +Общее для обоих вариантов: провайдеры и глобальные стили. Различается только способ организации роутинга. + +### React SPA + +```text +src/app/ +├── providers/ # Провайдеры и обёртки приложения +├── routing/ # Конфигурация маршрутов (React Router) +├── styles/ # Глобальные стили, CSS-переменные, custom media +└── index.ts # Entry point приложения +``` + +### Next.js (App Router) + +```text +src/app/ +├── providers/ # Провайдеры и обёртки приложения +├── styles/ # Глобальные стили, CSS-переменные, custom media +├── layout.tsx # Корневой layout (подключает providers, styles) +├── page.tsx # Главная страница +└── profile/ + └── page.tsx # Рендерит ProfileScreen +``` + +В Next.js файлы `page.tsx` остаются тонкими — они только импортируют экран из `screens/` и рендерят его. Вся логика, зависимости и стили страницы живут в компоненте экрана, а не в `app/`. + +```tsx +// src/app/profile/page.tsx +import { ProfileScreen } from '@/screens/profile'; + +export default function ProfilePage() { + return ; +} ``` **Плохо** @@ -608,8 +676,12 @@ src/ ## Правила организации +- В слоях FSD (`features`, `entities`, `widgets`, `screens` и т.д.) `ui/` используется только для дочерних элементов, которые относятся к модулю и не экспортируются отдельно. Главные компоненты, которые составляют сам слой, держат собственные `*.feature.tsx`, `*.widget.tsx` и т. п., а `ui/` служит для вспомогательных мелких компонентов. +- В `shared/ui/` хранятся базовые UI-элементы/компоненты, которыми пользуются сразу несколько модулей; в этом случае они экспортируются наружу и не считаются «дочерними» для слоя. +- Если модуль строится вокруг «главного» компонента (`*.feature.tsx`, `*.screen.tsx`, `*.widget.tsx`), помещайте его в корень модуля и экспортируйте через `index.ts`. Проверяйте, что `ui/` не используется просто как «контейнер» слоя. + - Каждый слой и модуль хранится в собственной папке. -- Внутренние реализации разделяются на `ui`, `model`, `lib`, `api`. +- Внутренние реализации разделяются на `ui/`, `model/`, `styles/`, `helpers/`, `lib/`, `api/`. - Публичный API модуля объявляется в `index.ts`. - Внутренние файлы не импортируются напрямую извне. - Не смешивать ответственность разных слоёв в одном модуле. @@ -658,7 +730,7 @@ export interface ContainerProps extends HTMLAttributes {} ```tsx import type { FC } from 'react' -import { cl } from 'clsx' +import cl from 'clsx' import type { ContainerProps } from './types/container.interface' import styles from './styles/container.module.scss' @@ -691,7 +763,7 @@ export const Container: FC = ({ className, ...htmlAttr }) => { export { Container } from './container.ui' ``` -## Генерация +## Шаблоны и генерация кода Генерация нужна, чтобы быстро создавать компоненты с единым каркасом и не допускать расхождений в структуре. Это даёт одинаковые папки и имена файлов, уменьшает ручные ошибки и ускоряет старт работы. @@ -742,15 +814,18 @@ npx @gromlab/create component button ```text .templates/ # корневая папка всех шаблонов ├── component/ # шаблон компонента -│ └── {{{name.pascalCase}}}/ -│ ├── index.ts -│ ├── {{{name.pascalCase}}}.tsx -│ └── {{{name.pascalCase}}}.module.css +│ └── {{name.kebabCase}}/ +│ ├── styles/ +│ │ └── {{name.kebabCase}}.module.css +│ ├── types/ +│ │ └── {{name.kebabCase}}.interface.ts +│ ├── {{name.kebabCase}}.ui.tsx +│ └── index.ts └── store/ # шаблон Zustand стора - └── {{{name.camelCase}}}Store/ - ├── index.ts - ├── {{{name.camelCase}}}Store.ts - └── {{{name.camelCase}}}Store.type.ts + └── {{name.kebabCase}}/ + ├── {{name.kebabCase}}.store.ts + ├── {{name.kebabCase}}.type.ts + └── index.ts ``` ## Синтаксис @@ -793,18 +868,30 @@ export const {{name.pascalCase}} = () => { ```ts // .templates/component/index.ts -export * from './{{name.pascalCase}}' +export { {{name.pascalCase}} } from './{{name.kebabCase}}.ui' +``` + +```ts +// .templates/component/types/{{name.kebabCase}}.interface.ts +import type { HTMLAttributes } from 'react' + +/** + * Параметры {{name.pascalCase}}. + */ +export interface {{name.pascalCase}}Props extends HTMLAttributes {} ``` ```tsx -// .templates/component/{{name.pascalCase}}.tsx -import { FC, HTMLAttributes } from "react"; -import styles from './{{name.kebabCase}}.module.css' +// .templates/component/{{name.kebabCase}}.ui.tsx +import type { FC } from 'react' import cl from 'clsx' +import type { {{name.pascalCase}}Props } from './types/{{name.kebabCase}}.interface' +import styles from './styles/{{name.kebabCase}}.module.css' -interface IOwnProps extends HTMLAttributes {} - -export const {{name.pascalCase}}:FC = ({className, ...htmlAttr}) => { +/** + * {{name.pascalCase}}. + */ +export const {{name.pascalCase}}: FC<{{name.pascalCase}}Props> = ({ className, ...htmlAttr }) => { return (
{{name.kebabCase}} @@ -814,15 +901,280 @@ export const {{name.pascalCase}}:FC = ({className, ...htmlAttr}) => { ``` ```css -/* .templates/component/{{name.kebabCase}}.module.css */ +/* .templates/component/styles/{{name.kebabCase}}.module.css */ .root { - + } ``` +# Стили + +Раздел описывает правила написания CSS: PostCSS Modules, вложенность, медиа-запросы, переменные, форматирование. + +## Общие правила + +- Только **PostCSS** и **CSS Modules** для стилизации. +- Подход **Mobile First** — стили пишутся от мобильных к десктопу. +- Именование классов — `camelCase` (`.root`, `.buttonNext`, `.itemTitle`). +- Модификаторы — отдельный класс с `_`, применяется через `&._modifier`. + +**Хорошо** +```css +.submitButton { + padding: 8px 16px; + + &._disabled { + opacity: 0.5; + } +} +``` + +**Плохо** +```css +/* Плохо: kebab-case и вложенный элемент вместо отдельного класса. */ +.submit-button { + padding: 8px 16px; + + &__icon { + margin-right: 8px; + } +} +``` + +## Вложенность + +- Вложенность селекторов запрещена. +- Исключения: + - Псевдоклассы: `&:hover`, `&:active`, `&:focus`, `&:disabled` и т.д. + - Псевдоэлементы: `&::before`, `&::after`. + - Медиа-запросы: `@media`. + - Модификаторы: `&._active`, `&._disabled`. +- Каждый вложенный блок отделяется пустой строкой от предыдущих свойств. + +**Хорошо** +```css +.card { + padding: 16px; + background-color: var(--color-bg); + + &:hover { + background-color: var(--color-bg-hover); + } + + &::after { + content: ''; + display: block; + } + + &._highlighted { + border-color: var(--color-primary); + } + + @media (--md) { + padding: 24px; + } +} + +.cardTitle { + font-size: 16px; + + @media (--md) { + font-size: 20px; + } +} +``` + +**Плохо** +```css +/* Плохо: вложенность селекторов, нет пустых строк между блоками. */ +.card { + padding: 16px; + .cardTitle { + font-size: 16px; + } + &:hover { + background-color: var(--color-bg-hover); + } +} +``` + +## Медиа-запросы + +- Только **Custom Media Queries**: `@media (--md) {}`. +- Запрещены произвольные breakpoints: `@media (min-width: 768px)`. +- `@media` пишется только **внутри** селектора. +- Запрещено писать `@media` на верхнем уровне с селекторами внутри. + +**Хорошо** +```css +.sidebar { + display: none; + + @media (--md) { + display: block; + } +} + +.sidebarTitle { + font-size: 14px; + + @media (--md) { + font-size: 18px; + } +} +``` + +**Плохо** +```css +/* Плохо: @media на верхнем уровне с селекторами внутри. */ +@media (--md) { + .sidebar { + display: block; + } + + .sidebarTitle { + font-size: 18px; + } +} + +/* Плохо: произвольный breakpoint вместо custom media. */ +.sidebar { + @media (min-width: 992px) { + display: block; + } +} +``` + +## CSS-переменные + +- Цвета (`--color-*`), отступы (`--space-*`), скругления (`--radius-*`) определяются в `app/styles/variables.css` через `:root`. +- Файл переменных подключается один раз в корневом layout/entry point — после этого переменные доступны глобально через каскад. +- Не дублировать магические значения в компонентах. + +**Хорошо** +```css +/* app/styles/variables.css */ +:root { + --color-primary: #3b82f6; + --color-bg: #ffffff; + --color-bg-hover: #f5f5f5; + --space-1: 4px; + --space-2: 8px; + --space-3: 12px; + --radius-1: 4px; + --radius-2: 8px; +} +``` + +```css +/* компонент */ +.card { + padding: var(--space-3); + border-radius: var(--radius-2); + background-color: var(--color-bg); +} +``` + +**Плохо** +```css +/* Плохо: магические значения вместо переменных. */ +.card { + padding: 12px; + border-radius: 8px; + background-color: #ffffff; +} +``` + +## Custom Media + +- Breakpoints определяются через Custom Media Queries в `app/styles/media.css`. +- Custom media подключаются глобально через конфиг PostCSS (плагин `postcss-custom-media`) — не импортировать в файлы стилей. + +```css +/* app/styles/media.css */ +@custom-media --sm (min-width: 36em); +@custom-media --md (min-width: 62em); +@custom-media --lg (min-width: 82em); +``` + +## Импорт стилей + +- Стили компонента импортируются только внутри своего компонента. +- Запрещено импортировать стили одного компонента в другой. +- Custom media не импортируются в файлы стилей — они подключаются глобально через конфиг PostCSS. + +## Форматирование + +- Пустая строка между селекторами верхнего уровня. +- Пустая строка перед каждым вложенным блоком (медиа, псевдокласс, модификатор). + +**Хорошо** +```css +.userBar { + display: none; + color: var(--color-text); + + @media (--md) { + display: flex; + } +} + +.userBarButton { + background-color: var(--color-bg); + + &:hover { + background-color: var(--color-bg-hover); + } + + &._active { + background-color: var(--color-primary); + } +} +``` + +**Плохо** +```css +/* Плохо: нет пустых строк между селекторами и вложенными блоками. */ +.userBar { + display: none; + color: var(--color-text); + @media (--md) { + display: flex; + } +} +.userBarButton { + background-color: var(--color-bg); + &:hover { + background-color: var(--color-bg-hover); + } + &._active { + background-color: var(--color-primary); + } +} +``` + +## Единицы измерения + +- `px` — основная единица измерения. +- Остальные (`em`, `rem`, `%`, `vh`/`vw`) — допускаются по необходимости дизайна. + +## Порядок CSS-свойств + +В стилях рекомендуется придерживаться логического порядка свойств: + +1. Позиционирование (`position`, `top`, `left`, `z-index`). +2. Блочная модель (`display`, `width`, `height`, `margin`, `padding`). +3. Оформление (`background`, `border`, `box-shadow`, `border-radius`). +4. Текст (`font`, `color`, `text-align`, `line-height`). +5. Прочее (`transition`, `animation`, `opacity`, `cursor`). + +## Комментарии + +- Желательно не писать комментарии в CSS. +- Исключение — нетривиальные хаки и обходные решения, к которым стоит оставить пояснение. @@ -851,3 +1203,8 @@ export const {{name.pascalCase}}:FC = ({className, ...htmlAttr}) => { + + + + +# SVG-спрайты diff --git a/parts/17-svg-sprites.md b/parts/17-svg-sprites.md new file mode 100644 index 0000000..2c1a175 --- /dev/null +++ b/parts/17-svg-sprites.md @@ -0,0 +1,5 @@ +--- +title: SVG-спрайты +--- + +# SVG-спрайты diff --git a/parts/2-architecture.md b/parts/2-architecture.md index 90b199a..6750845 100644 --- a/parts/2-architecture.md +++ b/parts/2-architecture.md @@ -16,8 +16,8 @@ title: Архитектура ## Слои (FSD) -- **app** — инициализация приложения, роутинг, конфигурации, глобальные провайдеры. -- **screens** — экраны и их композиция. +- **app** — инициализация приложения: провайдеры, глобальные стили. В Next.js эта же папка `app/` дополнительно содержит системные файлы роутинга (`layout.tsx`, `page.tsx`). +- **screens** — UI-компоненты страниц. Каждый экран — отдельный компонент, который собирает виджеты и фичи конкретной страницы. Роутинг только использует эти компоненты — он не является частью слоя `screens`. В Next.js файлы `page.tsx` остаются тонкими: импортируют экран и рендерят его. - **layouts** — каркас и шаблоны страниц. - **widgets** — крупные блоки интерфейса, собирающие несколько сценариев. - **features** — отдельные пользовательские действия и сценарии. diff --git a/parts/7-project-structure.md b/parts/7-project-structure.md index 32c7df4..8c90e7e 100644 --- a/parts/7-project-structure.md +++ b/parts/7-project-structure.md @@ -8,29 +8,25 @@ title: Структура проекта ## Базовая структура проекта -**Хорошо** +Слои FSD не зависят от фреймворка. Различается только содержимое `app/` — в React SPA это конфигурация роутинга, в Next.js — системные файлы фреймворка (`layout.tsx`, `page.tsx`, route-сегменты). + ```text src/ -├── app/ # Инициализация приложения, роутинг, провайдеры -│ ├── config/ # Конфигурации и константы уровня приложения -│ ├── providers/ # Провайдеры и обёртки приложения -│ ├── routing/ # Конфигурация маршрутов -│ ├── styles/ # Глобальные стили, CSS-переменные, custom media -│ └── index.ts # Публичный API слоя -├── screens/ # Экраны приложения -│ └── profile/ # Экран профиля +├── app/ # Инициализация приложения (см. «Слой app/») +├── screens/ # UI-компоненты страниц +│ └── profile/ │ ├── profile.screen.tsx │ └── index.ts ├── layouts/ # Общие шаблоны и каркасы страниц -│ └── main-layout/ # Основной layout +│ └── main-layout/ │ ├── main-layout.layout.tsx │ └── index.ts ├── widgets/ # Крупные блоки интерфейса -│ └── header/ # Виджет шапки +│ └── header/ │ ├── header.widget.tsx │ └── index.ts ├── features/ # Пользовательские сценарии -│ └── auth-by-email/ # Авторизация по email +│ └── auth-by-email/ │ ├── ui/ │ │ └── login-form.ui.tsx │ ├── model/ @@ -38,7 +34,7 @@ src/ │ ├── auth-by-email.feature.tsx │ └── index.ts ├── entities/ # Бизнес-сущности -│ └── user/ # Сущность пользователя +│ └── user/ │ ├── model/ │ │ └── user.store.ts │ ├── user.entity.tsx @@ -54,12 +50,49 @@ src/ │ └── index.ts ├── lib/ # Утилиты и хелперы ├── services/ # Общие сервисы и клиенты - ├── config/ # Общие конфигурации + ├── config/ # Общие конфигурации и константы └── assets/ # Ресурсы - ├── images/ # Изображения - ├── icons/ # Иконки - ├── fonts/ # Шрифты - └── video/ # Видео + ├── images/ + ├── icons/ + ├── fonts/ + └── video/ +``` + +## Слой app/ + +Общее для обоих вариантов: провайдеры и глобальные стили. Различается только способ организации роутинга. + +### React SPA + +```text +src/app/ +├── providers/ # Провайдеры и обёртки приложения +├── routing/ # Конфигурация маршрутов (React Router) +├── styles/ # Глобальные стили, CSS-переменные, custom media +└── index.ts # Entry point приложения +``` + +### Next.js (App Router) + +```text +src/app/ +├── providers/ # Провайдеры и обёртки приложения +├── styles/ # Глобальные стили, CSS-переменные, custom media +├── layout.tsx # Корневой layout (подключает providers, styles) +├── page.tsx # Главная страница +└── profile/ + └── page.tsx # Рендерит ProfileScreen +``` + +В Next.js файлы `page.tsx` остаются тонкими — они только импортируют экран из `screens/` и рендерят его. Вся логика, зависимости и стили страницы живут в компоненте экрана, а не в `app/`. + +```tsx +// src/app/profile/page.tsx +import { ProfileScreen } from '@/screens/profile'; + +export default function ProfilePage() { + return ; +} ``` **Плохо**