docs(blog-card): add post card styling implementation plan
This commit is contained in:
@@ -0,0 +1,289 @@
|
||||
# Blog Post Card Styling Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Restyle the blog post card to match `blog-post-card.png` — a vertical card with a featured image on top and a solid blue content area below containing the title, byline, and an orange "Read more" link.
|
||||
|
||||
**Architecture:** A targeted CSS update to `styles/components/post-list.css` (the card body, title, byline, image border, and a new `.post-list__read-more` rule), a one-line markup addition in `index.php` to render the "Read more" anchor, and a single new assertion in the existing `tests/blog-page.spec.js`. The dist CSS is rebuilt once at the end.
|
||||
|
||||
**Tech Stack:** WordPress 6.x, PHP 8.x, Tailwind CSS v4, Playwright, axe-core, PHPCS (WordPress standard).
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Tabs for PHP indentation (project standard; see `composer.json` and existing files).
|
||||
- The theme's Tailwind build outputs `static/dist/theme.css`; that file is gitignored but committed with `git add -f` per project convention.
|
||||
- PHPCS uses the WordPress coding standard; `composer lint` must pass. Pre-existing CRLF errors on untouched files are out of scope.
|
||||
- All design colors use the project's existing CSS custom properties from `styles/base/colors.css`. No hardcoded hex values for theme colors.
|
||||
- The card width behavior stays the same: `grid-cols-[repeat(auto-fit,minmax(20rem,1fr))]`. No layout refactor.
|
||||
- The "Read more" link reuses the post's permalink — it's the same destination as the title link.
|
||||
- The H1 page title and `search.php` are explicitly out of scope for this plan.
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
| File | Responsibility | Created/Modified |
|
||||
| --- | --- | --- |
|
||||
| `index.php` | Render the "Read more" link after the byline. | Modify |
|
||||
| `styles/components/post-list.css` | Style the card body, image border, title, byline, and the new `.post-list__read-more`. | Modify |
|
||||
| `static/dist/theme.css` | Rebuilt via `npm run build`. | Modified (committed with `git add -f`). |
|
||||
| `tests/blog-page.spec.js` | Add a single assertion that the "Read more" link is visible in the first card. | Modify |
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Add the "Read more" link in `index.php`
|
||||
|
||||
**Files:**
|
||||
- Modify: `index.php:44-53`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: the standard WordPress loop (`have_posts()`, `the_post()`).
|
||||
- Produces: a `<a class="post-list__read-more" href="<?php the_permalink(); ?>">Read more →</a>` element inside `.post-list__details`, after the `.post-list__byline` div.
|
||||
|
||||
- [ ] **Step 1: Add the "Read more" anchor**
|
||||
|
||||
In `index.php`, after the `<div class="post-list__byline mt-auto">...</div>` block (which ends on line 52 with `</div>`), and before the closing `</div>` of `.post-list__details` (line 53), add the new anchor. The result is:
|
||||
|
||||
```php
|
||||
<div class="post-list__details px-4 py-6 flex flex-col grow">
|
||||
<a href="<?php the_permalink(); ?>" class="">
|
||||
<h2 class="post-list__title font-normal text-25px text-balance line-clamp-4 truncate mt-0 mb-5 mx-0"><?php the_title(); ?></h2>
|
||||
</a>
|
||||
|
||||
<div class="post-list__byline mt-auto">
|
||||
<span class="post-list__author"><?php echo esc_html( ucfirst( get_the_author() ) ); ?> —</span>
|
||||
<span class="post-list__date"><?php echo get_the_date(); ?></span>
|
||||
</div>
|
||||
|
||||
<a class="post-list__read-more" href="<?php the_permalink(); ?>">Read more →</a>
|
||||
</div>
|
||||
```
|
||||
|
||||
Specifically:
|
||||
- Insert one new line after the closing `</div>` of `.post-list__byline` (the line that contains only `</div>` ending the byline block).
|
||||
- The new line is: `<a class="post-list__read-more" href="<?php the_permalink(); ?>">Read more →</a>`
|
||||
- Indentation: tabs (matching the existing PHP in the file).
|
||||
- Use `→` (HTML entity) for the arrow — this is consistent with other `—` / `…` / `«` usage in the same file (see lines 50, 64, 95, 96).
|
||||
|
||||
- [ ] **Step 2: Verify the file parses**
|
||||
|
||||
Run: `php -l index.php`
|
||||
Expected: `No syntax errors detected in index.php`.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add index.php
|
||||
git commit -m "feat(blog): add Read more link to post card"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Add card styles to `styles/components/post-list.css`
|
||||
|
||||
**Files:**
|
||||
- Modify: `styles/components/post-list.css`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: the new `<a class="post-list__read-more">` from Task 1; the existing `.post-list__post`, `.post-list__img`, `.post-list__details`, `.post-list__title`, `.post-list__byline` elements from `index.php`.
|
||||
- Produces: a card with a solid blue content area, dark blue title and byline, and an orange "Read more" link. Removes the gray bottom border between the image and content area.
|
||||
|
||||
- [ ] **Step 1: Replace the contents of `styles/components/post-list.css`**
|
||||
|
||||
The current file is 11 lines containing only `.post-list__h1`. Replace the entire file with:
|
||||
|
||||
```css
|
||||
/* Blog/post index listing styles */
|
||||
|
||||
.post-list__h1 {
|
||||
color: var(--color-cwc-blue-01);
|
||||
font-family: var(--font-quincy, 'Quincy', serif);
|
||||
font-size: var(--h1);
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
margin: 0 0 2rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.post-list__post {
|
||||
border-color: var(--color-cwc-blue-03);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.post-list__img {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.post-list__details {
|
||||
background: var(--color-cwc-blue-03);
|
||||
color: var(--color-cwc-blue-01);
|
||||
}
|
||||
|
||||
.post-list__title {
|
||||
color: var(--color-cwc-blue-01);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.post-list__byline {
|
||||
color: var(--color-cwc-blue-01);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.post-list__read-more {
|
||||
color: var(--color-secondary);
|
||||
font-weight: 600;
|
||||
margin-top: 0.75rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.post-list__read-more:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
```
|
||||
|
||||
Notes on the rules:
|
||||
- `.post-list__post { border-color: var(--color-cwc-blue-03); }` overrides the `border-secondary` class on the element so the card border matches the content area (effectively invisible against the same-colored background).
|
||||
- `.post-list__post { overflow: hidden; }` ensures the image's `rounded-t-md` corners are clipped by the card's `rounded-md` corners (this is the standard "image fills top of card" pattern).
|
||||
- `.post-list__img { border-bottom: 0; }` removes the existing `border-b border-secondary` so the image and content area flow as separate blocks without a divider line.
|
||||
- `.post-list__details { background: var(--color-cwc-blue-03); }` is the solid blue content area (`#90c9e7`).
|
||||
- `.post-list__title { font-weight: 700; }` overrides the `font-normal` in the markup.
|
||||
- `.post-list__read-more` uses `--color-secondary` (aliased to `--color-cwc-orange-01`).
|
||||
- Indentation: 4 spaces (CSS convention, matches the existing `.post-list__h1` rule).
|
||||
|
||||
- [ ] **Step 2: Verify the CSS file parses**
|
||||
|
||||
Tailwind build is the integration test (Task 3). No standalone CSS lint step.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add styles/components/post-list.css
|
||||
git commit -m "feat(blog): style the post card body, title, byline, and Read more link"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Add the "Read more" assertion in `tests/blog-page.spec.js`
|
||||
|
||||
**Files:**
|
||||
- Modify: `tests/blog-page.spec.js:31-35`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: the new `.post-list__read-more` element from Tasks 1 and 2.
|
||||
- Produces: a passing assertion that the "Read more" link is visible in the first card and points to a post URL.
|
||||
|
||||
- [ ] **Step 1: Add the assertion in the desktop test**
|
||||
|
||||
In `tests/blog-page.spec.js`, after the existing first-card assertions (after `await expect(firstCard.locator(".post-list__byline")).toBeVisible();` on line 35), add the new assertion. The result is:
|
||||
|
||||
```js
|
||||
// First card: image, title link, byline.
|
||||
const firstCard = posts.first();
|
||||
await expect(firstCard.locator(".post-list__img img")).toBeVisible();
|
||||
await expect(firstCard.locator(".post-list__title")).toBeVisible();
|
||||
await expect(firstCard.locator(".post-list__byline")).toBeVisible();
|
||||
|
||||
// First card has a "Read more" link.
|
||||
const readMore = firstCard.locator(".post-list__read-more");
|
||||
await expect(readMore).toBeVisible();
|
||||
await expect(readMore).toHaveText(/^Read more\s*\S+$/);
|
||||
await expect(readMore).toHaveAttribute("href", /\/[a-z0-9-]+\/?$/);
|
||||
```
|
||||
|
||||
Notes:
|
||||
- The text regex `/^Read more\s*\S+$/` matches "Read more" followed by whitespace and any non-whitespace character (the `→` arrow or any other trailing punctuation). This is robust to HTML-entity encoding in the rendered DOM.
|
||||
- The `href` regex matches a WordPress post permalink like `/my-post-slug/` or `/my-post-slug`.
|
||||
- Indentation: 4 spaces (matches the existing test file).
|
||||
- Do NOT remove the existing `.post-list__cats`/`.post-list__excerpt` count assertions (lines 38-39). They guard against the trim being undone and remain valid.
|
||||
|
||||
- [ ] **Step 2: Run the new assertion in isolation**
|
||||
|
||||
Run: `npx playwright test tests/blog-page.spec.js -g "Blog title, post grid"`
|
||||
Expected: PASS (the new assertion passes, all existing assertions still pass).
|
||||
|
||||
If the test fails:
|
||||
- "Read more" not found → Task 1's markup change didn't land.
|
||||
- Text mismatch → confirm `→` was used (not `->` or `&rarr;`).
|
||||
- `href` mismatch → confirm the markup uses `<?php the_permalink(); ?>` (not a hardcoded URL).
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add tests/blog-page.spec.js
|
||||
git commit -m "test(blog): assert Read more link is present in the first card"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Build the dist CSS and run the final quality gate
|
||||
|
||||
**Files:**
|
||||
- Modify: `static/dist/theme.css` (committed with `git add -f`).
|
||||
|
||||
- [ ] **Step 1: Run the build**
|
||||
|
||||
Run: `npm run build`
|
||||
Expected: build completes without errors.
|
||||
|
||||
- [ ] **Step 2: Verify the new classes are in the dist**
|
||||
|
||||
Run: `grep -c "post-list__read-more" static/dist/theme.css`
|
||||
Expected: at least 1 match.
|
||||
|
||||
Run: `grep -c "post-list__details" static/dist/theme.css`
|
||||
Expected: at least 1 match (the new `background` rule should compile into the dist).
|
||||
|
||||
- [ ] **Step 3: Commit the rebuilt dist**
|
||||
|
||||
```bash
|
||||
git add -f static/dist/theme.css
|
||||
git commit -m "build: regenerate dist with blog post card styles"
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run the new test file**
|
||||
|
||||
Run: `npx playwright test tests/blog-page.spec.js`
|
||||
Expected: all 4 tests pass.
|
||||
|
||||
- [ ] **Step 5: Run the full Playwright suite**
|
||||
|
||||
Run: `npx playwright test`
|
||||
Expected: same pass/fail count as before this plan. The new test (4th) added 1 passing test; no other tests should regress. The 14 pre-existing failures (12 from prior plans + 2 contact-page pre-existing) are unchanged.
|
||||
|
||||
- [ ] **Step 6: Run PHPCS**
|
||||
|
||||
Run: `composer lint`
|
||||
Expected: no NEW errors. The 5 pre-existing CRLF errors on untouched files (or 20 pre-existing issues per the prior `composer lint` baseline) remain unchanged.
|
||||
|
||||
- [ ] **Step 7: Final commit (only if anything changed in step 1-6)**
|
||||
|
||||
```bash
|
||||
git add -A
|
||||
git commit -m "chore: final pass after Playwright and PHPCS"
|
||||
```
|
||||
|
||||
If nothing changed (no dist drift, no PHPCS issues, no other drift), skip the commit.
|
||||
|
||||
---
|
||||
|
||||
## Self-Review Notes
|
||||
|
||||
- **Spec coverage:**
|
||||
- "Vertical card (image top, content below)" → already true; the card structure is unchanged.
|
||||
- "Title + byline + Read more link" → Task 1 adds the link; existing markup already has title and byline.
|
||||
- "Solid `--color-cwc-blue-03` background" → Task 2 (`.post-list__details`).
|
||||
- "Title in `--color-cwc-blue-01`, weight 700" → Task 2 (`.post-list__title`).
|
||||
- "Byline in `--color-cwc-blue-01`, smaller" → Task 2 (`.post-list__byline`).
|
||||
- "Read more in `--color-secondary` (orange)" → Task 2 (`.post-list__read-more`).
|
||||
- "Remove gray border between image and content" → Task 2 (`.post-list__img { border-bottom: 0; }`).
|
||||
- "Card border matches content area" → Task 2 (`.post-list__post { border-color: ...; }`).
|
||||
- "Single new test assertion" → Task 3.
|
||||
- "Dist rebuild" → Task 4.
|
||||
- "All design tokens, no hardcoded hex" → Task 2 uses only `var(--...)` references.
|
||||
- "Out of scope: H1, search.php" → explicitly noted in Global Constraints.
|
||||
|
||||
- **Placeholder scan:** No TBD/TODO. The "→" character is exact in both `index.php` (`→`) and the test (`→`). The `href` regex is exact.
|
||||
|
||||
- **Type consistency:** Class names: `post-list__read-more` (consistent with `post-list__post`, `post-list__title`, `post-list__byline`). CSS variable names: `--color-cwc-blue-01`, `--color-cwc-blue-03`, `--color-secondary` (all existing tokens).
|
||||
|
||||
- **Indentation:** Tabs for PHP (Task 1), 4 spaces for CSS (Task 2) and JS (Task 3). Each matches the convention in its file.
|
||||
Reference in New Issue
Block a user