# Style guide

Follow this guide when contributing. Deviations require review.

## PHP

- PSR-12 formatting. Run `composer format` (Laravel Pint) before every commit.
- Strict types where possible: `declare(strict_types=1);` at top of new files.
- One class per file. Class name matches filename.
- Constructor property promotion for services and actions.
- Readonly properties where mutation isn't needed.
- Guard clauses over nested `if`. Never `else` after early return.
- `throw` domain exceptions; never `abort(500)`.
- Use `dispatch_sync()` for actions triggered inline; never queue by accident.
- Type-hint everything including array shapes via docblocks:
  `@return array<int, User>`.

## Blade

- Views hold ONLY presentation. No `DB::` calls, no `Auth::user()->company->…`
  chains — use view composers or explicit view parameters.
- Reusable components live in `resources/views/components/`. Never repeat markup
  that appears in three places — component it.
- Slot names are singular (`<x-slot:actions>`, not `actions_list`).
- `@csrf` on every form. `@method('PUT'|'DELETE')` where applicable.
- Icons via `icon('name')` helper. Never inline raw `<svg>`.

## SCSS

- BEM naming: `.block__element--modifier`.
- Design tokens live only in `abstracts/_tokens.scss`. Every colour/size/radius
  MUST resolve to a CSS variable.
- Never target Bootstrap classes with `!important` — extend via layered rules.
- Import order in `app.scss` is FROZEN. New files go under the appropriate
  folder and are added at the bottom of that folder's group.

## JavaScript

- ES modules only. No IIFEs, no globals other than `window.DecentERP`.
- `resources/js/core/` files must run in isolation (import graph is a DAG rooted
  at `app.js`).
- Never fetch without going through `core/http.js`.
- Every user-facing string flows through `toast` — no `alert()`.

## Routes

- Named routes only. Format: `<module>.<resource>.<action>` (`settings.company.update`).
- REST verbs mapped correctly (GET/POST/PUT/PATCH/DELETE).
- Group by middleware, not by controller.

## Migrations

- Every migration MUST include `down()` even if empty.
- Foreign keys ALWAYS with `cascadeOnDelete()` or `nullOnDelete()` — never
  leave orphans.
- Add indexes proactively for columns used in `where`/`order by`.

## Commits

- Conventional Commits: `feat(scope): message`.
- One logical change per commit. No "misc fixes".
- Reference sprint + issue: `feat(auth): rate-limit login (S1/#42)`.
