npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@eleventy-plugin-themer/theme-base

v0.4.2

Published

A minimal convention-based Eleventy theme with self-describing metadata and extensible features

Readme

@eleventy-plugin-themer/theme-base

A blog theme for Eleventy built on @eleventy-plugin-themer/core. A port of eleventy-base-blog with dark mode, configurable styling, and extensible features.

Features

  • Cascade System - User files override theme files (layouts, data, assets, features)
  • @theme Alias - Clean imports in Nunjucks templates: {% extends "@theme/layouts/base.njk" %}
  • Dark Mode - Configurable light/dark toggle with system preference support
  • Extensible Features - Self-contained feature modules loaded per-page via front matter
  • CSS Custom Properties - Easy theming via variables for colors, typography, spacing
  • Configurable - Override theme defaults via theme.config.mjs

Installation

npm install @eleventy-plugin-themer/core @eleventy-plugin-themer/theme-base @11ty/eleventy-plugin-syntaxhighlight

With Vite build optimizations:

npm install -D @eleventy-plugin-themer/build-vite @11ty/eleventy-plugin-vite

Quick Start

// eleventy.config.mjs
import { eleventyPluginThemer } from '@eleventy-plugin-themer/core';

const THEME_NAME = '@eleventy-plugin-themer/theme-base';

export default async function (eleventyConfig) {
  const __dirname = path.dirname(fileURLToPath(import.meta.url));

  const { dir } = await eleventyPluginThemer(eleventyConfig, {
    theme: THEME_NAME,
    projectRoot: __dirname,
    input: 'content',
    output: '_site',
  });

  return { dir };
}

See the eleventy-starter repository for a complete working example.

Project Structure

your-project/
├── eleventy.config.mjs   # Theme + plugin configuration
├── content/              # Your content
│   ├── index.md
│   ├── posts/
│   └── _data/
│       ├── site.js       # Site metadata
│       └── theme.js      # Theme config overrides
├── overrides/            # Customizations
│   ├── layouts/          # Override/extend theme layouts
│   ├── features/         # Custom or overridden features
│   ├── scripts/
│   │   └── main.js       # Your JavaScript entry point
│   └── styles/           # Your styles
└── public/               # Static assets (favicon, etc.)

Theme Configuration

Override theme defaults by creating theme.config.mjs:

export default {
  themeToggle: {
    defaultTheme: 'auto', // 'auto', 'light', or 'dark'
    showToggle: true,
  },
  colors: {
    // Grayscale theme with a single reddish-plum accent. Links are grayscale and
    // light up to the accent on hover (linkHover defaults to the accent).
    light: {
      background: '#ffffff',
      primary: '#1a1a1a',
      accent: '#9b3b54',
      text: '#262626',
      link: '#1a1a1a',
      linkHover: '#9b3b54',
      linkVisited: '#595959',
    },
    dark: {
      background: '#171717',
      primary: '#f5f5f5',
      accent: '#9b3b54',
      text: '#e5e5e5',
      link: '#fafafa',
      linkHover: '#9b3b54',
      linkVisited: '#a3a3a3',
    },
  },
  typography: {
    fontFamily: '-apple-system, system-ui, sans-serif',
    fontFamilyHeading: 'inherit',
    fontFamilyMono: "'Consolas', 'Monaco', monospace",
  },
  logos: {
    default: '',
    dark: '',
    favicon: '/favicon.svg',
  },
  social: [],
  footer: {
    copyright: '{year} {site.title}', // supports {year} and {site.title}
    startYear: null, // e.g. 2024 -> renders a "2024–<current>" range
    alignment: 'center', // footer-bottom row: 'left' | 'center' | 'right'
    showPoweredBy: true, // "Built with Eleventy" line
    showGitSha: true, // short commit hash (see Build metadata below)
    gitHubRepo: '', // e.g. 'https://github.com/you/repo' -> links the commit
  },
};

All config values are deeply merged with the theme's defaults from theme.json. Use null to explicitly clear a value.

footer.alignment controls only the bottom row (copyright / "Built with" / commit hash); the footer navigation (menu + social links) layout is unaffected.

Customization

Override Layouts

Replace a theme layout:

{# overrides/layouts/post.njk - completely replaces theme's post.njk #}
<article>
  <h1>{{ title }}</h1>
  {{ content | safe }}
</article>

Extend a theme layout:

{# overrides/layouts/custom.njk #}
{% extends "@theme/layouts/base.njk" %}

{% block content %}
  <div class="custom">{{ content | safe }}</div>
{% endblock %}

Override Data

Create content/_data/site.mjs to replace theme's site data:

export default defineSiteData({
  title: 'My Blog',
  url: 'https://myblog.com',
  language: 'en',
  author: { name: 'Your Name', email: '[email protected]' },
  social: [{ platform: 'github', account: 'your-handle' }],
});

Override Static Assets

Place files in public/ to override theme assets:

public/
├── favicon.svg       # Overrides theme's favicon
└── logo.png          # Your custom asset

Features

Features are optional functionality modules loaded per-page via front matter.

Using Features

Add to any page's front matter:

---
title: My Post
feature: code-highlighting
---

Available Theme Features

code-highlighting

Syntax highlighting powered by PrismJS (via @11ty/eleventy-plugin-syntaxhighlight). Includes copy button, optional line numbers, diff highlighting, and custom scrollbar.

Usage — add to any page's front matter:

---
feature: code-highlighting
---

Configuration — override in theme.config.mjs:

export default {
  codeHighlighting: {
    prismTheme: 'prism-tomorrow', // PrismJS theme name (default)
    diffHighlight: true, // Include diff-highlight plugin (default)
  },
};

Available PrismJS themes:

| Theme | Style | | ---------------------- | --------------------------------------- | | prism | Light, minimal (Lea Verou's default) | | prism-coy | Light with subtle left border | | prism-solarizedlight | Light, Solarized palette | | prism-dark | Dark, muted tones | | prism-funky | Dark with coloured line backgrounds | | prism-okaidia | Dark, Monokai-inspired | | prism-tomorrow | Dark, Tomorrow Night Eighties (default) | | prism-twilight | Dark, warm greys |

Diff highlighting — use the diff- language prefix with +/- line markers:

```diff-js
+const added = true;
-const removed = false;
 const unchanged = null;
```

CSS custom properties — enhancements on top of PrismJS (override in your own CSS):

| Property | Default | Description | | --------------------------- | ------------------------ | ----------------------------- | | --code-border-radius | 0.5rem | Border radius for code blocks | | --code-copy-button-bg | var(--color-surface) | Copy button background | | --code-copy-button-fg | var(--color-text) | Copy button text colour | | --code-line-number-fg | rgb(255 255 255 / 30%) | Line number colour | | --code-line-number-width | 3rem | Line number column width | | --code-scrollbar-thumb-bg | rgb(255 255 255 / 20%) | Scrollbar thumb colour |

back-to-top

A floating button that appears once the page is scrolled past a threshold and smooth-scrolls back to the top. Fully self-contained (no template or data dependencies).

Usage — add to any page's front matter:

---
feature: back-to-top
---

Customization — import and call init() manually instead of using the auto-init variant:

import { init, defaultConfig } from '@theme/features/back-to-top/index.js';
init({ ...defaultConfig, threshold: 800 });

CSS custom properties (override in your own CSS):

| Property | Default | Description | | ---------------------- | ------------------------- | -------------------- | | --back-to-top-bg | var(--color-primary) | Button background | | --back-to-top-fg | var(--color-background) | Glyph colour | | --back-to-top-size | 2.75rem | Button width/height | | --back-to-top-offset | 1.5rem | Distance from corner |

Creating Custom Features

Create overrides/features/my-feature/index.js (or index.auto.js for auto-initialization):

// index.auto.js - auto-initializes when loaded
console.log('My custom feature loaded');

If both index.auto.js and index.js exist in the same directory, the plugin picks index.auto.js first. Use index.auto.js for self-running features and index.js for ones the consumer imports and initializes explicitly.

Then reference in front matter: feature: my-feature

Overriding Theme Features

Create overrides/features/code-highlighting/index.auto.js to replace the theme's version.

Shortcodes

Hero Section

{% hero title="Welcome", subtitle="Build beautiful websites", align="center", height="400px" %}
  {% heroButton url="/start", variant="primary" %}Get Started{% endheroButton %}
  {% heroButton url="/learn", variant="secondary" %}Learn More{% endheroButton %}
{% endhero %}

| Parameter | Description | Default | | ----------------- | ----------------------------------------- | ---------- | | title | Main heading | - | | subtitle | Secondary text | - | | background | Background image URL | - | | backgroundColor | Background color (fallback) | - | | align | Text alignment: left, center, right | 'center' | | height | Minimum height | 'auto' | | overlay | Dark overlay on background image | true |

Content Grid

{% contentGrid cols=3, gap="1.5rem" %}
  {% box title="Feature 1", link="/about", linkText="Learn More" %}
    Description of feature 1.
  {% endbox %}
{% endcontentGrid %}

Navigation

Uses @11ty/eleventy-navigation for menus. Add pages via front matter:

---
eleventyNavigation:
  key: About
  order: 2
---

Footer navigation uses parent: footer. Hierarchical pages get automatic breadcrumbs.

Build metadata

The theme provides a build global with the current git commit and a build timestamp, so the footer commit hash works without any setup on your side:

| Field | Example | | ------------------- | ---------------- | | build.gitSha | full commit SHA | | build.gitShaShort | 7-char short SHA | | build.timestamp | ISO build time |

It runs git rev-parse HEAD in your project at build time (falling back to unknown/dev outside a git repo). The footer shows build.gitShaShort when footer.showGitSha is enabled, linking to the commit if site.repository is set.

To override (e.g. inject a CI build number), provide your own content/_data/build.js — Eleventy's directory data takes precedence over the theme's global data.

Social links

Configure social links via the social array in content/_data/site.mjs (social is site data, not theme config). Each entry needs a platform plus either an account (expanded through a URL template) or a full url:

export default defineSiteData({
  social: [
    { platform: 'github', account: 'artislismanis' },
    { platform: 'mastodon', url: 'https://fosstodon.org/@you' },
    { platform: 'linkedin', account: 'your-handle', label: 'LinkedIn' },
  ],
});
  • account is expanded by core's socialUrl filter using the framework's SOCIAL_PLATFORMS table (github, x, linkedin, youtube, instagram, facebook, tiktok, discord, twitch, reddit, bluesky, mastodon). Themes may extend it via theme.json#socialPlatforms. Mastodon also accepts an @user@instance account.
  • url takes precedence over account and is validated by safeUrl (see Security helpers).
  • label is optional (used for the aria-label); it defaults to the capitalized platform name.

Icons

Icons are inline brand SVGs from simple-icons, rendered with fill="currentColor" so they inherit the link colour (grey, plum on hover). The platform value must match a simple-icons slug. Browse and search slugs at simpleicons.org — e.g. the "GitHub" tile has the slug github, "X" has x. The resolution order per link is:

  1. social.icon — a custom image path you provide (per-link override, wins over everything)
  2. the simple-icons brand SVG matched by slug
  3. a plain text label fallback when no icon is found

Special cases handled in lib/filters.mjs:

  • twitter is aliased to the x slug (the Twitter glyph was retired upstream), so legacy config keeps working.
  • A few brands have been removed from simple-icons at the owner's request (notably LinkedIn). The theme ships fallback glyphs for those in the SUPPLEMENTAL_ICON_PATHS map in lib/filters.mjs — add an entry there (keyed by slug, 24×24 path data) to cover any other removed brand, or override overrides/layouts/partials/social/icon.njk to fully customise rendering.

Security helpers

Nunjucks runs with autoescape: false in this framework, so the theme exports a set of escape filters that must be applied explicitly to any dynamic value:

| Filter | Use for | | ---------------- | ----------------------------------------------------------------------- | | escapeHtml | Text in HTML body context | | escapeAttr | Values inside ="…" attributes | | escapeCssValue | CSS custom-property values (strips quotes, brackets, ;, \, /* */) | | escapeJsString | Values inside JS string literals (escapes </script>, U+2028/9, etc.) | | safeUrl | Values used as href / src |

safeUrl() and the related socialUrl() filter use a strict scheme allowlist (http, https, mailto, tel, plus relative URLs). Everything else returns #. The filter also strips control / zero-width / bidi-override chars, rejects https:\/\/ backslash-authority forms, and blocks percent-encoded CR/LF in mailto: / tel: (header smuggling).

Related Packages

License

MIT