Claude-skill-registry eleventy
Build content-focused websites with Eleventy (11ty). Use when creating templates (.njk, .liquid), working with data cascade, collections, or deploying static sites.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/eleventy" ~/.claude/skills/majiayu000-claude-skill-registry-eleventy && rm -rf "$T"
manifest:
skills/data/eleventy/SKILL.mdsource content
Eleventy (11ty) Skill
Build fast, flexible static sites with Eleventy's zero-JS-by-default approach and powerful data cascade.
Philosophy Alignment
Eleventy perfectly matches progressive enhancement principles:
| Principle | Eleventy Implementation |
|---|---|
| HTML-first | Outputs pure static HTML |
| Zero JS by default | No JavaScript unless you add it |
| Template flexibility | Use any template language |
| Data-driven | Powerful data cascade system |
Project Structure
src/ ├── _includes/ # Layouts and partials │ ├── layouts/ │ │ └── base.njk │ └── partials/ │ └── header.njk ├── _data/ # Global data files │ ├── site.json # Site metadata │ └── navigation.js # Dynamic data ├── content/ # Content pages │ ├── index.njk # → / │ ├── about.njk # → /about/ │ └── blog/ │ ├── blog.json # Directory data │ └── post-1.md # → /blog/post-1/ ├── assets/ │ ├── css/ │ └── js/ └── eleventy.config.js # Configuration
Configuration
// eleventy.config.js export default function(eleventyConfig) { // Copy static assets eleventyConfig.addPassthroughCopy('src/assets'); // Watch for changes eleventyConfig.addWatchTarget('src/assets/css/'); // Add filters eleventyConfig.addFilter('dateFormat', (date, format) => { return new Intl.DateTimeFormat('en-US', { dateStyle: format || 'medium' }).format(date); }); // Add shortcodes eleventyConfig.addShortcode('year', () => `${new Date().getFullYear()}`); // Add collections eleventyConfig.addCollection('posts', (collectionApi) => { return collectionApi .getFilteredByGlob('src/content/blog/*.md') .filter(post => !post.data.draft) .sort((a, b) => b.date - a.date); }); return { dir: { input: 'src', output: '_site', includes: '_includes', data: '_data', }, markdownTemplateEngine: 'njk', htmlTemplateEngine: 'njk', }; }
Data Cascade
Data flows from global to specific, with later values overriding earlier:
1. Global Data (_data/*.json, _data/*.js) 2. Directory Data (blog/blog.json) 3. Template Front Matter 4. Computed Data
Global Data
// src/_data/site.json { "title": "My Website", "description": "A website built with Eleventy", "url": "https://example.com", "author": { "name": "Your Name", "email": "you@example.com" } }
// src/_data/navigation.js export default [ { text: 'Home', url: '/' }, { text: 'About', url: '/about/' }, { text: 'Blog', url: '/blog/' }, ];
Directory Data
Apply data to all files in a directory:
// src/content/blog/blog.json { "layout": "layouts/post.njk", "tags": ["posts"], "permalink": "/blog/{{ page.fileSlug }}/" }
Front Matter
--- title: My Blog Post description: A description of this post date: 2024-01-15 tags: - javascript - tutorial draft: false ---
Template Languages
Nunjucks (.njk)
Primary recommended template language:
{# src/_includes/layouts/base.njk #} <!doctype html> <html lang="en"> <head> <meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1"/> <meta name="description" content="{{ description or site.description }}"/> <title>{{ title }} | {{ site.title }}</title> <link rel="stylesheet" href="/assets/css/main.css"/> </head> <body> {% include "partials/header.njk" %} <main> {{ content | safe }} </main> {% include "partials/footer.njk" %} </body> </html>
Conditionals and Loops
{# Conditionals #} {% if featured %} <span class="badge">Featured</span> {% endif %} {% if posts.length %} <ul> {% for post in posts %} <li> <a href="{{ post.url }}">{{ post.data.title }}</a> <time datetime="{{ post.date | dateFormat }}"> {{ post.date | dateFormat }} </time> </li> {% endfor %} </ul> {% else %} <p>No posts yet.</p> {% endif %}
Macros (Reusable Components)
{# src/_includes/macros/card.njk #} {% macro card(title, href, description) %} <article class="card"> <h2><a href="{{ href }}">{{ title }}</a></h2> {% if description %} <p>{{ description }}</p> {% endif %} </article> {% endmacro %} {# Usage #} {% from "macros/card.njk" import card %} {{ card( title="My Post", href="/blog/my-post/", description="A brief description" ) }}
Collections
Group content for listing pages:
// eleventy.config.js eleventyConfig.addCollection('posts', (collectionApi) => { return collectionApi.getFilteredByGlob('src/content/blog/**/*.md'); }); // Tag-based collection (automatic) // Any content with `tags: posts` in front matter eleventyConfig.addCollection('posts', (collectionApi) => { return collectionApi.getFilteredByTag('posts'); });
Using Collections
{# List all posts #} <ul> {% for post in collections.posts %} <li> <a href="{{ post.url }}">{{ post.data.title }}</a> </li> {% endfor %} </ul> {# Paginate posts #} --- pagination: data: collections.posts size: 10 alias: posts --- {% for post in posts %} ... {% endfor %} {# Pagination navigation #} {% if pagination.href.previous %} <a href="{{ pagination.href.previous }}">Previous</a> {% endif %} {% if pagination.href.next %} <a href="{{ pagination.href.next }}">Next</a> {% endif %}
Filters
Built-in and custom filters:
// eleventy.config.js // Date formatting eleventyConfig.addFilter('dateFormat', (date, format = 'medium') => { return new Intl.DateTimeFormat('en-US', { dateStyle: format }).format(date); }); // Reading time eleventyConfig.addFilter('readingTime', (content) => { const words = content.split(/\s+/).length; const minutes = Math.ceil(words / 200); return `${minutes} min read`; }); // Limit array eleventyConfig.addFilter('limit', (arr, limit) => arr.slice(0, limit)); // Excerpt eleventyConfig.addFilter('excerpt', (content, length = 200) => { const text = content.replace(/<[^>]+>/g, ''); return text.length > length ? text.slice(0, length) + '...' : text; });
{# Usage #} <time>{{ post.date | dateFormat('long') }}</time> <span>{{ post.content | readingTime }}</span> <p>{{ post.content | excerpt(150) }}</p> {% for post in collections.posts | limit(5) %} ... {% endfor %}
Shortcodes
Reusable content snippets:
// eleventy.config.js // Simple shortcode eleventyConfig.addShortcode('year', () => `${new Date().getFullYear()}`); // Paired shortcode (with content) eleventyConfig.addPairedShortcode('callout', (content, type = 'info') => { return `<aside class="callout callout--${type}">${content}</aside>`; }); // Async shortcode (for fetching data) eleventyConfig.addAsyncShortcode('image', async (src, alt) => { // Image optimization logic here return `<img src="${src}" alt="${alt}" loading="lazy"/>`; });
{# Usage #} <p>Copyright {% year %}</p> {% callout "warning" %} This is a warning message. {% endcallout %} {% image "hero.jpg", "A descriptive alt text" %}
Permalinks
Control output URLs:
--- # Static permalink permalink: /custom-url/ # Dynamic permalink permalink: /blog/{{ page.date | date: '%Y/%m' }}/{{ page.fileSlug }}/ # Disable output (data-only file) permalink: false # Multiple outputs (feeds) permalink: - /feed.xml - /feed.json ---
RSS Feed
{# src/feed.njk #} --- permalink: /feed.xml eleventyExcludeFromCollections: true --- <?xml version="1.0" encoding="utf-8"?> <feed xmlns="http://www.w3.org/2005/Atom"> <title>{{ site.title }}</title> <link href="{{ site.url }}/feed.xml" rel="self"/> <link href="{{ site.url }}/"/> <updated>{{ collections.posts[0].date | dateFormat }}</updated> <id>{{ site.url }}/</id> <author> <name>{{ site.author.name }}</name> </author> {% for post in collections.posts | limit(10) %} <entry> <title>{{ post.data.title }}</title> <link href="{{ site.url }}{{ post.url }}"/> <updated>{{ post.date | dateFormat }}</updated> <id>{{ site.url }}{{ post.url }}</id> <content type="html">{{ post.content | escape }}</content> </entry> {% endfor %} </feed>
Deployment
Cloudflare Pages
# Build command npx @11ty/eleventy # Output directory _site
DigitalOcean App Platform
# .do/app.yaml name: my-eleventy-site static_sites: - name: web source_dir: / build_command: npm run build output_dir: _site
Checklist
Before deploying:
- Global data in
is complete_data/ - Collections are properly configured
- Permalinks generate correct URLs
- RSS feed validates
- Images have alt text
- Build succeeds:
npx @11ty/eleventy - Output is correct in
_site/
Related Skills
- xhtml-author - HTML patterns for templates
- markdown-author - Content authoring
- css-author - Styling patterns
- deployment - Cloudflare and DigitalOcean
- performance - Static site optimization