# Universal admin component redesign

Audience: AI coding agents first.

## Decision

Evolve existing shared component library in place. Migrate every admin route to shared components. NEVER add page-local visual primitives, inline styles, or a parallel design system.

Authoritative visual references:

- `docs/specs/assets/universal-admin-component-redesign/reference-list.png`
- `docs/specs/assets/universal-admin-component-redesign/reference-fields-split.png` — authoritative Fields/ACF layout reference
- `docs/specs/assets/universal-admin-component-redesign/reference-fields.png` — superseded Fields reference; retain for provenance only

Match references closely. Permit differences only where route content, text, dates, counts, datasets, viewport, or state naturally changes geometry.

## Goals

- Make every International Zone admin route read as one product.
- Match reference hierarchy: fixed light sidebar, restrained page header, horizontal local tabs, bordered white surfaces, compact labeled controls, dense readable tables, blue primary actions, and subtle state badges.
- Preserve current workflows, data contracts, billing safeguards, accessibility, responsive behavior, RTL, and dark mode.
- Make shared components sole rendering vocabulary for visible controls and structural regions.
- Prove visual fidelity with fresh screenshots inspected independently after implementation workers report completion.

## Non-goals

- Do not redesign information architecture or backend APIs.
- Do not revive Admin Snappy lifecycle architecture wholesale.
- Do not create alternate mockups or introduce a second visual direction.
- Do not rewrite working behavior merely to normalize code.
- Do not browser-test client or production sites.

## Migration sequence

Migrate incrementally; NEVER run a big-bang restyle.

1. Generate canonical surface manifest from SPA registrations in `admin/src/main.js` plus plugin-owned WordPress admin registrations, including editor metaboxes and setup/integration surfaces.
2. Capture behavior characterization tests and baseline screenshots before changing each surface.
3. Inventory raw markup, route-local presentation, every inline style/style mutation, and every shared-component bypass. Classify dynamic geometry cases before enforcing removal.
4. Establish semantic tokens, dependency boundaries, compatibility adapters, lint rules, screenshot manifest, and visual-diff tooling.
5. Pilot reference-list translation view; require behavior and visual gates.
6. Pilot reference-fields card-style split view with left group selector and right selected-group detail; require behavior and visual gates, including universal zebra rows in all main/nested tables.
7. Migrate remaining surfaces in bounded waves; each wave must remain independently revertible and pass its route gates before the next wave.
8. Remove compatibility adapters and obsolete styles only after the manifest proves no consumers remain.

## Visual contract

### Application frame

`PageShell` remains the existing route lifecycle coordinator. It MUST retain store/reconciliation/cancellation/teardown ownership. A shared `AppFrame` visual primitive owns sidebar, content canvas, route heading, description, and route-local navigation; `PageShell` composes it through a backward-compatible adapter.

- Sidebar follows reference proportions and hierarchy: product/version block; single-column navigation; blue gradient/solid active item; compact icons; collapse control anchored at bottom.
- Content canvas uses quiet off-white background and generous but bounded desktop gutters.
- Main heading and description align to one content grid. Route tabs sit directly beneath header with thin divider and blue active underline.
- Narrow widths convert navigation and content without document-level horizontal overflow. Only intrinsically wide data regions may scroll internally.

Seam:

```text
AppFrame({ route, title, description, tabs?, actions?, children }): HTMLElement
```

`PageHeader` and `Tabs` remain compatibility seams until all consumers use `AppFrame`; adapters MUST preserve their public APIs and are deleted only when the surface manifest reports zero consumers.

### Surface and layout primitives

`Card`, `FilterBar`, `DataRegion`, and disclosure containers share one surface contract:

- White/light surface, fine neutral border, modest radius, minimal shadow.
- Consistent internal padding and vertical rhythm from global spacing tokens.
- No page-defined border, radius, shadow, or padding literals.
- Dark and high-contrast values come from semantic tokens, not route selectors.

### Controls

`Button`, `Input`, `Select`, `Checkbox`, `Toggle`, search input, and action groups own all interactive appearance.

- Primary buttons use reference blue, white text, compact height, and clear hover/focus/disabled states.
- Secondary buttons use light surface and neutral border.
- Inputs/selects align labels above controls and share heights, borders, radii, typography, icons, focus rings, and error states.
- Page modules configure content and callbacks only; they MUST NOT assemble a custom visual control from raw markup when a shared component contract exists.

### Tables and data regions

`Table`/`GridTable` and `DataRegion` own header, row, selection, action, loading, empty, error, and responsive presentation.

- Match reference density, column alignment, separators, typography, checkbox geometry, and right-aligned actions.
- State labels use `Badge`; completion ratios may use `Badge` or semantic progress presentation.
- Row actions use shared button variants.
- Tables use semantic table markup when data is tabular.
- Wide tables scroll inside their surface; page body never scrolls horizontally.
- All tables use the same universal zebra-row appearance, including field-policy and translation tables. Zebra parity resets independently per table.

`DataRegion` retains store state, reconciliation, cancellation, and interaction-safe update ownership. It composes the single universal zebra-table presentation through existing compatibility seams. `Table` owns semantic tabular markup; `GridTable` remains only as a temporary compatibility adapter where current responsive behavior cannot immediately preserve table semantics. Both MUST render the identical zebra visual contract. Do not create another table representation; migrate `GridTable` consumers to `Table` where semantic tables can satisfy their behavior.

Compatibility adapters preserve existing `Table`/`GridTable` caller APIs during migration. Delete an adapter only after canonical surface-manifest and import-graph checks prove zero consumers.

### Fields/ACF split view

Use `reference-fields-split.png` as authoritative layout for Fields. Render one shared card-style split view:

- Left pane owns “Field Groups” heading, group search, selectable numbered group cards, field-count badges, active blue outline/chevron, bounded scrolling, and result count footer.
- Right pane owns selected-group heading/count, global-default control, field search, main field table, nested child-field cards/tables, total count, and pagination footer.
- Selection updates detail pane without navigating away or losing group-search context.
- At narrow widths, stack group selector above detail while preserving logical focus/order and avoiding document-level horizontal overflow.
- Main and nested tables MUST use universal zebra rows even though reference mockup depicts plain rows. This explicit owner requirement overrides row treatment in the image; all other Fields geometry and card treatment follows the reference.

The split-view boundary is a shared master-detail layout primitive only if another production surface consumes the same interaction contract. Otherwise compose it from `Card`, searchable selection-list, `DataRegion`, and table primitives inside Fields; do not create a speculative generic abstraction.

### Disclosures and field policies

Field groups and nested groups use one shared disclosure primitive with summary row, count metadata, keyboard semantics, and lazy content mount. Expanded tables retain Name, Slug, Type, Key, and Sync/Setting semantics already shipped.

```text
Disclosure({ id, title, meta?, expanded, onToggle, children }): HTMLElement
```

A disclosure boundary is justified because it hides keyboard, ARIA, state, icon, border, and lazy-mount behavior used across nested field structures and future expandable regions.

### Feedback and overlays

`Modal`, `Toast`, `Notice`, `StatusFeedback`, `ProgressBar`, `Skeleton`, `EmptyState`, and `ErrorState` own all corresponding visuals. Route code supplies copy, state, and callbacks only. Modal sizing, tabs, footer actions, focus trapping, and progress layouts remain shared behavior.

## Component migration rule

For every route under `admin/src/pages/`:

1. Inventory every rendered visual unit.
2. Map unit to existing shared component.
3. If shared component lacks a required reusable capability, extend its contract and styles centrally.
4. If no shared component exists and at least two routes/states need the visual behavior, add one deep shared component.
5. If behavior is unique route content, compose existing primitives; do not name a page-local visual component.
6. Delete obsolete duplicate presentation created by migration.

Raw semantic markup remains allowed for route-specific content where no reusable visual behavior exists. “No inline components” means no page-owned reusable visual implementation; it does not require wrapping every text node in a component.

## Styling and dependency architecture

- Central Sass maps define semantic token source values. Build emits semantic CSS custom properties on the admin root; `body.dark-mode` overrides those properties at runtime.
- Shared component styles live under `admin/src/styles/components/` and load through `main.scss`. This existing webpack architecture is authoritative for the SPA despite the stale `/css/` wording in `.claude/agents/expert.md`; update that guidance before implementation so workers receive one rule.
- Page styles may position route-specific content only. They MUST NOT restyle shared controls or define competing tokens.
- Define import layers and enforce them with ESLint/stylelint or an AST-based project check: pages may import public exports from `components/index.js`; pages MUST NOT import component-private modules/styles or create reusable visual classes. Shared components may import tokens/utilities but MUST NOT import pages.
- Replace grep-only enforcement with parser-backed checks for JSX/template style attributes, DOM `.style` writes, `<style>` blocks, `wp_add_inline_style`, and imports that bypass public component exports.
- Inventory existing dynamic style writes before enabling the gate. Replace geometry-dependent cases, including sortable positioning, with shared CSS classes/custom-property contracts whose values are validated at the component trust boundary. Do not suppress required dynamic behavior merely to satisfy the gate.
- Remove translation-local token aliases when global semantic equivalents exist.
- Canonical dark-mode authority is `body.dark-mode`; preserve explicit light mode and do not auto-switch solely from `prefers-color-scheme`.
- Retain reduced motion, forced/high contrast, RTL, 320 CSS px, and 200% zoom support.

## Behavior preservation

Visual migration MUST preserve:

- Explicit language selection before billable bulk generation.
- Disabled and confirmation states protecting billing actions.
- Content/string filters, selection, pagination, generation, translation, progress, and completion behavior.
- ACF accordion disclosure/search/focus behavior and lazy mounting.
- Route teardown, request cancellation, keyed updates, and focus continuity where already implemented.
- Existing API request/response contracts.

## Route coverage

Apply system to every plugin-owned WordPress admin surface, not only SPA routes. Generate coverage from code registrations rather than maintaining this list manually. Coverage includes:

- Dashboard and analytics
- Languages
- Fields
- Translation shell and all Themes/Pages/Posts/CPT/Fields/Plugins views
- History
- Settings and exceptions
- Licensing
- Migration
- Team
- Workflow
- My Assignments
- Onboarding and any reachable setup state
- Editor translation metaboxes and all other plugin-owned WordPress admin integrations outside the SPA

Hidden demos, README examples, and development-only fixtures are not production routes; update only when required by shared API changes.

## Error handling

- Shared data-state components render loading, empty, recoverable error, and terminal error consistently.
- Failed route requests retain stable shell/layout and expose retry where valid.
- Failed mutations preserve user selection, filter context, disclosure state, and logical focus when existing behavior supports it.
- Visual migration MUST NOT turn errors into silent empty states.

## Verification contract

### Reference measurements and visual diff

Before implementation, inspect reference image metadata and record viewport pixel dimensions, assumed DPR, sidebar/content proportions, content bounds, major spacing intervals, representative control heights, typography scale/weight, radii, border colors, and table row density in a machine-readable visual contract. Do not infer browser CSS pixels from image pixels without recording the DPR assumption.

### Risk-based visual verification

NEVER capture every state × theme × direction × viewport combination. Use deterministic layered evidence:

1. **Shared-component showcase:** render every public component and meaningful visual variant. Capture light, `body.dark-mode`, RTL, 320 CSS px, keyboard focus, and forced/high contrast.
2. **Representative layouts:** capture one page per distinct composition. MUST include translation list, Fields/ACF split view, modal, and plugin-owned editor surface.
3. **Pairwise matrix:** select cases so every relevant pair among viewport, direction, theme, and materially visual state occurs at least once. Generate a pair inventory; require 100% pair coverage.
4. **Targeted regions:** visually diff complex tables, nested Fields/disclosures, pagination, modal, and progress presentation where showcase/full-page evidence is insufficient.
5. **Direct references:** compare desktop translation list to `reference-list.png` and desktop Fields to `reference-fields-split.png`; universal zebra rows remain the explicit override.
6. **DOM/browser assertions:** test loading, empty, error, disabled, selected, accessibility attributes, focus continuity, reduced motion, cancellation, and nonvisual interaction contracts without screenshots unless appearance or geometry changes materially.

Manifest entries record coverage layer, component/surface, material state, viewport, direction, theme, fixture, artifact, masks, baseline/reference, calibrated threshold, and covered pairs. Calibrate each threshold from repeated deterministic captures; NEVER use global tolerance. Missing layers, uncovered pairs, direct-reference cases, or artifacts fail verification. The sole canonical inventory is `docs/plans/2026-08-12-universal-admin-component-redesign.md` under **Canonical screenshot inventory — exactly 30**. Agents MUST capture those exact 30 IDs/files and MUST NOT choose, add, substitute, or remove images. Keep assertion-only coverage named there out of the screenshot manifest.

### Deterministic checks

- Add parser-backed source and dependency audits that fail on inline style attributes, JS style mutation, page-owned style blocks, public-export bypasses, and prohibited page-local visual definitions.
- Capture route/surface characterization tests before each migration wave. Cover billing confirmation/disabled states, focus restoration, request cancellation, disclosure state, sorting, selection, filtering, pagination, progress, mutation errors, and teardown where applicable.
- Test shared component variants and route composition contracts.
- Run lint, admin tests, production build, PHP/release gates, and `git diff --check`; address every warning.

### Browser checks

Use `e2e-remote` against authorized self-contained localhost/buildbox WordPress. Capture exactly the 30 screenshots named in the plan’s canonical inventory:

- Shared-component showcase in light, `body.dark-mode`, RTL, 320 CSS px, keyboard focus, and forced/high contrast.
- Translation list/filter/table at reference desktop geometry.
- Fields/ACF split view with main and nested zebra tables at reference desktop geometry.
- Representative modal and plugin-owned editor surface.
- Targeted complex-component regions only where showcase/full-page evidence cannot expose the visual risk.
- Deterministic pairwise cases proving 100% relevant pair coverage across viewport, direction, theme, and materially visual state.

Use DOM/browser assertions—not screenshots—for nonvisual loading, empty, error, disabled, selected, accessibility, focus continuity, reduced motion, cancellation, and interaction contracts unless appearance or geometry changes materially.

Store artifacts under a repository-relative test artifact directory that `e2e-remote` returns to the worktree.

### Independent visual acceptance

Implementation agents MUST NOT self-certify visual completion. After their done claim, main session MUST open every required screenshot and compare it to authoritative references for:

- application frame and alignment;
- typography scale/weight/line-height;
- spacing and density;
- surface, border, radius, and shadow treatment;
- control dimensions and states;
- table/disclosure geometry;
- active navigation/tabs and primary action color;
- overflow, clipping, wrapping, and responsive defects;
- route-to-route consistency.

Any material mismatch returns to implementation. Acceptance requires corrected screenshots, not rationale.

## Architecture decisions

- **Accepted: evolve shared library in place.** Two-way door; preserves behavior and prevents competing systems.
- **Accepted: preserve `PageShell` and `DataRegion` lifecycle responsibilities.** `AppFrame` owns visual shell; `DataRegion` composes table presentation without becoming a table API.
- **Accepted: one universal zebra-table visual contract.** `Table` is target semantic renderer; `GridTable` is compatibility-only and MUST look identical until removable. No dense/plain/zebra representation variants.
- **Accepted: keep disclosure shared.** Boundary hides substantial ARIA, keyboard, lazy-mount, state, and styling complexity.
- **Rejected: parallel “reference theme” layer.** Violates universal-system invariant and creates permanent drift.
- **Rejected: page-by-page CSS matching.** Fast initially but unmaintainable and inconsistent.
- **Rejected: wholesale Admin Snappy revival.** Visual goal does not justify reintroducing rolled-back lifecycle scope.
- **Collapsed: route-specific cards/filter/table wrappers.** Decorative seams fail deletion test; shared primitives own appearance.

## Delivery

Launch implementation from `docs/plans/2026-08-12-universal-admin-component-redesign.md` through `/claudex-workflow`. Preserve reference assets and this spec as immutable acceptance inputs. Land and deploy only after independent visual acceptance and all gates pass.
