---
name: Portfolio 2026 Theme Design
description: Surface overhaul, conflict resolution, and template build-out for the ks-portfolio WordPress theme.
created: 2026-08-16
status: approved
---

# Portfolio 2026 Theme — Design Spec

## 1. Purpose

Rebuild the surface of `ks-portfolio` (WordPress theme at `wp-content/themes/ks-portfolio/`) to match the **Portfolio 2026** identity defined in `notes/DESIGN.md`, `notes/TOKENS.md`, and the three mockups (`home-screen.png`, `archive-screen.png`, `single-screen.png`). Resolve the documented conflicts in `notes/DESIGN.md` §12. Keep the SoloFrame Evo architecture and the installed `projects-portfolio` plugin as-is; build only the presentation layer.

## 2. Source hierarchy

When sources disagree, follow `notes/DESIGN.md` §1. In short:

1. `style.css` for current identity.
2. `notes/Projects Plugin Design.md` for content model.
3. `notes/Background Gradient.md` for atmospheric field.
4. `theme.json`, `styles/`, ACF JSON, templates for implemented tokens.
5. `README.md` and `docs/` for inherited architecture.

User decisions (locked at brainstorm time):

- **Projects source**: theme renders templates against the `projects` CPT provided by the installed `projects-portfolio` plugin. The theme does NOT re-register the CPT.
- **Mockup content**: live `projects` CPT with empty-state fallbacks ("Nothing here yet..."). The mockups are layout references, not content sources.
- **Test scope**: Chromium + axe only. Firefox / WebKit / mobile are out of scope.
- **Pen-test**: static security audit (PHPCS + grep + manual review). No OWASP ZAP.

## 3. Identity & naming

- `style.css` keeps `Theme Name: Portfolio 2026`, `Text Domain: ks-portfolio`.
- PHP namespace `SoloFrameEvo` → `KsPortfolio` across all PHP files (composer script + manual review).
- Drop `screenshot.png` (Basic WP) at the end; replace with a Carbon-Blue-themed capture.

## 4. Visual system

### 4.1 Body surface

```css
body {
  background-color: #100e0b;          /* Carbon Blue page base */
  color: var(--color-on-surface);     /* #e3e1e9 — ~14:1 on dark */
}
```

The existing rule `main#maincontent { background: var(--color-background); }` is removed. Sections paint directly on the dark body; there is no light block underneath content.

### 4.2 Carbon Blue atmospheric field

Implemented as a `views/partials/aura-bg.php` partial loaded on the home and project archive templates (per mockup: dark atmospheric field on hero surfaces, plain dark surface elsewhere).

- Layer 1: linear 138deg `#05070c → #0f172a → #172554 → #1e3a8a`. `mix-blend-mode: normal`. `transform: translateZ(0)`.
- Layer 2: radial ellipse `45% 50% at 72% 40%`, `rgba(96,165,250,0.20)` → transparent 68%. `mix-blend-mode: screen`. `filter: blur(105px)` mobile, `151px` desktop.
- Layer 3: inline SVG `feTurbulence baseFrequency="0.7" numOctaves="4" stitchTiles="stitch"`, `mix-blend-mode: overlay`, `opacity: 0.85`.
- All three: `position: absolute; inset: 0;`, `pointer-events: none;`, `aria-hidden="true"`.
- Parent container: `position: relative; overflow: hidden; min-height: 100vh;` with NO `background-color` (per the blend-mode architecture warning in `notes/Background Gradient.md`).
- Content wrapper: `position: relative; z-index: 1`.

### 4.3 Tokens

Resolve the unresolved tokens from `notes/DESIGN.md` §12:

- Define `--font-headings: var(--font-sans)` in `styles/base/typography.css`.
- Update `theme.json` `styles` block to reference the existing palette slugs (`theme-bg`, `theme-text`) — currently references `background` / `text` which don't exist.
- Add Carbon-Blue editor palette entries in `theme.json`: `theme-surface` (#121318), `theme-on-surface` (#e3e1e9), `theme-outline` (#8f909d), `theme-outline-variant` (#444651), `theme-primary` (#b6c4ff), `theme-on-primary` (#05297a).
- Add 100–900 mix tokens for `theme-primary` if not already present.
- Remove legacy hex values (`#3857BC`, `#ddd`) from pagination CSS; replace with `var(--color-outline-variant)` / `var(--color-primary)`.

Keep the implemented light tokens in `styles/base/colors.css` for any block-editor surface that still wants light, but default new portfolio-facing CSS to the dark surface.

## 5. Template → mockup mapping

### 5.1 `front-page.php` — matches `notes/home-screen.png`

- Header: `WP_DEV_2026` logo, top nav (Plugins, Themes, Open Source, Documentation, Blog), `Download CV` button (right-aligned).
- Hero on Carbon Blue: heading + intro + two CTAs (`Download CV`, `View Repositories`) pulled from ACF Homepage Hero fields. No hardcoded sample text.
- "Recent Projects" section: queries `projects` CPT, max 3, rendered as 3-column grid on `lg`, single column on mobile.
  - Each card: type badge (Plugin / Theme / Pattern), title, excerpt, `Download` + `View Repo` links when both URLs exist (provider-agnostic labels).
- Empty state when zero published projects: "Nothing here yet..." + "No published projects found." centered in the grid area.
- Footer: copyright + GitHub / Privacy / License / Site Status links.

### 5.2 `archive-projects.php` (new) — matches `notes/archive-screen.png`

- Hero: `Project Archive` heading + intro from ACF option (or page-derived intro).
- Grid: project cards 3-up on `lg`, 2-up on `md`, 1-up on mobile.
- Each card: type label (PLUGIN / THEME FRAMEWORK / INTEGRATION, uppercase tracking-wide), title, excerpt, language pill + star count, `View Repo →` link.
- Pagination via `the_posts_pagination` styled against dark surface.

### 5.3 `single-projects.php` (new) — matches `notes/single-screen.png`

- Hero: provider icon + type pill, project title + version suffix (from `projects_portfolio_get_latest_version()`), excerpt.
- Primary `Download vX.Y.Z` button + secondary `View Repo` link.
- Two-column body below: featured image / project content on the left; sidebar (Maintainer, Repository Details table, four stat cards) on the right.
- Owner avatar: 40px square using `background-image` with explicit `width: 40px; height: 40px; background-size: 40px 40px;` (per `notes/Projects Plugin Design.md` §9 — host CSS quirk workaround).
- Below columns: `Architecture & Implementation` (post body content), `Key Features` (list), `Installation` (code block) — all from WordPress content.

### 5.4 `taxonomy-project-type.php` (new)

- Same template as `archive-projects.php` with taxonomy-aware title: `<h1>{project-type label}</h1>` plus a back link to the full archive.

### 5.5 Other templates

- `index.php` (post archive), `search.php`, `single.php` (post), `page.php`, `404.php`: keep structure, recolor surfaces to dark, remove `picsum.photos` fallback URLs, ensure empty-state copy matches existing `Nothing here yet...` / `No published posts found.` / `Page Not Found` patterns.
- Keep `Resources` CPT and `resource_type` taxonomy styling independent from `projects`.

## 6. Components

### 6.1 Buttons

Keep the existing token-driven button block (Primary / Secondary / White / Black × Default / Outline × Small / Medium / Large × Auto / Small / Wide / Full Width). The Primary button in the mockup maps to the existing default-styled Primary button (3px border, semibold 16px, 200ms transitions, focus outline offset, 99% active press).

### 6.2 Project card

New `views/components/project-card.php`:

- Border `1px solid var(--color-outline-variant)`.
- Background `var(--color-surface-container)` (#1e1f25).
- Radius `lg` (1rem).
- Padding `1.5rem`.
- Type badge top-left (small, uppercase, tracking-wide).
- Provider icon top-right (decorative if visible text label is present).
- Title (h3 if in archive, h2 on home), excerpt (line-clamp), action row at bottom with `Download` and `View Repo`.
- Hover: subtle border tint to `var(--color-primary)`.
- Focus-visible: 2px primary outline, 3px offset.

### 6.3 Stat card

New `views/components/project-stat-card.php`:

- Square surface with `var(--color-surface-container)`, radius `lg`, padding `1rem`.
- Icon (decorative), large value, uppercase label below.

### 6.4 Navigation

Keep `views/components/nav-main.php`, `nav-aux.php`, `nav-main__toggle.php`. Recolor links to `var(--color-on-surface)`. Preserve skip link, dropdown behavior, focus visibility, mobile variants.

### 6.5 Header layout

Switch from `grid-cols-[83px_auto]` to a three-region responsive layout (logo | main nav | aux action button). The `Download CV` button is in the right region on `lg`, collapses into the mobile menu below.

## 7. Accessibility (WCAG 2.1 AA)

- Skip-to-main link: preserved (`#f6ff00` on black, yellow focus outline).
- Body text contrast: `#e3e1e9` on `#100e0b` ≈ 14:1 — passes AA.
- Primary buttons: `#b6c4ff` background with `#05297a` text ≈ 9:1 — passes AA.
- All decorative layers `aria-hidden="true"` + `pointer-events: none`.
- Heading order: each template has exactly one `<h1>`. Mockup headings map to page `<h1>`; lower-level headings step down in order.
- Provider icons supplement text (GitHub, Gitea, Download, View Repo) — never replace them.
- Keyboard: tab order header → hero → projects → footer; focus-visible on every interactive element.
- `prefers-reduced-motion`: media-query guard around hover scale on cards / media.

## 8. Security

- All forms use `wp_nonce_field`; state-changing requests use `wp_verify_nonce`.
- Dynamic output escaped with the right `esc_*` function for its context (`esc_html`, `esc_attr`, `esc_url`, `wp_kses_post`).
- Post meta access via `get_post_meta( $id, $key, true )` with explicit defaults.
- No `$_GET` / `$_POST` / `$_REQUEST` without `sanitize_*` + `wp_unslash`.
- External URLs (Download / View Repo) use `esc_url` and add `rel="noopener noreferrer"` when `target="_blank"`.

## 9. Testing plan

### 9.1 Unit / static (composer)

- `composer run lint` (PHPCS with WordPress + WPCS ruleset) must pass clean across all theme PHP.
- Fix any violations inline.

### 9.2 Static security audit

- PHPCS `WordPress.Security` ruleset (`NonceVerification`, `ValidatedSanitizedInput`, `EscapeOutput`).
- Grep audit for `$_GET` / `$_POST` / `$_REQUEST` without `sanitize_*` / `wp_unslash` / `esc_*`.
- Manual review of every new template for nonce, sanitize, escape coverage.
- Severity-ranked written report (Critical / High / Medium / Low / Info) with `file:line` citations and remediation per finding.

### 9.3 Browser / functional (Playwright)

- `playwright.config.js`: chromium project only, base URL `http://portfolio.test` (matches `.env`).
- New `tests/home.spec.js`: home renders Carbon Blue hero, project grid or empty state, no axe violations.
- New `tests/archive.spec.js`: project archive grid renders, no axe violations.
- New `tests/single.spec.js`: project detail hero + sidebar + Download button visible, no axe violations.
- Keep existing `tests/site-a11y.spec.js` (homepage, blog index, 404).
- axe tags: `wcag2a, wcag2aa, wcag21a, wcag21aa, wcag22a, wcag22aa`.
- Full-page screenshots saved to `test-results/` per test.

## 10. File-level changes

### Modified

- `style.css` (already correct — verify)
- `functions.php` (namespace swap, project helper include)
- `header.php`, `footer.php` (namespace swap, dark surface)
- `index.php`, `single.php`, `page.php`, `search.php`, `404.php`, `front-page.php` (namespace swap, dark surface, remove `picsum.photos`)
- `lib/*.php` (11 files: `activation.php`, `class-acf.php`, `class-breadcrumbs.php`, `class-enqueue.php`, `class-menuitems.php`, `class-resources.php`, `extras.php`, `helpers.php`, `hooks.php`, `search-features.php`, `show-template.php` — namespace swap + dark surface tweaks)
- `views/blocks/*/block.json` and any block PHP that uses a namespace (light touch — only what axe or the new templates require)
- `views/forms/search.php` (light touch — escape review only, no behavior change)
- `theme.json` (fix preset references, add dark palette)
- `styles/base/colors.css` (add Carbon Blue tokens)
- `styles/base/typography.css` (define `--font-headings`)
- `styles/base/global.css` (dark body, remove light main rule)
- `views/components/nav-*.php`, `views/partials/*.php` (namespace swap, dark overrides)

### New

- `archive-projects.php`
- `single-projects.php`
- `taxonomy-project-type.php`
- `views/partials/aura-bg.php` (Carbon Blue layers + grain)
- `views/components/project-card.php`
- `views/components/project-stat-card.php`
- `lib/class-projects.php` (thin wrapper around plugin helpers: `get_provider_label()`, `get_release_url()`, `get_repo_browse_url()`, `get_repo_data()`, `get_owner_data()`, `get_download_url()` with provider-agnostic fallbacks)
- `tests/home.spec.js`
- `tests/archive.spec.js`
- `tests/single.spec.js`
- `docs/superpowers/specs/2026-08-16-portfolio-2026-design.md` (this file)
- `docs/superpowers/specs/2026-08-16-portfolio-2026-testing-report.md` (final report)

### Removed

- `screenshot.png` (Basic WP) — replaced at the end
- `picsum.photos` fallback URLs in `index.php`, `search.php`
- Legacy hex values in pagination CSS

## 11. Out of scope

- Migrating existing content (site has minimal published content — no destructive cleanup).
- Changing `projects-portfolio` plugin behavior (we only consume its helpers).
- Removing the `Resources` CPT (per notes: keep both, don't conflate).
- Block-editor UI polish (follow-up).
- Firefox / WebKit / mobile browser testing (out of scope per user choice).
- OWASP ZAP scan (out of scope per user choice).

## 12. Definition of done

- `composer run lint` exits 0.
- Playwright suite passes locally (Chromium, axe-core zero violations across all four specs).
- Static security audit produced with no Critical or High findings (Medium+ requires remediation before sign-off).
- All three mockups (home, archive, single project) reachable on `http://portfolio.test` and visually consistent with their reference screenshot.
- `screenshot.png` replaced with a Carbon-Blue capture.
- Final testing report committed to `docs/superpowers/specs/2026-08-16-portfolio-2026-testing-report.md`.
