From 8cd3c86bfabfd47bf0e305de10c97069b674710d Mon Sep 17 00:00:00 2001 From: Ricardo Date: Wed, 4 Mar 2026 19:26:18 +0100 Subject: [PATCH] feat: add featured posts section for homepage builder Add `featuredPosts` collection filtering posts with `featured: true` frontmatter. New `featured-posts` section template with type-aware rendering (articles, notes, photos, bookmarks, etc.) and star icon header. Registered in homepage-section.njk dispatcher. To feature a post, add `featured: true` to its frontmatter. Then add a `{ "type": "featured-posts" }` section to the homepage config. Confab-Link: http://localhost:8080/sessions/bd3f7012-c703-47e9-bfe2-2ad04ce1842d --- _includes/components/homepage-section.njk | 4 +- .../components/sections/featured-posts.njk | 251 ++++++++++++++++++ eleventy.config.js | 9 + 3 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 _includes/components/sections/featured-posts.njk diff --git a/_includes/components/homepage-section.njk b/_includes/components/homepage-section.njk index c6900e0..e61e715 100644 --- a/_includes/components/homepage-section.njk +++ b/_includes/components/homepage-section.njk @@ -1,5 +1,7 @@ {# Homepage Section Dispatcher — maps section.type to the right partial #} -{% if section.type == "recent-posts" %} +{% if section.type == "featured-posts" %} + {% include "components/sections/featured-posts.njk" %} +{% elif section.type == "recent-posts" %} {% include "components/sections/recent-posts.njk" %} {% elif section.type == "custom-html" %} {% include "components/sections/custom-html.njk" %} diff --git a/_includes/components/sections/featured-posts.njk b/_includes/components/sections/featured-posts.njk new file mode 100644 index 0000000..6fb8703 --- /dev/null +++ b/_includes/components/sections/featured-posts.njk @@ -0,0 +1,251 @@ +{# + Featured Posts Section - displays curated posts with `featured: true` frontmatter + Rendered by homepage-builder when featured-posts section is configured + Supports type-aware rendering for articles, notes, likes, bookmarks, reposts, replies, photos +#} + +{% set sectionConfig = section.config or {} %} +{% set maxItems = sectionConfig.maxItems or 6 %} +{% set showSummary = sectionConfig.showSummary if sectionConfig.showSummary is defined else true %} + +{% if collections.featuredPosts and collections.featuredPosts.length %} +
+

+ + {{ sectionConfig.title or "Featured" }} +

+ +
+ {% for post in collections.featuredPosts | head(maxItems) %} + {# Detect post type from frontmatter properties #} + {% set likedUrl = post.data.likeOf or post.data.like_of %} + {% set bookmarkedUrl = post.data.bookmarkOf or post.data.bookmark_of %} + {% set repostedUrl = post.data.repostOf or post.data.repost_of %} + {% set replyToUrl = post.data.inReplyTo or post.data.in_reply_to %} + {% set hasPhotos = post.data.photo and post.data.photo.length %} + + {# Determine border color by post type #} + {% set borderClass = "" %} + {% if likedUrl %} + {% set borderClass = "border-l-[3px] border-l-red-400 dark:border-l-red-500" %} + {% elif bookmarkedUrl %} + {% set borderClass = "border-l-[3px] border-l-amber-400 dark:border-l-amber-500" %} + {% elif repostedUrl %} + {% set borderClass = "border-l-[3px] border-l-green-400 dark:border-l-green-500" %} + {% elif replyToUrl %} + {% set borderClass = "border-l-[3px] border-l-sky-400 dark:border-l-sky-500" %} + {% elif hasPhotos %} + {% set borderClass = "border-l-[3px] border-l-purple-400 dark:border-l-purple-500" %} + {% else %} + {% set borderClass = "border-l-[3px] border-l-amber-400 dark:border-l-amber-500" %} + {% endif %} + +
+ + {% if likedUrl %} + {# ── Like card ── #} +
+
+ +
+
+
+ Liked + +
+ {{ likedUrl | unfurlCard | safe }} + + {{ likedUrl }} + + {% if post.templateContent %} +
+ {{ post.templateContent | safe }} +
+ {% endif %} + Permalink +
+
+ + {% elif bookmarkedUrl %} + {# ── Bookmark card ── #} +
+
+ +
+
+
+ Bookmarked + +
+ {% if post.data.title %} +

+ {{ post.data.title }} +

+ {% endif %} + {{ bookmarkedUrl | unfurlCard | safe }} + + {{ bookmarkedUrl }} + + {% if post.templateContent %} +
+ {{ post.templateContent | safe }} +
+ {% endif %} + Permalink +
+
+ + {% elif repostedUrl %} + {# ── Repost card ── #} +
+
+ +
+
+
+ Reposted + +
+ {{ repostedUrl | unfurlCard | safe }} + + {{ repostedUrl }} + + {% if post.templateContent %} +
+ {{ post.templateContent | safe }} +
+ {% endif %} + Permalink +
+
+ + {% elif replyToUrl %} + {# ── Reply card ── #} +
+
+ +
+
+
+ In reply to + +
+ {{ replyToUrl | unfurlCard | safe }} + + {{ replyToUrl }} + + {% if post.templateContent %} +
+ {{ post.templateContent | safe }} +
+ {% endif %} + Permalink +
+
+ + {% elif hasPhotos %} + {# ── Photo card ── #} +
+
+ +
+
+
+ Photo + +
+ + {% if post.templateContent %} +
+ {{ post.templateContent | safe }} +
+ {% endif %} + Permalink +
+
+ + {% elif post.data.title %} + {# ── Article/Page card ── #} +

+ + {{ post.data.title }} + +

+ {% if showSummary and post.templateContent %} +

+ {{ post.templateContent | striptags | truncate(250) }} +

+ {% endif %} +
+ + {% if post.data.postType %} + + {{ post.data.postType }} + + {% endif %} +
+ + {% else %} + {# ── Note card ── #} +
+ + + + {% if post.data.postType %} + + {{ post.data.postType }} + + {% endif %} +
+ {% if post.templateContent %} +
+ {{ post.templateContent | safe }} +
+ {% endif %} + + Permalink + + {% endif %} + +
+ {% endfor %} +
+
+{% endif %} diff --git a/eleventy.config.js b/eleventy.config.js index ba8a9b4..abf07a7 100644 --- a/eleventy.config.js +++ b/eleventy.config.js @@ -856,6 +856,15 @@ export default function (eleventyConfig) { .slice(0, 5); }); + // Featured posts — curated selection via `featured: true` frontmatter + eleventyConfig.addCollection("featuredPosts", function (collectionApi) { + return collectionApi + .getFilteredByGlob("content/**/*.md") + .filter(isPublished) + .filter((item) => item.data.featured === true) + .sort((a, b) => b.date - a.date); + }); + // Weekly digests — posts grouped by ISO week for digest pages and RSS feed eleventyConfig.addCollection("weeklyDigests", function (collectionApi) { const allPosts = collectionApi