# Indiekit Eleventy Theme A modern, IndieWeb-native Eleventy theme designed for [Indiekit](https://getindiekit.com/)-powered personal websites. Own your content, syndicate everywhere. ## Features ### IndieWeb First This theme is built from the ground up for the IndieWeb: - **Microformats2** markup (h-card, h-entry, h-feed, h-cite) - **Webmentions** via webmention.io (likes, reposts, replies) - **IndieAuth** with rel="me" verification - **Micropub** integration with Indiekit - **POSSE** syndication to Bluesky, Mastodon, LinkedIn, IndieNews ### Full Post Type Support All IndieWeb post types via Indiekit: - **Articles** — Long-form blog posts with titles - **Notes** — Short status updates (like tweets) - **Photos** — Image posts with multi-photo galleries - **Bookmarks** — Save and share links with descriptions - **Likes** — Appreciate others' content - **Replies** — Respond to posts across the web - **Reposts** — Share others' content - **Pages** — Root-level slash pages (/about, /now, /uses) ### Homepage Builder Dynamic, plugin-configured homepage with: - **Hero section** with avatar, bio, social links - **Recent posts** with configurable filtering - **CV sections** (experience, skills, education, projects, interests) - **Custom HTML** sections from admin UI - **Two-column layout** with configurable sidebar - **Single-column** or **full-width hero** layouts ### Plugin Integration Integrates with custom Indiekit endpoint plugins: | Plugin | Features | |--------|----------| | `@rmdes/indiekit-endpoint-homepage` | Dynamic homepage builder with admin UI | | `@rmdes/indiekit-endpoint-cv` | CV/resume builder with admin UI | | `@rmdes/indiekit-endpoint-github` | GitHub activity, commits, stars, featured repos | | `@rmdes/indiekit-endpoint-funkwhale` | Listening activity from Funkwhale | | `@rmdes/indiekit-endpoint-lastfm` | Scrobbles and loved tracks from Last.fm | | `@rmdes/indiekit-endpoint-youtube` | Channel info, latest videos, live status | | `@rmdes/indiekit-endpoint-blogroll` | OPML/Microsub blog aggregator with admin UI | | `@rmdes/indiekit-endpoint-podroll` | Podcast episode aggregator | | `@rmdes/indiekit-endpoint-rss` | RSS feed reader with MongoDB caching | | `@rmdes/indiekit-endpoint-microsub` | Social reader with channels and timeline | | `@rmdes/indiekit-endpoint-conversations` | Multi-platform interaction aggregation + owner reply threading | | `@rmdes/indiekit-endpoint-comments` | IndieAuth visitor comments with owner replies | ### Modern Tech Stack - **Eleventy 3.0** — Fast, flexible static site generator - **Tailwind CSS** — Utility-first styling with dark mode - **Alpine.js** — Lightweight JavaScript framework - **Pagefind** — Fast client-side search - **Markdown-it** — Rich markdown with auto-linking - **Image optimization** — Automatic WebP conversion, lazy loading ## Installation ### As a Git Submodule (Recommended) This theme is designed to be used as a Git submodule in your Indiekit deployment repository: ```bash # In your Indiekit deployment repo git submodule add https://github.com/rmdes/indiekit-eleventy-theme.git eleventy-site git submodule update --init --recursive cd eleventy-site npm install ``` **Why submodule?** Keeps the theme neutral (no personal data), allows upstream updates, and separates theme development from deployment. ### Standalone Installation For local development or testing: ```bash git clone https://github.com/rmdes/indiekit-eleventy-theme.git cd indiekit-eleventy-theme npm install ``` ## Configuration **All configuration is done via environment variables** — the theme contains no hardcoded personal data. ### Required Variables ```bash # Site basics SITE_URL="https://your-site.com" SITE_NAME="Your Site Name" SITE_DESCRIPTION="A short description of your site" SITE_LOCALE="en" # Author info (displayed in h-card) AUTHOR_NAME="Your Name" AUTHOR_BIO="A short bio about yourself" AUTHOR_AVATAR="/images/avatar.jpg" ``` ### Social Links Format: `Name|URL|icon,Name|URL|icon` ```bash SITE_SOCIAL="GitHub|https://github.com/you|github,Mastodon|https://mastodon.social/@you|mastodon,Bluesky|https://bsky.app/profile/you|bluesky" ``` **Auto-generation:** If `SITE_SOCIAL` is not set, social links are automatically generated from feed credentials (GitHub, Bluesky, Mastodon, LinkedIn). ### Optional Author Fields ```bash AUTHOR_TITLE="Software Developer" AUTHOR_LOCATION="City, Country" AUTHOR_LOCALITY="City" AUTHOR_REGION="State/Province" AUTHOR_COUNTRY="Country" AUTHOR_ORG="Company Name" AUTHOR_PRONOUN="they/them" AUTHOR_CATEGORIES="IndieWeb,Open Source,Photography" # Comma-separated AUTHOR_KEY_URL="https://keybase.io/you/pgp_keys.asc" AUTHOR_EMAIL="you@example.com" ``` ### Social Activity Feeds For sidebar social activity widgets: ```bash # Bluesky BLUESKY_HANDLE="you.bsky.social" # Mastodon MASTODON_INSTANCE="https://mastodon.social" MASTODON_USER="your-username" ``` ### Plugin API Credentials #### GitHub Activity ```bash GITHUB_USERNAME="your-username" GITHUB_TOKEN="ghp_xxxx" # Personal access token (optional, increases rate limit) GITHUB_FEATURED_REPOS="user/repo1,user/repo2" # Comma-separated ``` #### Funkwhale ```bash FUNKWHALE_INSTANCE="https://your-instance.com" FUNKWHALE_USERNAME="your-username" FUNKWHALE_TOKEN="your-api-token" ``` #### YouTube ```bash YOUTUBE_API_KEY="your-api-key" YOUTUBE_CHANNELS="@channel1,@channel2" # Comma-separated handles ``` #### LinkedIn ```bash LINKEDIN_USERNAME="your-username" ``` ### Post Type Configuration Control which post types appear in navigation: ```bash # Option 1: Environment variable (comma-separated) POST_TYPES="article,note,photo,bookmark" # Option 2: JSON file (written by Indiekit or deployer) # Create content/.indiekit/post-types.json: # ["article", "note", "photo"] ``` **Default:** All standard post types enabled (article, note, photo, bookmark, like, reply, repost). ## Directory Structure ``` indiekit-eleventy-theme/ ├── _data/ # Data files │ ├── site.js # Site config from env vars │ ├── cv.js # CV data from plugin │ ├── homepageConfig.js # Homepage layout from plugin │ ├── enabledPostTypes.js # Post types for navigation │ ├── githubActivity.js # GitHub data (Indiekit API → GitHub API fallback) │ ├── funkwhaleActivity.js # Funkwhale listening activity │ ├── lastfmActivity.js # Last.fm scrobbles │ ├── youtubeChannel.js # YouTube channel info │ ├── blueskyFeed.js # Bluesky posts for sidebar │ ├── mastodonFeed.js # Mastodon posts for sidebar │ ├── blogrollStatus.js # Blogroll API availability check │ └── urlAliases.js # Legacy URL mappings for webmentions ├── _includes/ │ ├── layouts/ │ │ ├── base.njk # Base HTML shell (header, footer, nav) │ │ ├── home.njk # Homepage layout (plugin vs default) │ │ ├── post.njk # Individual post (h-entry, webmentions) │ │ └── page.njk # Simple page layout │ ├── components/ │ │ ├── homepage-builder.njk # Renders plugin homepage config │ │ ├── homepage-section.njk # Section router │ │ ├── sidebar.njk # Default sidebar │ │ ├── h-card.njk # Author identity card │ │ ├── reply-context.njk # Reply/like/repost context │ │ └── webmentions.njk # Webmention display + form │ │ ├── sections/ │ │ │ ├── hero.njk # Homepage hero │ │ │ ├── recent-posts.njk # Recent posts grid │ │ │ ├── cv-experience.njk # Work experience timeline │ │ │ ├── cv-skills.njk # Skills with proficiency │ │ │ ├── cv-education.njk # Education history │ │ │ ├── cv-projects.njk # Featured projects │ │ │ ├── cv-interests.njk # Personal interests │ │ │ └── custom-html.njk # Custom HTML content │ │ └── widgets/ │ │ ├── author-card.njk # Sidebar h-card │ │ ├── social-activity.njk # Bluesky/Mastodon feed │ │ ├── github-repos.njk # GitHub featured repos │ │ ├── funkwhale.njk # Now playing widget │ │ ├── blogroll.njk # Recently updated blogs │ │ └── categories.njk # Category list ├── css/ │ ├── tailwind.css # Tailwind source │ ├── style.css # Compiled output (generated) │ └── prism-theme.css # Syntax highlighting theme ├── js/ │ ├── webmentions.js # Client-side webmention fetcher │ └── admin.js # Admin auth detection (shows FAB + dashboard link) ├── images/ # Static images ├── *.njk # Page templates (blog, about, cv, etc.) ├── eleventy.config.js # Eleventy configuration ├── tailwind.config.js # Tailwind configuration ├── postcss.config.js # PostCSS pipeline └── package.json # Dependencies and scripts ``` ## Usage ### Development ```bash # Install dependencies npm install # Development server with hot reload npm run dev # → http://localhost:8080 # Build for production npm run build # → Output to _site/ # Build CSS only (after Tailwind config changes) npm run build:css ``` ### Content Directory The theme expects content in a `content/` directory (typically a symlink to Indiekit's content store): ``` content/ ├── .indiekit/ # Plugin data files │ ├── homepage.json # Homepage builder config │ ├── cv.json # CV data │ └── post-types.json # Enabled post types ├── articles/ │ └── 2025-01-15-post.md ├── notes/ │ └── 2025-01-15-note.md ├── photos/ │ └── 2025-01-15-photo.md └── pages/ └── about.md # Slash page ``` ## Customization ### Colors and Typography Edit `tailwind.config.js`: ```javascript theme: { extend: { colors: { primary: { 500: "#3b82f6", // Your primary color 600: "#2563eb", // ... }, }, fontFamily: { sans: ["Your Font", "system-ui", "sans-serif"], }, }, } ``` Then rebuild CSS: `npm run build:css` ### Dark Mode The theme includes full dark mode support with `dark:` variants. Toggle is available in header/mobile nav, syncs with system preference. ### Override Files When using as a submodule, place override files in your parent repo: ``` your-deployment-repo/ ├── overrides/ │ └── eleventy-site/ │ ├── _data/ # Override data files │ ├── images/ # Your images │ └── about.njk # Override templates └── eleventy-site/ # This theme (submodule) ``` Override files are copied over the submodule during build. **Warning:** Be careful with `_data/` overrides — they can shadow dynamic plugin data. Use only for truly static customizations. ## Plugin Integration ### How Plugins Provide Data Indiekit plugins write JSON files to `content/.indiekit/*.json`. The theme's `_data/*.js` files read these JSON files at build time. **Example flow:** 1. User edits CV in Indiekit admin UI (`/cv`) 2. `@rmdes/indiekit-endpoint-cv` saves to `content/.indiekit/cv.json` 3. Eleventy rebuild triggers (`_data/cv.js` reads the JSON file) 4. CV sections render with new data ### Homepage Builder The homepage builder is controlled by `@rmdes/indiekit-endpoint-homepage`: 1. Plugin provides admin UI at `/homepage` 2. User configures layout, sections, sidebar widgets 3. Plugin writes `content/.indiekit/homepage.json` 4. Theme renders configured layout (or falls back to default) **Fallback:** If no homepage plugin is installed, the theme shows a default layout (hero + recent posts + sidebar). ### Adding Custom Sections To add a custom homepage section: 1. Create template in `_includes/components/sections/your-section.njk` 2. Register in `_includes/components/homepage-section.njk`: ```nunjucks {% if section.type == "your-section" %} {% include "components/sections/your-section.njk" %} {% endif %} ``` 3. Plugin should register the section via `homepageSections` in Indiekit ## Deployment ### Cloudron See `indiekit-cloudron` repository for Cloudron deployment with this theme as submodule. ### Docker Compose See `indiekit-deploy` repository for Docker Compose deployment with this theme as submodule. ### Static Host (Netlify, Vercel, etc.) 1. **Not recommended** — Indiekit needs a server for Micropub/Webmentions 2. For static-only use (no Indiekit), set all env vars and run `npm run build` 3. Deploy `_site/` directory ## Pages Included | Page | URL | Description | |------|-----|-------------| | Home | `/` | Dynamic homepage (plugin or default) | | About | `/about/` | Full h-card with bio | | CV | `/cv/` | Resume with all sections | | Blog | `/blog/` | All posts chronologically | | Articles | `/articles/` | Long-form articles | | Notes | `/notes/` | Short status updates | | Photos | `/photos/` | Photo posts | | Bookmarks | `/bookmarks/` | Saved links | | Likes | `/likes/` | Liked posts | | Replies | `/replies/` | Responses to others | | Reposts | `/reposts/` | Shared content | | Interactions | `/interactions/` | Combined social interactions | | Slashes | `/slashes/` | Index of all slash pages | | Categories | `/categories/` | Posts by category | | GitHub | `/github/` | GitHub activity (if plugin enabled) | | Funkwhale | `/funkwhale/` | Listening history (if plugin enabled) | | Last.fm | `/listening/` | Last.fm scrobbles (if plugin enabled) | | YouTube | `/youtube/` | YouTube channel (if plugin enabled) | | Blogroll | `/blogroll/` | Blog aggregator (if plugin enabled) | | Podroll | `/podroll/` | Podcast episodes (if plugin enabled) | | IndieNews | `/news/` | IndieNews submissions (if plugin enabled) | | Search | `/search/` | Pagefind search UI | | RSS Feed | `/feed.xml` | RSS 2.0 feed | | JSON Feed | `/feed.json` | JSON Feed 1.1 | | Changelog | `/changelog/` | Site changelog | ## IndieWeb Resources - [IndieWebify.me](https://indiewebify.me/) — Test your IndieWeb implementation - [Microformats Wiki](https://microformats.org/wiki/h-card) — Microformats2 reference - [webmention.io](https://webmention.io/) — Webmention service - [IndieAuth](https://indieauth.com/) — Authentication protocol - [Bridgy](https://brid.gy/) — Backfeed social interactions ## Reply-to-Interactions The theme supports threaded owner replies on all interaction types: IndieWeb webmentions, Mastodon/Bluesky backfills, and native authenticated comments. ### How It Works ``` Visitor interaction (Mastodon reply, Bluesky like, webmention, native comment) │ v Conversations API (/conversations/api/mentions) │ Enriches response with owner replies from posts collection │ Adds is_owner: true + parent_url for threading v webmentions.js (client-side) │ processWebmentions() separates owner replies from regular interactions │ Renders regular interactions (likes, reposts, replies) │ threadOwnerReplies() inserts owner reply cards under parent interactions v Threaded display: ┌─────────────────────────────────┐ │ Jane Doe [Mastodon] Mar 11 │ │ Great post! │ │ [Reply] │ │ ┌─────────────────────────────┐ │ │ │ Ricardo Mendes [Author] │ │ │ │ Thanks! │ │ │ └─────────────────────────────┘ │ └─────────────────────────────────┘ ``` ### Key Files | File | Role | |------|------| | `js/webmentions.js` | Fetches interactions from 3 APIs (webmentions, conversations, comments), deduplicates, renders, and threads owner replies | | `js/comments.js` | Alpine.js component for native comment form, IndieAuth flow, and inline reply UI | | `_includes/components/webmentions.njk` | Server-side template with `data-wm-url` attributes and `wm-owner-reply-slot` divs for threading | ### Threading Mechanism 1. **`processWebmentions(allChildren)`** separates items with `is_owner: true` and `parent_url` from regular interactions 2. Regular interactions render normally (likes, reposts, reply cards) 3. Each reply `