## Added - Feed Media Enhancement with Media RSS namespace support - RSS enclosure, media:content, media:thumbnail elements - JSON Feed image field for first image - ADR-059: Full feed media standardization roadmap ## Fixed - Media display on homepage (was only showing on note pages) - Responsive image sizing with CSS constraints - Caption display (now alt text only, not visible) - Logging correlation ID crash in non-request contexts ## Documentation - Feed media design documents and implementation reports - Media display fixes design and validation reports - Updated ROADMAP with v1.3.0/v1.4.0 media plans 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
8.4 KiB
Media Display Fixes - Architectural Design
Status
Active
Problem Statement
Three issues with current media display implementation:
- Images too large - No CSS constraints on image dimensions
- Captions visible - Currently showing figcaption, should use alt text only
- Images missing on homepage - Media not fetched or displayed in index.html
Root Cause Analysis
Issue 1: Images Too Large
The current CSS (/static/css/style.css) has NO styles for:
.note-mediacontainer.media-itemfigure elements.u-photoimages- Responsive image constraints
Images display at their native dimensions, which can break layouts.
Issue 2: Captions Visible
Template (note.html lines 25-27) explicitly renders figcaption:
{% if item.caption %}
<figcaption>{{ item.caption }}</figcaption>
{% endif %}
This violates the social media pattern where captions are for accessibility (alt text) only.
Issue 3: Missing Homepage Media
The index route (public.py line 231) doesn't fetch media:
notes = list_notes(published_only=True, limit=20)
Compare to the note route (lines 263-267) which DOES fetch media.
Architectural Solution
Design Principles
- Consistency: Same media display logic on all pages
- Responsive: Images adapt to viewport and container
- Accessible: Alt text for screen readers, no visible captions
- Performance: Lazy loading for below-fold images
- Standards: Proper Microformats2 markup maintained
Component Architecture
1. CSS Media Display System
Create responsive, constrained image display with grid layouts:
/* Media container styles */
.note-media {
margin-bottom: var(--spacing-md);
width: 100%;
}
/* Single image - full width */
.note-media:has(.media-item:only-child) {
max-width: 100%;
}
.note-media:has(.media-item:only-child) .media-item {
width: 100%;
}
/* Two images - side by side */
.note-media:has(.media-item:nth-child(2):last-child) {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--spacing-sm);
}
/* Three or four images - grid */
.note-media:has(.media-item:nth-child(3)),
.note-media:has(.media-item:nth-child(4)) {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--spacing-sm);
}
/* Media item wrapper */
.media-item {
margin: 0;
padding: 0;
background: var(--color-bg-alt);
border-radius: var(--border-radius);
overflow: hidden;
aspect-ratio: 1 / 1; /* Instagram-style square crop */
display: flex;
align-items: center;
justify-content: center;
}
/* Image constraints */
.media-item img,
.u-photo {
width: 100%;
height: 100%;
object-fit: cover; /* Crop to fill container */
display: block;
}
/* For single images, allow natural aspect ratio */
.note-media:has(.media-item:only-child) .media-item {
aspect-ratio: auto;
max-height: 500px; /* Prevent extremely tall images */
}
.note-media:has(.media-item:only-child) .media-item img {
object-fit: contain; /* Show full image for singles */
width: 100%;
height: auto;
max-height: 500px;
}
/* Remove figcaption from display */
.media-item figcaption {
display: none; /* Captions are for alt text only */
}
/* Mobile responsive adjustments */
@media (max-width: 767px) {
/* Stack images vertically on small screens */
.note-media:has(.media-item:nth-child(2):last-child) {
grid-template-columns: 1fr;
}
.media-item {
aspect-ratio: 16 / 9; /* Wider aspect on mobile */
}
}
2. Template Refactoring
Create a reusable macro for media display to ensure consistency:
New template partial: templates/partials/media.html
{# Reusable media display macro #}
{% macro display_media(media_items) %}
{% if media_items %}
<div class="note-media">
{% for item in media_items %}
<figure class="media-item">
<img src="{{ url_for('public.media_file', path=item.path) }}"
alt="{{ item.caption or 'Image' }}"
class="u-photo"
loading="lazy">
{# No figcaption - caption is for alt text only #}
</figure>
{% endfor %}
</div>
{% endif %}
{% endmacro %}
Updated note.html (lines 16-31):
{# Import media macro #}
{% from "partials/media.html" import display_media %}
{# Media display at TOP (v1.2.0 Phase 3, per ADR-057) #}
{{ display_media(note.media) }}
Updated index.html (after line 26, before e-content):
{# Import media macro at top of file #}
{% from "partials/media.html" import display_media %}
{# In the note loop, after the title check #}
{% if has_explicit_title %}
<h3 class="p-name">{{ note.title }}</h3>
{% endif %}
{# Media preview (if available) #}
{{ display_media(note.media) }}
{# e-content: note content (preview) #}
<div class="e-content">
3. Route Handler Updates
Update the index route to fetch media for each note:
starpunk/routes/public.py (lines 219-233):
@bp.route("/")
def index():
"""
Homepage displaying recent published notes with media
Returns:
Rendered homepage template with note list including media
Template: templates/index.html
Microformats: h-feed containing h-entry items with u-photo
"""
from starpunk.media import get_note_media
# Get recent published notes (limit 20)
notes = list_notes(published_only=True, limit=20)
# Attach media to each note for display
for note in notes:
media = get_note_media(note.id)
# Use object.__setattr__ since Note is frozen dataclass
object.__setattr__(note, 'media', media)
return render_template("index.html", notes=notes)
Implementation Guidelines
Phase 1: CSS Foundation
- Add media display styles to
/static/css/style.css - Test with 1, 2, 3, and 4 image layouts
- Verify responsive behavior on mobile/tablet/desktop
- Ensure images don't overflow containers
Phase 2: Template Refactoring
- Create
templates/partials/directory if not exists - Create
media.htmlwith display macro - Update
note.htmlto use macro - Update
index.htmlto import and use macro - Remove figcaption rendering completely
Phase 3: Route Updates
- Import
get_note_mediain index route - Fetch media for each note in loop
- Attach media using
object.__setattr__ - Verify media passes to template
Testing Checklist
Visual Tests
- Single image displays at reasonable size
- Two images display side-by-side
- Three images display in 2x2 grid (one empty)
- Four images display in 2x2 grid
- Images maintain aspect ratio appropriately
- No layout overflow on any screen size
- Captions not visible (alt text only)
Functional Tests
- Homepage shows media for notes
- Individual note page shows media
- Media lazy loads below fold
- Alt text present for accessibility
- Microformats2 u-photo preserved
Performance Tests
- Page load time acceptable with media
- Images don't block initial render
- Lazy loading works correctly
Security Considerations
- Media paths already sanitized in media_file route
- Alt text must be HTML-escaped in templates
- No user-controlled CSS injection points
Accessibility Requirements
- Alt text MUST be present (fallback to "Image")
- Images must not convey information not in text
- Focus indicators for keyboard navigation
- Proper semantic HTML (figure elements)
Future Enhancements (Not for V1)
- Image optimization/resizing on upload
- WebP format support with fallbacks
- Lightbox for full-size viewing
- Video/audio media support
- CDN integration for media serving
Decision Rationale
Why Grid Layout?
- Native CSS, no JavaScript required
- Excellent responsive support
- Handles variable image counts elegantly
- Familiar social media pattern
Why Hide Captions?
- Follows Twitter/Mastodon pattern
- Captions are for accessibility (alt text)
- Cleaner visual presentation
- Text content provides context
Why Lazy Loading?
- Improves initial page load
- Reduces bandwidth for visitors
- Native browser support
- Progressive enhancement
Why Aspect Ratio Control?
- Prevents layout shift during load
- Creates consistent grid appearance
- Matches social media expectations
- Improves visual harmony
Implementation Priority
- Critical: Fix homepage media display (functionality gap)
- High: Add CSS constraints (UX/visual issue)
- Medium: Hide captions (visual polish)
All three fixes should be implemented together for consistency.