feat: Garden dev 0.1
This commit is contained in:
18
_includes/components/garden-badge.njk
Normal file
18
_includes/components/garden-badge.njk
Normal file
@@ -0,0 +1,18 @@
|
||||
{#
|
||||
garden-badge.njk — Digital Garden stage indicator
|
||||
Usage: {% include "components/garden-badge.njk" %}
|
||||
Requires: gardenStage variable in scope (from post frontmatter)
|
||||
Renders a coloured pill badge linking to /garden/#<stage>
|
||||
#}
|
||||
{% if gardenStage %}
|
||||
{% set _stageInfo = gardenStage | gardenStageInfo %}
|
||||
{% if _stageInfo %}
|
||||
<a href="/garden/#{{ gardenStage }}"
|
||||
class="garden-badge garden-badge--{{ gardenStage }}"
|
||||
title="{{ _stageInfo.description }}"
|
||||
aria-label="Garden stage: {{ _stageInfo.label }}">
|
||||
<span aria-hidden="true">{{ _stageInfo.emoji }}</span>
|
||||
<span>{{ _stageInfo.label }}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
@@ -43,6 +43,8 @@ withBlogSidebar: true
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{# Digital Garden stage badge #}
|
||||
{% include "components/garden-badge.njk" %}
|
||||
</div>
|
||||
|
||||
{# Bridgy syndication content - controls what gets posted to social networks #}
|
||||
@@ -228,6 +230,9 @@ withBlogSidebar: true
|
||||
<span data-pagefind-filter="ai-text-level">{{ aiTextLevel }}</span>
|
||||
<span data-pagefind-filter="ai-code-level">{{ aiCodeLevel or "0" }}</span>
|
||||
<span data-pagefind-filter="ai-used">{% if aiUsed %}yes{% else %}no{% endif %}</span>
|
||||
{% if gardenStage %}
|
||||
<span data-pagefind-filter="garden-stage">{{ gardenStage }}</span>
|
||||
{% endif %}
|
||||
{% if category %}
|
||||
{% if category is string %}
|
||||
<span data-pagefind-filter="category">{{ category }}</span>
|
||||
|
||||
@@ -986,3 +986,48 @@ body[data-indiekit-auth="true"] .share-post-btn:hover {
|
||||
background: #374151;
|
||||
color: #34d399;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
Digital Garden — stage badges
|
||||
Used by _includes/components/garden-badge.njk
|
||||
Stages: plant · cultivate · question · repot · revitalize · revisit
|
||||
============================================================ */
|
||||
@layer components {
|
||||
.garden-badge {
|
||||
@apply inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium border no-underline transition-colors;
|
||||
}
|
||||
.garden-badge--plant {
|
||||
@apply bg-green-100 text-green-800 border-green-200 hover:bg-green-200
|
||||
dark:bg-green-900/30 dark:text-green-300 dark:border-green-800 dark:hover:bg-green-900/50;
|
||||
}
|
||||
.garden-badge--cultivate {
|
||||
@apply bg-emerald-100 text-emerald-800 border-emerald-200 hover:bg-emerald-200
|
||||
dark:bg-emerald-900/30 dark:text-emerald-300 dark:border-emerald-800 dark:hover:bg-emerald-900/50;
|
||||
}
|
||||
.garden-badge--question {
|
||||
@apply bg-yellow-100 text-yellow-800 border-yellow-200 hover:bg-yellow-200
|
||||
dark:bg-yellow-900/30 dark:text-yellow-300 dark:border-yellow-800 dark:hover:bg-yellow-900/50;
|
||||
}
|
||||
.garden-badge--repot {
|
||||
@apply bg-orange-100 text-orange-800 border-orange-200 hover:bg-orange-200
|
||||
dark:bg-orange-900/30 dark:text-orange-300 dark:border-orange-800 dark:hover:bg-orange-900/50;
|
||||
}
|
||||
.garden-badge--revitalize {
|
||||
@apply bg-purple-100 text-purple-800 border-purple-200 hover:bg-purple-200
|
||||
dark:bg-purple-900/30 dark:text-purple-300 dark:border-purple-800 dark:hover:bg-purple-900/50;
|
||||
}
|
||||
.garden-badge--revisit {
|
||||
@apply bg-blue-100 text-blue-800 border-blue-200 hover:bg-blue-200
|
||||
dark:bg-blue-900/30 dark:text-blue-300 dark:border-blue-800 dark:hover:bg-blue-900/50;
|
||||
}
|
||||
}
|
||||
|
||||
/* Digital Garden index page — post card in garden listing */
|
||||
@layer components {
|
||||
.garden-post-card {
|
||||
@apply p-3 rounded-lg border border-surface-200 dark:border-surface-700
|
||||
hover:border-surface-300 dark:hover:border-surface-600
|
||||
hover:bg-surface-50 dark:hover:bg-surface-800/50
|
||||
transition-colors;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -927,6 +927,28 @@ export default function (eleventyConfig) {
|
||||
return posts.filter(isListed);
|
||||
});
|
||||
|
||||
// ── Digital Garden ───────────────────────────────────────────────────────
|
||||
// Returns display metadata for a garden stage slug.
|
||||
// Used by garden-badge.njk and garden.njk to render labels + emoji.
|
||||
// Stages map to Obsidian's #garden/* tag convention:
|
||||
// #garden/plant → gardenStage: plant (newly planted idea)
|
||||
// #garden/cultivate → gardenStage: cultivate (actively developing)
|
||||
// #garden/question → gardenStage: question (open exploration)
|
||||
// #garden/repot → gardenStage: repot (being restructured)
|
||||
// #garden/revitalize → gardenStage: revitalize (being refreshed)
|
||||
// #garden/revisit → gardenStage: revisit (flagged for revisiting)
|
||||
eleventyConfig.addFilter("gardenStageInfo", (stage) => {
|
||||
const stages = {
|
||||
plant: { label: "Seedling", emoji: "🌱", description: "Newly planted idea" },
|
||||
cultivate: { label: "Growing", emoji: "🌿", description: "Being actively developed" },
|
||||
question: { label: "Open Question", emoji: "❓", description: "Open for exploration" },
|
||||
repot: { label: "Repotting", emoji: "🪴", description: "Being restructured" },
|
||||
revitalize: { label: "Revitalizing", emoji: "✨", description: "Being refreshed and updated" },
|
||||
revisit: { label: "Revisit", emoji: "🔄", description: "Flagged for revisiting" },
|
||||
};
|
||||
return stages[stage] || null;
|
||||
});
|
||||
|
||||
// Collections for different post types
|
||||
// Note: content path is content/ due to symlink structure
|
||||
// "posts" shows ALL content types combined
|
||||
@@ -1093,6 +1115,15 @@ export default function (eleventyConfig) {
|
||||
.sort((a, b) => b.date - a.date);
|
||||
});
|
||||
|
||||
// Digital Garden — posts with a gardenStage frontmatter property
|
||||
eleventyConfig.addCollection("gardenPosts", function (collectionApi) {
|
||||
return collectionApi
|
||||
.getFilteredByGlob("content/**/*.md")
|
||||
.filter(isPublished)
|
||||
.filter((item) => item.data.gardenStage)
|
||||
.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
|
||||
|
||||
105
garden.njk
Normal file
105
garden.njk
Normal file
@@ -0,0 +1,105 @@
|
||||
---
|
||||
layout: layouts/base.njk
|
||||
title: Digital Garden
|
||||
withSidebar: true
|
||||
permalink: /garden/
|
||||
pagefindIgnore: true
|
||||
---
|
||||
<div class="h-feed">
|
||||
|
||||
{# ── Header ─────────────────────────────────────────────────────────── #}
|
||||
<div class="mb-8 sm:mb-10 pb-6 border-b border-surface-200 dark:border-surface-700">
|
||||
<h1 class="text-2xl sm:text-3xl font-bold text-surface-900 dark:text-surface-100 mb-3 flex items-center gap-2">
|
||||
🌱 Digital Garden
|
||||
</h1>
|
||||
<p class="text-surface-600 dark:text-surface-400 max-w-2xl">
|
||||
A growing collection of ideas in various stages of development. Unlike polished blog posts,
|
||||
garden notes are living documents — planted, cultivated, and sometimes transplanted as my
|
||||
thinking evolves.
|
||||
</p>
|
||||
{# Stage legend #}
|
||||
<div class="mt-4 flex flex-wrap gap-2">
|
||||
{% set _allStages = ["plant", "cultivate", "question", "repot", "revitalize", "revisit"] %}
|
||||
{% for _s in _allStages %}
|
||||
{% set _info = _s | gardenStageInfo %}
|
||||
{% if _info %}
|
||||
<a href="/garden/#{{ _s }}" class="garden-badge garden-badge--{{ _s }}">
|
||||
<span aria-hidden="true">{{ _info.emoji }}</span>
|
||||
<span>{{ _info.label }}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# ── Posts grouped by stage ──────────────────────────────────────────── #}
|
||||
{% set _stages = ["plant", "cultivate", "question", "repot", "revitalize", "revisit"] %}
|
||||
|
||||
{% for _stage in _stages %}
|
||||
|
||||
{# Collect posts for this stage #}
|
||||
{% set _stagePosts = [] %}
|
||||
{% for post in collections.gardenPosts %}
|
||||
{% if post.data.gardenStage == _stage %}
|
||||
{% set _stagePosts = (_stagePosts.push(post), _stagePosts) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if _stagePosts.length > 0 %}
|
||||
{% set _stageInfo = _stage | gardenStageInfo %}
|
||||
|
||||
<section id="{{ _stage }}" class="mb-10 scroll-mt-20">
|
||||
<div class="flex items-baseline gap-3 mb-1">
|
||||
<h2 class="text-xl font-bold text-surface-900 dark:text-surface-100 flex items-center gap-2">
|
||||
<span>{{ _stageInfo.emoji }}</span>
|
||||
<span>{{ _stageInfo.label }}</span>
|
||||
</h2>
|
||||
<span class="text-sm text-surface-500 dark:text-surface-400">
|
||||
{{ _stagePosts.length }} note{% if _stagePosts.length != 1 %}s{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-sm text-surface-500 dark:text-surface-400 mb-4 italic">{{ _stageInfo.description }}</p>
|
||||
|
||||
<ul class="space-y-3 list-none p-0 m-0">
|
||||
{% for post in _stagePosts %}
|
||||
<li class="h-entry garden-post-card">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="font-medium text-surface-900 dark:text-surface-100 mb-0.5">
|
||||
<a class="u-url p-name hover:text-accent-700 dark:hover:text-accent-300 transition-colors"
|
||||
href="{{ post.url }}">
|
||||
{{ post.data.title or (post.templateContent | striptags | truncate(80)) or "Untitled" }}
|
||||
</a>
|
||||
</h3>
|
||||
<div class="flex items-center gap-3 text-xs text-surface-500 dark:text-surface-400">
|
||||
<time class="dt-published font-mono" datetime="{{ post.date | isoDate }}">
|
||||
{{ post.date | dateDisplay }}
|
||||
</time>
|
||||
{% set _postType = post.inputPath | replace("./content/", "") %}
|
||||
{% set _postType = _postType.split("/")[0] %}
|
||||
<span class="capitalize">{{ _postType }}</span>
|
||||
</div>
|
||||
{% if post.data.description %}
|
||||
<p class="p-summary text-sm text-surface-600 dark:text-surface-400 mt-1 line-clamp-2">
|
||||
{{ post.data.description }}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{# ── Empty state ─────────────────────────────────────────────────────── #}
|
||||
{% if not collections.gardenPosts or collections.gardenPosts.length == 0 %}
|
||||
<p class="text-surface-600 dark:text-surface-400">
|
||||
The garden is being prepared. Posts with a <code>gardenStage</code> frontmatter property
|
||||
will appear here once published.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
Reference in New Issue
Block a user