The light lavender theme carried its color in decoration -- four colored
background blobs, a conic-gradient brand mark, gradient-clipped wordmark,
a four-color animated button, a spinning conic empty mark, and a five-color
spectrum rail. All of that competed with the only color on screen that
carries meaning: which source a story came from.
- One indigo-slate ink ramp (bg / surface / line / ink) carries every neutral
surface; the only saturated hues are the three sources, used strictly as
signals -- filter pill, card spine, source chip, timestamp, feed health.
- Rule for every filled state: bright fill, deep ink type. Source tokens kept
in sync between :root and View::sourceColor().
- Signature "signal bars": a CSS brand mark of three ascending bars in the
source hues, and the header rail re-cut into three wire segments that double
as the palette legend. The empty state reuses the mark, unlit and static.
- Bootstrap switched to data-bs-theme="dark" with matching form controls,
alerts, and focus rings; --ink-faint recalibrated so small text clears
WCAG 4.5:1 on every surface. Reduced-motion behavior unchanged.
- README documents the two rules next to View::sourceColor().
"<- Dashboard" used href="?", which resolves to the current path with an
empty query -- manage.php -- so it reloaded the manage page instead of
returning to the feed. Point it at index.php.
Browser-based feed selection layered on top of config/feeds.php:
- manage.php (password + CSRF gated, session based): add subreddits
(name / r/name / URL) and YouTube channels (@handle / UC id / URL,
handle resolved to channel_id once, youtube.com only); enable/disable
toggles; remove manual feeds; config feeds are hidden, not deleted.
- New feeds are validated with one immediate fetch (bypassing the
per-refresh host cap): HTTP 404 rejects the entry, 429 keeps it for
a later refresh. YouTube labels adopt the real channel title.
- feeds table gains origin (config|manual) and enabled columns, with
a guarded ALTER migration for existing databases.
- Only enabled feeds are refreshed and served: getItems/countItems
filter by feed_url in enabled feeds, so deselected feeds' cached
stories are hidden, not deleted; re-adding a URL re-enables it.
- Dashboard header gets a Manage link; manage page styled with the
existing design tokens; README documents the feature.
- PHP 8 + PDO SQLite cache with on-demand TTL refresh and dedup by link hash
- laminas-feed + Guzzle for RSS/Atom parsing and HTTP
- Bootstrap 5 UI with source and time-range filters, AJAX load-more
- User cron-free deployment under Apache/php-fpm; .htaccess hardening