docs: добавить стайлгайд nextjs-style-guide в репозиторий

- Добавлена документация SLM-архитектуры, базовых правил и прикладных разделов
- Добавлены разделы: стили, SVG-спрайты, шаблоны генерации, PostCSS, REST, Realtime
- Удалены устаревшие файлы (спрайты, скрипты, стили из app/)
This commit is contained in:
2026-04-30 19:32:10 +03:00
parent bf792f6159
commit f2358da397
60 changed files with 5308 additions and 372 deletions

View File

@@ -0,0 +1,201 @@
---
title: Компонент
description: Как создавать React-компоненты внутри SLM-модулей.
---
# Компонент
Как создавать React-компоненты внутри SLM-модулей.
## Назначение
Компонент — минимальная UI-единица проекта. Это один `.tsx` файл без собственной папки, сегментов и публичного API.
Компонент может использовать стили, типы, хуки и другие ресурсы родительского модуля. Наличие связанных файлов в `styles/` или `types/` не превращает компонент в модуль.
## Компонент или модуль
Классификация определяется границей владения:
- `component` — один `.tsx` файл внутри модуля;
- `module` — папка с `index.ts`, сегментами и собственной публичной границей.
```text
user/
├── ui/
│ └── user-avatar.tsx # компонент
├── styles/
│ └── user-avatar.module.css # ресурс родительского модуля
├── types/
│ └── user-avatar.type.ts # ресурс родительского модуля
└── user.tsx # корневой компонент модуля
```
`user-avatar.tsx` остаётся компонентом, потому что у него нет собственной папки, `index.ts` и сегментов.
## Где размещать
Компонент размещается внутри модуля:
- В корне модуля, если это главная UI-сущность модуля.
- В `ui/`, если это вспомогательный компонент модуля.
```text
user/
├── ui/
│ └── user-avatar.tsx
├── styles/
│ ├── user.module.css
│ └── user-avatar.module.css
├── types/
│ ├── user.type.ts
│ └── user-avatar.type.ts
├── user.tsx
└── index.ts
```
`user.tsx` — корневой компонент модуля. `ui/user-avatar.tsx` — вспомогательный компонент этого же модуля.
## Что запрещено
- Заворачивать компонент в папку: `ui/header/header.tsx`.
- Создавать для компонента отдельный `index.ts`.
- Создавать собственные сегменты внутри папки компонента: `ui/header/styles/`, `ui/header/types/`, `ui/header/hooks/` и т.п.
- Декларировать внутри `.tsx` сторы, сервисы, API-клиенты, мапперы или утилиты. Для этого есть сегменты родительского модуля.
- Размещать бизнес-правила прямо в компоненте. Компонент может использовать готовые зависимости модуля, но не определяет их.
- Размещать компонент в `parts/` напрямую. `parts/` содержит только модули.
**Плохо**
```text
user/
└── ui/
└── user-avatar/
├── styles/
│ └── user-avatar.module.css
├── user-avatar.tsx
└── index.ts
```
**Хорошо**
```text
user/
├── ui/
│ └── user-avatar.tsx
├── styles/
│ └── user-avatar.module.css
└── types/
└── user-avatar.type.ts
```
## Стили и типы
Компонент использует ресурсы родительского модуля.
`styles/` и `types/` рядом с корневым компонентом — это сегменты модуля, а не собственные папки `.tsx` файла.
- CSS Module компонента лежит в `styles/` родительского модуля и называется по компоненту: `user-avatar.module.css`.
- Если у компонента есть CSS Module, его корневой класс всегда называется `.root`.
- Типы компонента лежат в `types/` родительского модуля и называются по компоненту: `user-avatar.type.ts`.
- Локальный `type Props` внутри `.tsx` не используется. Типы пропсов всегда выносятся в `types/` родительского модуля.
- Экспорт типа из `types/` не делает его публичным API. Публичным он становится только при реэкспорте из `index.ts` модуля.
Причина `.root`: в DevTools проще находить корневой DOM-узел компонента и отличать его от внутренних элементов.
## Реализация
- Компоненты объявляются через `const`.
- `React.FC` не используется.
- JSDoc-комментарий обязателен для компонента.
- Пропсы деструктурируются в теле компонента.
- `className` объединяется с `styles.root` через `cl()`.
- Побочные эффекты и состояние выносятся в хуки модуля, если перестают быть тривиальными.
- Компонент возвращает JSX и не содержит orchestration-код страницы или бизнес-домена.
`user/types/user-avatar.type.ts`
```ts
import type { ImageProps } from 'next/image'
/**
* Параметры UserAvatar.
*/
export type UserAvatarParams = {}
/** Пропсы базового изображения. */
type RootAttrs = ImageProps
export type UserAvatarProps = RootAttrs & UserAvatarParams
```
`user/ui/user-avatar.tsx`
```tsx
import cl from 'clsx'
import Image from 'next/image'
import type { UserAvatarProps } from '../types/user-avatar.type'
import styles from '../styles/user-avatar.module.css'
/**
* Аватар пользователя.
*
* Используется для:
* - отображения пользователя в карточке
* - отображения пользователя в шапке профиля
*/
export const UserAvatar = (props: UserAvatarProps) => {
const { className, ...imageProps } = props
return <Image {...imageProps} className={cl(styles.root, className)} />
}
```
`user/styles/user-avatar.module.css`
```css
.root {
display: block;
border-radius: 50%;
}
```
## Когда нужен модуль
Решение о выделении модуля остаётся за разработчиком. Поднимать компонент в модуль стоит, если он становится самостоятельной областью:
- получает свои вложенные компоненты;
- получает свои хуки, стор или сервисы;
- получает внутренние мапперы или утилиты;
- требует собственного публичного API;
- начинает переиспользоваться вне родительского модуля;
- становится отдельной зоной параллельной разработки.
Пример: страница — это screen-модуль, а самостоятельные секции страницы — вложенные модули в `parts/`.
```text
screens/home/
├── parts/
│ ├── hero-section/
│ │ ├── styles/
│ │ │ └── hero-section.module.css
│ │ ├── types/
│ │ │ └── hero-section.type.ts
│ │ ├── hero-section.tsx
│ │ └── index.ts
│ └── features-section/
│ ├── styles/
│ │ └── features-section.module.css
│ ├── types/
│ │ └── features-section.type.ts
│ ├── features-section.tsx
│ └── index.ts
├── styles/
│ └── home.module.css
├── types/
│ └── home.type.ts
├── home.screen.tsx
└── index.ts
```
`hero-section` и `features-section` — модули, потому что это самостоятельные части страницы со своей структурой и публичной точкой входа.