firehose/investigation/question-2-rss-prominence.md
Firehose Bot fe156ef81e Add Dashboard nav link for authenticated users
- Add conditional "Dashboard" link in navbar that shows when a user is logged in
- Tests verify the link is hidden for unauthenticated users and visible when authenticated
- Investigation reports for Q1 (drafts/scheduled), Q2 (RSS), Q3 (email subs)
2026-07-06 09:11:13 +01:00

12 KiB

Question 2: How can we make RSS feeds more obvious to viewers?

1. Where are the RSS/Atom feeds currently served?

Application router (app/lib/firehose_web/router.ex, lines 39-41):

scope "/api/blog" do
  forward "/engineering", Blogex.Router, blog: Firehose.EngineeringBlog
  forward "/releases", Blogex.Router, blog: Firehose.ReleaseNotes
end

The Blogex.Router is mounted under /api/blog/{engineering,releases}. This means feeds are at:

Feed URL
Engineering RSS /api/blog/engineering/feed.xml
Engineering Atom /api/blog/engineering/atom.xml
Release Notes RSS /api/blog/releases/feed.xml
Release Notes Atom /api/blog/releases/atom.xml

Blogex.Router (blogex/lib/blogex/router.ex, lines 27-61) handles:

  • GET /feed.xml (line 27) — RSS 2.0 via Blogex.Feed.rss/2
  • GET /atom.xml (line 39) — Atom feed via Blogex.Feed.atom/2

The feeds are not mounted under the human-facing /blog/{id}/ paths but under /api/blog/{id}/. This is suboptimal for usability — users browsing at /blog/engineering won't find a feed at /blog/engineering/feed.xml.

Feed generation (blogex/lib/blogex/feed.ex):

  • Blogex.Feed.rss/2 generates RSS 2.0 with proper XML, using all_posts(), respecting a :limit option (default 20).
  • Blogex.Feed.atom/2 generates Atom feed with same post data.
  • Both include a self-referencing <atom:link> in RSS / <link rel="self"> in Atom (for feed reader validation).
  • Blog/author info is read from the blog module's title/0, description/0, base_path/0 functions.

No. There are zero references to RSS/Atom/feed in any template.

Location File Line(s) Has RSS link?
Navigation bar app/lib/firehose_web/components/layouts/app.html.heex 1-32 No
Homepage app/lib/firehose_web/controllers/page_html/home.html.heex 1-38 No
Blog index app/lib/firehose_web/controllers/blog_html/index.html.heex 1-9 No
Blog post show app/lib/firehose_web/controllers/blog_html/show.html.heex 1-23 No
Blog tag page app/lib/firehose_web/controllers/blog_html/tag.html.heex 1-10 No
HTML <head> app/lib/firehose_web/components/layouts/root.html.heex 1-40 No

Confirmed by codebase grep — grep -rn "feed\|rss\|atom" app/lib/firehose_web/ returned only comments and false matches (e.g. "feed" in error handling, not RSS).


(A) HTML <head> — Autodiscovery (highest priority)

File: app/lib/firehose_web/components/layouts/root.html.heex

Two <link> tags per blog for RSS autodiscovery (feed readers use these to auto-detect feeds):

<link rel="alternate" type="application/rss+xml" title="Engineering Blog" href="/api/blog/engineering/feed.xml" />
<link rel="alternate" type="application/atom+xml" title="Engineering Blog (Atom)" href="/api/blog/engineering/atom.xml" />
<link rel="alternate" type="application/rss+xml" title="Release Notes" href="/api/blog/releases/feed.xml" />
<link rel="alternate" type="application/atom+xml" title="Release Notes (Atom)" href="/api/blog/releases/atom.xml" />

Challenge: The root layout (root.html.heex) is shared across all pages (hompage, about, contact, blog pages). Hardcoding both blogs' feeds works, but ideally only the currently-relevant blog's feed links appear on blog-specific pages. Since the app router uses a single root layout, you'd need to pass the blog context from the controller into the layout assigns, or use @content_for/put_layout approaches.

Option A1 (simple): Hardcode both feeds in <head> unconditionally. Acceptable because:

  • The site only has two blogs with stable URLs.
  • Readers can subscribe to either.
  • Zero configuration needed.

Option A2 (precise): Add blog-specific metadata to the assigns in BlogController (lines 14-28, 31-46) and pass it to the layout via @content_for, similar to how meta_tags are already handled (line 20: FirehoseWeb.Layouts.put_content_for(:meta_tags, meta)). This would require adding a new @content_for slot for feed links.

(B) Blog index pages (app/lib/firehose_web/controllers/blog_html/index.html.heex)

Add an RSS icon next to the blog title in the <header> section (line 4):

<h1 class="text-3xl font-bold font-display">{@blog_title}</h1>

Change to include a small RSS icon link pointing at the appropriate feed URL. The @base_path assign is already available (set in BlogController.index/2, line 24). We can derive the feed URL as /api/blog/engineering/feed.xml when @base_path is /blog/engineering, but there's no direct mapping. A helper mapping function would be needed, or a @blog_id assign should be added to the controller.

Option B1: Add a blog_id assign to the controller, then in the template do:

<a href={"/api/blog/#{@blog_id}/feed.xml"} class="..." title="RSS feed">
  <.rss_icon />
</a>

Option B2 (simpler): Hardcode the mapping in the template or add a helper function in BlogHTML:

defp feed_url("/blog/engineering"), do: "/api/blog/engineering/feed.xml"
defp feed_url("/blog/releases"), do: "/api/blog/releases/feed.xml"

(C) Blog post pages (app/lib/firehose_web/controllers/blog_html/show.html.heex)

Add a subtle "Subscribe via RSS" link or icon near the "Back to posts" link (line 2). The @base_path and @post assigns are available.

Best placement: inline with the "Back to posts" link at the top, or as a small badge/icon in the post metadata area (which is rendered by Blogex.Components.post_meta/1 in blogex/lib/blogex/components.ex, line 76).

(D) Homepage (app/lib/firehose_web/controllers/page_html/home.html.heex)

Add an RSS icon or link near the "Recent posts" heading (line 21, currently just <h2>Recent posts</h2>). Since the homepage shows posts from both blogs, you could:

  • Link to the Engineering blog feed (primary blog)
  • Or show a dropdown between the two feeds
  • Or link to both (Engineering RSS, Releases RSS)

(E) Navigation bar (app/lib/firehose_web/components/layouts/app.html.heex)

More prominent placement: add an RSS icon as a nav item (after the Contact link, line 25). Could be a dropdown linking to both feeds, or just the Engineering blog feed as default. This makes RSS discoverable from any page.


The standard pattern for RSS 2.0 autodiscovery is:

<link rel="alternate" type="application/rss+xml" title="Engineering Blog" href="/api/blog/engineering/feed.xml" />

And for Atom:

<link rel="alternate" type="application/atom+xml" title="Engineering Blog (Atom)" href="/api/blog/engineering/atom.xml" />

For both blogs, this would be 4 <link> tags inserted in root.html.heex between the favicon links (line 9) and the <.live_title> component (line 10), or after line 15 (after the title).

Autodiscovery works with relative paths (feed readers resolve them against the page URL), so using /api/blog/engineering/feed.xml is fine and avoids needing the full host URL.

Insert after line 14 (</.live_title>) and before the CSS link (line 15):

<link rel="alternate" type="application/rss+xml" title="Engineering Blog" href="/api/blog/engineering/feed.xml" />
<link rel="alternate" type="application/atom+xml" title="Engineering Blog (Atom)" href="/api/blog/engineering/atom.xml" />
<link rel="alternate" type="application/rss+xml" title="Release Notes" href="/api/blog/releases/feed.xml" />
<link rel="alternate" type="application/atom+xml" title="Release Notes (Atom)" href="/api/blog/releases/atom.xml" />

5. Adding RSS subscription icons while keeping the design clean

Icon options

Use an inline SVG (no additional dependencies), a commonly recognized RSS icon, approximately 16-20px, matching the current text color. The standard RSS icon is the orange/white dot-and-waves — but matching the site's monochrome theme (text color / primary color) would be more cohesive.

The current design uses:

  • text-base-content for text
  • text-primary for accent links
  • Consistently uses btn btn-ghost btn-sm for nav links

Placement strategies (from subtle to prominent)

  1. Only autodiscovery (no visible icon) — functional for feed readers, invisible to human visitors. Minimum viable.

  2. Small RSS icon in blog header — next to the blog title on index pages. Clean and contextual.

  3. Small RSS icon next to "Recent posts" on homepage — visible on the landing page.

  4. RSS icon as a nav item — consistently visible from any page:

    [Blog] [Releases] [QWAN] [Contact] [📡] [theme toggle]
    
  5. Combination: Autodiscovery + blog index icons + optionally a nav item.

Use a small inline SVG RSS icon (matching the text-base-content/60 muted color) placed:

  • In <head> for autodiscovery (always, no visual impact)
  • In blog index header (a small icon right-aligned next to the page title, or an inline link reading "RSS" in subtle text)
  • Optionally in the navbar as a subtle icon-only button

Implementation details

For the blog index (index.html.heex), the current template at line 4:

<h1 class="text-3xl font-bold font-display">{@blog_title}</h1>

Could become:

<h1 class="text-3xl font-bold font-display flex items-center gap-3">
  {@blog_title}
  <a href={feed_url(@base_path)} class="text-base-content/40 hover:text-primary transition" title="Subscribe to RSS feed">
    <svg class="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
      <path d="M6.18 15.64a2.18 2.18 0 0 1 2.18 2.18C8.36 19 7.38 20 6.18 20C5 20 4 19 4 17.82a2.18 2.18 0 0 1 2.18-2.18M4 4.44A15.56 15.56 0 0 1 19.56 20h-2.83A12.73 12.73 0 0 0 4 7.27V4.44m0 5.66a9.9 9.9 0 0 1 9.9 9.9h-2.83A7.07 7.07 0 0 0 4 12.93v-2.83Z"/>
    </svg>
  </a>
</h1>

This requires a feed_url/1 helper. Since base_path maps cleanly to the API route (/blog/engineering/api/blog/engineering/feed.xml), a simple String.replace_prefix(@base_path, "/blog", "/api/blog") <> "/feed.xml" would work.

Summary of implementation points

Location File Action
<head> app/lib/firehose_web/components/layouts/root.html.heex ~ line 14 Add 4 <link rel="alternate"> tags
Blog index app/lib/firehose_web/controllers/blog_html/index.html.heex ~ line 4 Add RSS icon next to title
Blog show app/lib/firehose_web/controllers/blog_html/show.html.heex ~ line 2 Add subtle RSS link near "Back to posts"
Homepage app/lib/firehose_web/controllers/page_html/home.html.heex ~ line 21 Add RSS icon near "Recent posts" heading
Navbar app/lib/firehose_web/components/layouts/app.html.heex ~ line 25 Optionally add RSS icon nav item
Controller app/lib/firehose_web/controllers/blog_controller.ex ~ line 22 Optionally add blog_id assign for feed URL mapping
Helper app/lib/firehose_web/controllers/blog_html.ex Add feed_url/1 helper function

Note: The current feed URLs under /api/blog/ are slightly awkward. Consider adding the feed.xml and atom.xml routes to the main Blogex.Router also under the human-facing path (e.g., /blog/engineering/feed.xml) by mounting Blogex.Router at both paths, or by adding explicit routes. However, this would be a larger refactor — the simplest short-term approach is to use the existing /api/blog/ URLs and just link to them prominently.