deploy without node
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-02-10
|
||||
62
openspec/changes/archive/2026-02-10-card-layout/design.md
Normal file
62
openspec/changes/archive/2026-02-10-card-layout/design.md
Normal 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).
|
||||
|
||||
29
openspec/changes/archive/2026-02-10-card-layout/proposal.md
Normal file
29
openspec/changes/archive/2026-02-10-card-layout/proposal.md
Normal 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.
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
20
openspec/changes/archive/2026-02-10-card-layout/tasks.md
Normal file
20
openspec/changes/archive/2026-02-10-card-layout/tasks.md
Normal 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
|
||||
Reference in New Issue
Block a user