# codify-frontend-component-style — Design ## Context Frontend уже использует React/Vite, TypeScript strict, Biome, FSD checks, Knip, coverage ratchet, build и bundle budget внутри canonical gate `npm run verify`. Code style сейчас документирован в `frontend/docs`, но React component declaration style не нормализован: production `*.tsx` files содержат `export function`, function declarations для локальных components, destructuring props прямо в parameters, inline props object types и несколько export statements в одном module. Желаемый project style уже проявлен на `AccountHeading`: React component становится `const`, получает named props type, принимает `props`, destructuring делает внутри body, а module export стоит внизу. Этот shape нужно распространить на authored frontend source. Generated OpenAPI source исключён из style ownership, потому что им владеет generator. Ограничение Change Owner: не писать custom style checker. Поэтому hard guardrail покрывает только то, что поддерживает existing deterministic local tooling. Остальные conventions фиксируются как mandatory review guidance, но не выдаются за gate-blocking invariant. ## Goals / Non-Goals **Goals:** - Нормализовать authored frontend React components к const-valued component declarations с `FC` typing. - Зафиксировать named props types и запрет inline props typing как mandatory frontend convention. - Перенести props destructuring из function parameters внутрь component body как mandatory frontend convention. - Настроить existing frontend tooling так, чтобы проверяемая часть component style блокировала `npm run verify`. - Мигрировать current authored frontend source, включая `index.ts` barrels, к единому export style без изменения runtime behavior. - Документировать границу между enforceable guardrails и review-only conventions. **Non-Goals:** - Custom AST checker, repository hygiene style checker или новый lint framework. - Автоматический запрет inline props typing, props parameter destructuring или более чем одного export statement, если existing tooling не поддерживает это правило. - Изменение route structure, FSD boundaries, API hooks, generated OpenAPI types, localization keys, visual design, tests semantics или product behavior. - Изменение backend, persisted data, runtime OpenAPI contract или отдельного as-built docs repository. ## Decisions **D1. Components объявляются как `const` с `FC`.** Named React components в authored frontend source объявляются как `const Component: FC = (props) => { ... }` или `const Component: FC = () => { ... }`, когда props нет. Это сохраняет визуально явную React-component семантику и позволяет не повторять props type в parameter annotation. *Alternative:* `const Component = (props: Props) => { ... }`. Отклонено: Change Owner предпочёл `FC`, потому что это яснее маркирует React component и уменьшает annotation noise в arguments. **D2. Props contract named и не inline.** Components с props получают named `type ComponentProps = ...` рядом с component либо в соседнем `Component.types.ts`, если тип переиспользуется или module становится крупным. Components только с `children` используют `FC` без лишнего props alias. Components, у которых кроме `children` есть другие props, используют named type на основе `PropsWithChildren<{ ... }>`. Empty props type не создаётся для components без props. *Alternative:* разрешить inline `{ foo: Foo }` для маленьких components. Отклонено: именно inline props typing создаёт разнобой и ухудшает scanning. **D3. Props destructuring выполняется внутри body.** Component parameter остаётся `props`, а `const { ... } = props;` располагается в начале component body после local hooks/перед использованием согласно читаемости конкретного component. Это делает parameter shape одинаковым и упрощает добавление новых props без изменения signature style. **D4. Export surface выбирает форму по роли файла.** Authored source использует named exports, default exports запрещены. Для обычных modules preferred shape — один bottom export statement. Для чистых `index.ts` barrels preferred shape — прямые re-exports `export { X } from "./X";` без промежуточных imports. Где TypeScript/Biome не позволяет выразить всё одним statement без ухудшения читаемости, это остаётся review convention, но `useExportsLast` всё равно запрещает exports перед non-export declarations. **D5. Existing Biome rules покрывают enforceable subset.** Biome configuration включает rules для named React components as arrow/const-valued components, запрета default exports и размещения exports после body declarations. Эти rules становятся частью existing `npm run lint`, а значит и `npm run verify`. *Alternative:* написать project-specific checker для exact style. Отклонено по явному решению Change Owner и из-за maintenance cost для правил, которые должны оставаться review conventions. **D6. Проверка нового hard guardrail доказывает pass и fail без custom checker.** Implementation должен показать, что compliant authored source проходит configured Biome gate, а controlled representative violations падают. Это проверяет именно Biome config, не вводя отдельный style checker для project source. **D7. Migration behavior-preserving и mechanical.** Existing frontend source мигрируется синтаксически: declarations, props typing/destructuring и export placement. JSX, hooks order, route definitions, query behavior, state selection, localization keys и public imports не меняются намеренно. Если при миграции обнаруживается behavior change, work возвращается к upstream artifact. ## Delivery Map | Area | Status | Architectural outcome or N/A reason | | --- | --- | --- | | frontend | `affected` | Authored frontend source migrated to unified React component/export style; frontend Biome config enforces the checkable subset; frontend conventions document enforceable vs review-only parts; frontend canonical gate covers the configured rules. | | backend | `N/A` | Backend behavior, routes, DTOs, persistence, T-Bank integration and runtime OpenAPI are not touched. | | specs | `affected` | This SDD change carries approved Proposal, Delta Spec, Design and later Tasks for the frontend component style guardrail. | | docs | `N/A` | Separate as-built docs repository does not change: product behavior and system architecture are unchanged; frontend-local conventions live in the frontend repository. | ### Contract Gate Decision | Gate | Status | Reason | | --- | --- | --- | | runtime-openapi | `N/A` | Change does not touch backend API behavior, generated frontend API client or committed OpenAPI snapshot. | ## Solution Assessment This change adjusts frontend tooling policy but does not introduce a new tooling platform. The relevant options are: - **Existing Biome rules.** Best fit for enforceable subset: already installed, already part of `npm run lint` and `npm run verify`, deterministic and low-maintenance. Limitation: it cannot enforce every preferred convention, including exact single-export count and inline props typing. - **Custom repository checker.** Could enforce exact local style, but adds parser/AST maintenance, exception policy and separate failure UX. Rejected by Change Owner. - **ESLint or another lint framework.** Could offer broader ecosystem rules but would duplicate Biome, add dependencies and increase maintenance for a small frontend. Rejected because existing Biome support covers the hard guardrail subset required by Delta Spec. Decision: use existing Biome rules for gate-blocking checks and document the remaining style as review conventions. ## Risks / Trade-offs - **Partial enforcement.** Inline props typing, destructuring-in-parameters and exact single-export count can still regress. Mitigation: conventions clearly mark them mandatory review rules, while docs avoid calling them hard guardrails. - **Nursery rule stability.** Biome's React component definition rule is a nursery rule. Mitigation: pin exact dependency versions already used by the project and validate config through the canonical gate. - **Large mechanical diff.** Migrating all frontend source can obscure accidental behavior changes. Mitigation: keep edits syntax-focused, run existing tests/build/typecheck and perform semantic verification against the non-goals. - **`FC` typing trade-off.** `FC` makes component intent explicit but imports a React type into many files and may imply React component-specific typing where a plain function would suffice. This is accepted as the project convention. - **Barrel readability.** Direct re-exports in `index.ts` can create several export statements. Mitigation: use this shape only for pure barrels and keep ordinary modules on bottom named export blocks. ## Migration Plan 1. Configure Biome for the checkable style subset: const-valued named React components, no default exports and exports after body declarations. 2. Add verification evidence for the configured hard guardrail with one compliant case and representative failing cases, without adding a custom project style checker. 3. Update frontend conventions to document the full preferred style and clearly separate hard gate rules from review-only conventions. 4. Migrate authored frontend source mechanically: component declarations, `FC` imports, named props types where they carry real props shape, direct `FC` for children-only components, internal props destructuring, bottom grouped exports for ordinary modules and direct re-exports for pure barrels. 5. Run focused style/lint checks, then frontend canonical `npm run verify`. 6. Validate the OpenSpec change and run the workspace dispatcher after Tasks are complete. Rollback is a normal revert of frontend/specs commits. No data migration, feature flag, API migration or backend deployment sequence is required.