# Coding Standards

## PHP

### WordPress Coding Standards

The project follows [WordPress PHP Coding Standards](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/php/) enforced via PHP_CodeSniffer.

**Configuration:** `phpcs.xml`

Key rules:
- PHP 5.6+ compatibility required
- Text domain: `ajaxpress`
- Minimum WordPress version: 6.0
- `src/` and `node_modules/` directories are excluded from linting

Run the linter:

```bash
npm run report   # Output saved to report.txt
```

### Naming Conventions

| Element | Convention | Example |
|---------|-----------|---------|
| Classes | Title case | `class-admin-rest.php` -> `Admin\REST` |
| Methods | Snake case | `get_default_settings()` |
| Constants | Upper snake case | `AJAXPRESS_VERSION` |
| Options | Prefixed snake case | `ajaxpress_enable_navigation` |
| Hooks | Prefixed snake case | `ajaxpress_is_license_active` |

### File Naming

PHP files follow the WordPress convention:
- Classes: `class-{name}.php` (e.g., `class-boot.php`)
- Abstract classes: `abstract-class-{name}.php`
- One class per file

### Namespaces

All PHP classes use the `AjaxPress` root namespace:

```
AjaxPress\Base
AjaxPress\Options
AjaxPress\Enqueues
AjaxPress\Templates
AjaxPress\Admin\Hooks
AjaxPress\Admin\Enqueues
AjaxPress\Admin\REST
AjaxPress\Admin\Deactivate_Feedback
AjaxPress\Cloudflare\API
AjaxPress\Cloudflare\Cache
AjaxPress\Cloudflare\Hooks
```

### Singleton Pattern

All classes extending `Base` use the singleton pattern. Get an instance via:

```php
$instance = ClassName::get_instance();
```

Never instantiate directly with `new`.

## JavaScript

### Admin Panel (Solid.js)

- **Framework:** Solid.js with JSX
- **File extension:** `.jsx` for components, `.js` for utilities
- **Component naming:** PascalCase (e.g., `CloudflareSetup.jsx`)
- **Store/utility naming:** camelCase (e.g., `settings.js`)

### Frontend Engine

- **Vanilla JavaScript** (no framework)
- **Module pattern:** Functions exported from feature files
- **File extension:** `.js`

### Shared Conventions

| Element | Convention | Example |
|---------|-----------|---------|
| Components | PascalCase | `Switch.jsx`, `ColorPicker.jsx` |
| Functions | camelCase | `initParent()`, `createTransition()` |
| Constants | UPPER_SNAKE_CASE | `WORKER_NAME`, `API_BASE` |
| CSS classes | kebab-case with prefix | `.ajaxpress-progressbar` |
| Event types | UPPER_SNAKE_CASE | `AJAXPRESS_NAVIGATE` |

### Import Aliases

Use path aliases instead of relative imports in source files:

```javascript
// Admin
import Button from '@components/Button'
import { useSettings } from '@util/context'
import Basic from '@/Basic'

// Frontend
import { emitEvent } from '@/utils'
import { IframeContainer } from '@features/iframe-container'
```

## CSS

### Tailwind CSS

Both admin and frontend use Tailwind for utility classes. Custom styles go in SCSS component files.

### Class Prefixing

All custom CSS classes use the `ajaxpress-` prefix to avoid conflicts:

```css
.ajaxpress-progressbar { }
.ajaxpress-spinner { }
.ajaxpress-animate-fade { }
```

### CSS Custom Properties

Runtime-configurable values use CSS custom properties:

```css
--ajaxpress-cursor-mode
--ajaxpress-animation-duration
--progressbar-color
--animation-speed
```
