feat: neutralize theme for fresh deployments

Strip personal data from templates so the theme ships clean for any
deployer. Collection pages now use generatePageOnEmptyData so empty
post types show encouraging placeholders instead of 404s. Navigation
is conditional on enabled post types and installed plugins. Sidebar
widgets split into individual components with plugin-aware visibility.
Slashes page explains required plugins for root-level page creation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
rmdes
2026-02-08 15:16:29 +01:00
parent e8d7b29a4b
commit 32aea5ace9
27 changed files with 738 additions and 540 deletions

View File

@@ -22,13 +22,17 @@ withSidebar: true
<p class="text-lg sm:text-xl text-primary-600 dark:text-primary-400 mb-3 sm:mb-4">
{{ site.author.title }}
</p>
{% if site.author.bio %}
<p class="text-base sm:text-lg text-surface-700 dark:text-surface-300 mb-4">
Hi, I geek around tech, information systems, democracy, justice, coercive groups (aka cults), and discernment.
{{ site.author.bio }}
</p>
{% endif %}
{% if site.description %}
<p class="text-base sm:text-lg text-surface-700 dark:text-surface-300 mb-4 sm:mb-6">
My blog serves as a repository for my thoughts, long-form writings (some still in draft), and a place where I bookmark interesting finds from the web. It's also my central hub for cross-posting to networks like Mastodon, Bluesky.
{{ site.description }}
<a href="/about/" class="text-primary-600 dark:text-primary-400 hover:underline font-medium">Read more &rarr;</a>
</p>
{% endif %}
{# Social Links #}
<div class="flex flex-wrap gap-3">
@@ -56,6 +60,25 @@ withSidebar: true
</div>
</section>
{# Homepage content — three-tier fallback: #}
{# 1. Plugin config (homepageConfig) — Phase 3, future #}
{# 2. CV data — show experience/projects/skills #}
{# 3. Default — show recent posts and activity #}
{% set hasCvData = (cv.experience and cv.experience.length) or
(cv.projects and cv.projects.length) or
(cv.skills and (cv.skills | dictsort | length)) %}
{# --- Tier 1: Plugin-driven layout (future) --- #}
{% if homepageConfig and homepageConfig.sections %}
{# Reserved for indiekit-endpoint-homepage plugin — will render configured sections here #}
<section class="mb-8 sm:mb-12">
<p class="text-surface-500 text-center">Homepage plugin layout will render here.</p>
</section>
{# --- Tier 2: CV-based layout --- #}
{% elif hasCvData %}
{# Work Experience Timeline - only show if data exists #}
{% if cv.experience and cv.experience.length %}
<section class="mb-8 sm:mb-12">
@@ -210,3 +233,83 @@ withSidebar: true
Last updated: {{ cv.lastUpdated }}
</p>
{% endif %}
{# --- Tier 3: Default — recent activity when no CV and no plugin --- #}
{% else %}
{# Recent Posts #}
{% if collections.posts and collections.posts.length %}
<section class="mb-8 sm:mb-12">
<h2 class="text-xl sm:text-2xl font-bold text-surface-900 dark:text-surface-100 mb-4 sm:mb-6">Recent Posts</h2>
<div class="space-y-4">
{% for post in collections.posts | head(10) %}
<article class="p-4 bg-white dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 hover:border-primary-400 dark:hover:border-primary-600 transition-colors">
<h3 class="font-semibold text-surface-900 dark:text-surface-100 mb-1">
<a href="{{ post.url }}" class="hover:text-primary-600 dark:hover:text-primary-400">
{{ post.data.title or post.data.name or "Untitled" }}
</a>
</h3>
{% if post.data.summary %}
<p class="text-sm text-surface-600 dark:text-surface-400 mb-2 line-clamp-2">{{ post.data.summary }}</p>
{% endif %}
<div class="flex items-center gap-3 text-xs text-surface-500">
<time datetime="{{ post.data.published or post.date }}">
{{ (post.data.published or post.date) | date("MMM d, yyyy") }}
</time>
{% if post.data.postType %}
<span class="px-2 py-0.5 bg-surface-100 dark:bg-surface-700 rounded">{{ post.data.postType }}</span>
{% endif %}
</div>
</article>
{% endfor %}
</div>
<a href="/blog/" class="text-sm text-primary-600 dark:text-primary-400 hover:underline mt-4 inline-flex items-center gap-1">
View all posts
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
</a>
</section>
{% endif %}
{# Getting Started — onboarding guide for new deployments #}
<section class="mb-8 sm:mb-12 p-6 bg-primary-50 dark:bg-primary-900/20 rounded-lg border border-primary-200 dark:border-primary-800">
<h2 class="text-xl font-bold text-surface-900 dark:text-surface-100 mb-4">Getting Started</h2>
<div class="space-y-4 text-surface-700 dark:text-surface-300">
<div class="flex gap-3">
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-primary-600 text-white text-sm flex items-center justify-center font-bold">1</span>
<div>
<strong class="text-surface-900 dark:text-surface-100">Create your first post</strong>
<p class="text-sm mt-1">
<a href="/session/login" class="text-primary-600 dark:text-primary-400 hover:underline">Sign in</a>,
then visit <a href="/create" class="text-primary-600 dark:text-primary-400 hover:underline">/create</a>
to publish notes, articles, bookmarks, and photos.
</p>
</div>
</div>
<div class="flex gap-3">
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-primary-600 text-white text-sm flex items-center justify-center font-bold">2</span>
<div>
<strong class="text-surface-900 dark:text-surface-100">Set up syndication</strong>
<p class="text-sm mt-1">
Cross-post to Mastodon, Bluesky, and LinkedIn automatically.
Add your credentials to the <code class="text-xs bg-surface-200 dark:bg-surface-700 px-1 py-0.5 rounded">.env</code> file and restart.
</p>
</div>
</div>
<div class="flex gap-3">
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-primary-600 text-white text-sm flex items-center justify-center font-bold">3</span>
<div>
<strong class="text-surface-900 dark:text-surface-100">Enable interactions</strong>
<p class="text-sm mt-1">
Receive likes, replies, and reposts from across the web.
Register at <a href="https://webmention.io" class="text-primary-600 dark:text-primary-400 hover:underline" target="_blank" rel="noopener">webmention.io</a>
and add the token to <code class="text-xs bg-surface-200 dark:bg-surface-700 px-1 py-0.5 rounded">.env</code> as <code class="text-xs bg-surface-200 dark:bg-surface-700 px-1 py-0.5 rounded">WEBMENTION_IO_TOKEN</code>.
</p>
</div>
</div>
</div>
</section>
{% endif %} {# end three-tier fallback #}