# Skill: Admin Panel Fullstack

Skill ID: `admin-panel-fullstack`

Rules
- Keep admin code modular: pages, components, API client, utilities.
- Preserve hash-route conventions where already used.
- Keep state flow deterministic (`isDirty`, snapshot compare, save/reset).
- Use shared feedback components (Toast/status), not one-off banners.
- Admin write actions require capability + nonce.
- Validate route/action names with allowlists.
- Scope admin enqueues by `$hook`/target page.
- Do not expose stack/backend internals in admin error UI.
- Use CSS Grid/Flexbox for layout; avoid table-based layout structures.
- Keep global JS identifiers descriptive (4+ chars, plugin-prefixed).
- Keep proper WordPress admin notice classes and contextual help text.
- Keep ABSPATH guard in admin PHP files.
- Admin PHP files start with `if ( ! defined( 'ABSPATH' ) ) exit;`.
- Ensure styles do not conflict with WordPress admin.

Accessibility
- Label all controls and include help text for complex settings.
- Every `<label>` MUST have a matching `for` attribute pointing to the input's `id`. Every form input MUST have an associated `<label>`.
- Modal: focus trap, Escape close, focus restore.
- Modal with tabs: `role="tabpanel"` on content area, `aria-label` from active tab name, `aria-controls` on each tab button pointing to panel ID.
- Tab elements: each tab button needs `aria-controls` pointing to its panel, `aria-selected`, and `role="tab"`. Tab list needs `role="tablist"`.
- Dashboard tab panels: wrap content in `<div role="tabpanel" id="..." tabindex="-1">` for keyboard focus management.
- Announce status updates with `aria-live`.
- Keyboard support for custom controls.
- Add Enter/Space keyboard handlers to custom focusable elements.

Build
- `cd admin && npm run build`
- If shared frontend CSS changed: `npm run build:css`

Mistakes to avoid
| Mistake | Fix |
|---|---|
| One-off page notifications | Use shared Toast/status components |
| Skipping admin rebuild | Run `cd admin && npm run build` |
| Missing nonce/capability checks | Enforce both before write |
| Unvalidated action route values | Allowlist action names |
| Focus not restored after modal close | Restore focus to trigger |
| `<label>` without `for` attribute | Add `for="input-id"` matching the input's `id` |
| Tab buttons without `aria-controls` | Add `aria-controls` pointing to panel ID |
| Modal tabpanel without `aria-label` | Add `aria-label` from active tab name |
| Tab panel content not focusable | Add `tabindex="-1"` for programmatic focus |
| Hardcoded admin URLs | Use `admin_url()` helpers |
| Short global identifiers | Use 4+ char plugin-prefixed names |
| Admin style collisions with WP core | Scope selectors to plugin root wrappers |
| Inconsistent namespace references in old docs | Enforce `TranslatePresszone` namespace |
