deploy without node
Some checks failed
ci / site (push) Has been cancelled
publish-image / publish (push) Has been cancelled

This commit is contained in:
2026-02-10 02:52:14 -05:00
parent 03df2b3a6c
commit 3b0b97f139
25 changed files with 312 additions and 51 deletions

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-02-10

View File

@@ -0,0 +1,62 @@
## Context
The site renders multiple card-like UI elements today:
- videos/podcast listings use `site/src/components/ContentCard.astro`
- blog listings use `site/src/components/BlogPostCard.astro`
These cards have different layouts and metadata placement. This change standardizes the card information architecture so all cards feel consistent.
The site is statically generated (Astro). Card layout consistency should be enforced primarily through shared components and shared CSS rather than copy/paste per page.
## Goals / Non-Goals
**Goals:**
- Define and implement a single, consistent card structure:
- media (image/placeholder) at top
- title
- trimmed summary/excerpt
- meta row: date (left) + views (right, if available)
- footer: source label (youtube/podcast/blog/etc.)
- Apply to all existing card surfaces:
- `/videos` listing cards
- `/podcast` listing cards
- `/blog` post cards (and category listings)
- Keep the layout resilient when fields are missing (no views, no image, no summary).
**Non-Goals:**
- Redesigning non-card list links (e.g., simple navigation links) into cards unless needed for consistency.
- Changing Umami tracking taxonomy (attributes stay intact).
- Large typographic or theme redesign beyond card structure/spacing.
## Decisions
- **Decision: Implement a shared Card component used by existing card components.**
- Rationale: Centralizes markup and ensures layout stays consistent across surfaces.
- Approach:
- Create a new component (e.g., `Card.astro`) with props for:
- `href`, `title`, `summary`, `imageUrl`, `dateLabel`, `viewsLabel`, `sourceLabel`
- optional tracking attributes pass-through (keep existing `data-umami-*` behavior)
- Update `ContentCard.astro` and `BlogPostCard.astro` to render the shared Card component.
- **Decision: Add an optional `summary` field to normalized items.**
- Rationale: Enables the standard card layout to show trimmed summaries for videos/podcast, similar to blog excerpts.
- Approach:
- Extend the normalized content schema/types with `summary?: string`.
- Populate it during ingestion where available (YouTube description snippet; podcast episode summary/description).
- **Decision: Views are optional and shown only when available.**
- Rationale: Not all sources provide views; the layout should be consistent without forcing synthetic values.
## Risks / Trade-offs
- [Risk] Ingestion sources may provide very long summaries.
- Mitigation: Standardize trimming logic in the card component (single truncation helper).
- [Risk] CSS regressions across multiple pages.
- Mitigation: Add tests that assert key card structure/classes exist; verify build outputs for `/videos`, `/podcast`, `/blog`.
- [Risk] Blog post cards and content cards have different link targets (internal vs outbound).
- Mitigation: Shared Card component should be able to render both internal links and external links (target/rel configurable).

View File

@@ -0,0 +1,29 @@
## Why
The site currently renders multiple card variants (videos/podcast cards, blog post cards, etc.) with inconsistent structure and metadata placement, which makes the UI feel uneven. A standardized card layout will create a consistent UX across the website.
## What Changes
- Standardize the UI structure for all content cards across the site:
- featured image displayed prominently on top (when available)
- title
- summary/excerpt, trimmed
- meta row with date (left) and views (right) when available (`space-between`)
- footer row showing the content source (YouTube/podcast/blog/etc.)
- Update existing card renderers/components to use the standardized structure and styling.
- Where a content source does not provide one of the fields (for example, views for blog posts), the layout MUST still render cleanly with the missing field omitted.
## Capabilities
### New Capabilities
- `card-layout-system`: Define the standard card information architecture (image/title/summary/meta/footer) and rules for optional fields so all surfaces render consistently.
### Modified Capabilities
- `social-content-aggregation`: Extend normalized content items to include an optional `summary`/`excerpt` field where available (e.g., YouTube description snippet, podcast episode summary) so non-blog cards can display a trimmed summary.
- `blog-section-surface`: Standardize blog listing cards to include the meta row (publish date and optional views) and footer source label, consistent with the global card layout system.
## Impact
- Affected code: shared card/link components (e.g., `site/src/components/ContentCard.astro`, `site/src/components/BlogPostCard.astro`) and pages that render listings (`/`, `/videos`, `/podcast`, `/blog`).
- Data model: normalized cached items may gain an optional summary field; ingestion code may need to populate it for YouTube/podcast.
- Styling: global CSS updates to ensure consistent spacing/typography and footer/meta layout.

View File

@@ -0,0 +1,33 @@
## MODIFIED Requirements
### Requirement: Blog index listing (posts)
The site MUST provide a blog index page at `/blog` that lists WordPress posts as cards containing:
- featured image (when available)
- title
- excerpt/summary
- publish date
The card MUST render a footer bar that includes:
- publish date on the left
- views on the right when available (if views are not provided by the dataset, the card MUST omit views without breaking layout)
- a content source label (e.g., `blog`)
The listing MUST be ordered by publish date descending (newest first).
Each post card MUST be instrumented with Umami Track Events data attributes and MUST include at minimum:
- `data-umami-event`
- `data-umami-event-target_id`
- `data-umami-event-placement`
- `data-umami-event-target_url`
#### Scenario: Blog index lists posts
- **WHEN** the cached WordPress dataset contains posts
- **THEN** `/blog` renders a list of post cards ordered by publish date descending
#### Scenario: Blog post card click is tracked
- **WHEN** a user clicks a blog post card on `/blog`
- **THEN** the click emits an Umami event with `target_id`, `placement`, and `target_url`
#### Scenario: Blog post card layout is standardized
- **WHEN** `/blog` renders a blog post card
- **THEN** the card shows featured image (when available), title, trimmed excerpt, and a footer bar containing date, optional views, and a source label

View File

@@ -0,0 +1,27 @@
## ADDED Requirements
### Requirement: Standard card information architecture
All content cards rendered by the site MUST use a standardized layout so cards across different surfaces look consistent.
The standard card layout MUST be:
- featured image displayed prominently at the top (when available)
- title
- summary/excerpt text, trimmed to a fixed maximum length
- footer bar showing:
- publish date on the left
- views when available (if omitted, the footer MUST still render cleanly)
- the content source label (e.g., `youtube`, `podcast`, `blog`)
If a field is not available (for example, views for some sources), the card MUST still render cleanly with that field omitted.
#### Scenario: Card renders with all fields
- **WHEN** a content item has an image, title, summary, publish date, views, and source
- **THEN** the card renders those fields in the standard card layout order
#### Scenario: Card renders without views
- **WHEN** a content item has no views data
- **THEN** the card renders the footer bar with date + source and omits views without breaking the layout
#### Scenario: Card renders without featured image
- **WHEN** a content item has no featured image
- **THEN** the card renders a placeholder media area and still renders the remaining fields

View File

@@ -0,0 +1,27 @@
## MODIFIED Requirements
### Requirement: Normalized content items
The system MUST normalize all ingested items (YouTube videos, Instagram posts, podcast episodes) into a single internal schema so the website can render them consistently.
The normalized item MUST include at minimum:
- `id` (stable within its source)
- `source` (`youtube`, `instagram`, or `podcast`)
- `url`
- `title`
- `publishedAt` (ISO-8601)
- `thumbnailUrl` (optional)
The system MUST support an optional summary field on normalized items when available from the source:
- `summary` (optional, short human-readable excerpt suitable for cards)
#### Scenario: Normalizing a YouTube video
- **WHEN** the system ingests a YouTube video item
- **THEN** it produces a normalized item containing `id`, `source: youtube`, `url`, `title`, and `publishedAt`
#### Scenario: Normalizing a podcast episode
- **WHEN** the system ingests a podcast RSS episode
- **THEN** it produces a normalized item containing `id`, `source: podcast`, `url`, `title`, and `publishedAt`
#### Scenario: Summary available
- **WHEN** an ingested item provides summary/description content
- **THEN** the normalized item includes a `summary` suitable for rendering in cards

View File

@@ -0,0 +1,20 @@
## 1. Card Component + Styles
- [x] 1.1 Create a shared card component implementing the standard card layout (media, title, summary, meta row, footer)
- [x] 1.2 Add/adjust shared CSS so the card meta row uses `space-between` and the footer consistently shows the source label
## 2. Data Model Support
- [x] 2.1 Extend normalized `ContentItem` to support an optional `summary` field and ensure it is persisted in the content cache
- [x] 2.2 Populate `summary` for YouTube and podcast items during ingestion (safe trimming / fallback when missing)
## 3. Apply Across Site
- [x] 3.1 Update `ContentCard` surfaces (`/`, `/videos`, `/podcast`) to use the shared card layout and include date/views/source in the standard positions
- [x] 3.2 Update blog post cards (`/blog`, category listings) to use the shared card layout (including publish date and `blog` source footer)
- [x] 3.3 Ensure cards render cleanly when optional fields are missing (no image, no views, no summary)
## 4. Verify
- [x] 4.1 Add/update tests to assert standardized card structure/classes across `ContentCard` and blog post cards
- [x] 4.2 Build the site and verify `/videos`, `/podcast`, and `/blog` render cards matching the standard layout