Eleventy Post Graph

Generate Github-like post distribution graph for your blog posts in Eleventy

Blog post

npm
Used by 16 people
2023

Jan
Feb
Mar
Apr
May
Jun
Jul
Aug
Sep
Oct
Nov
Dec

Usage

Install the package

npm install @rknightuk/eleventy-plugin-post-graph --save-dev

In your Eleventy config:

const postGraph = require('@rknightuk/eleventy-plugin-post-graph')

module.exports = (eleventyConfig, options = {}) => {
    eleventyConfig.addPlugin(postGraph)
}

In your template, use the shortcode and pass it your posts collection:

{% postGraph collections.posts %}

You can also override the config by passing in the options directly:

{% postGraph collections.posts, { "highlightColor": "#e33d94" } %}

Note: The plugin looks for either post.data.date (from the frontmatter) or post.date (automatically from the file itself) to get the date of the post.

Customisation

Any of these options can be passed in directly to the shortcode, or set in the config.

Light and Dark Mode

By default, the plugin uses :root/@media (prefers-color-scheme: dark) to handle themes, but if you want use a class instead, you can pass in selectors for light and dark mode:

eleventyConfig.addPlugin(postGraph, {
    darkModeSelector: '.dark',
    lightModeSelector: '.light',
})

Colors

You can customise box, highlight, and text colors for both light and dark mode:

eleventyConfig.addPlugin(postGraph, {
    boxColorLight: '#e9ecef',
    highlightColorLight: '#69db7c',
    textColorLight: '#000',

    boxColorDark: '#2d333b',
    highlightColorDark: '#69db7c',
    textColorDark: '#fff',
})

If you want the same colors for both modes, you can pass any of the following and these will override the light/dark options:

eleventyConfig.addPlugin(postGraph, {
    boxColor: '#2d333b',
    highlightColor: '#e33d94',
    textColor: '#fff',
})

This plugin uses standard CSS (no css-in-js-on-the-blockchain here) so you can also pass CSS variables:

eleventyConfig.addPlugin(postGraph, {
    boxColor: 'var(--background)',
    highlightColor: 'var(--primary)',
    textColor: 'var(--text)',
})

Link to years

By default, the year labels are not links. If you want to link to a page with all the posts for that year, you can pass in yearLink and {{year}} will be replaced with the year:

eleventyConfig.addPlugin(postGraph, {
    yearLink: '/posts/{{year}}',
})

Only include specific years

If this is left empty, it will show all years.

eleventyConfig.addPlugin(postGraph, {
    only: [2022],
})

Limit to a certain amount of years

eleventyConfig.addPlugin(postGraph, {
    limit: 2, // only show 2 years worth of posts
})

Sorting

Pass sort to set the order (default: asc):

eleventyConfig.addPlugin(postGraph, {
    sort: 'desc',
})

Hide labels

Hide the year and month labels:

eleventyConfig.addPlugin(postGraph, {
    noLabels: true,
})

Show dates on highlighted box hover

Adds a title element to each highlighted box with the date of the post. The default display is Aug 6, 2024, but this can be updated using Moment's formatting options:

eleventyConfig.addPlugin(postGraph, {
    dayBoxTitle: true,
    dayBoxTitleFormat: 'MMM D, YYYY',
})

Customise styles

Pass in noStyles: true to do all the styling yourself. Note: If you have a CSP that blocks inline css, you will need to include the styles yourself.

eleventyConfig.addPlugin(postGraph, {
    noStyles: true,
})

The full CSS:

:root {
    --epg-box: #e9ecef;
    --epg-box-highlight: #69db7c;
    --epg-text: #000;
}

@media (prefers-color-scheme: dark) { 
    :root {
        --epg-box: #2d333b;
        --epg-box-highlight: #69db7c;
        --epg-text: #fff;
    }
}

.epg {
    color: var(--text);
    margin: 20px 0;
    font-size: 0.8em;
}

.epg__year {
    text-align: center;
    font-weight: bold;
    margin-bottom: 10px;
}

.epg__months {
    display: flex;
    justify-content: space-between;
    margin-bottom: 10px;
}

@media (max-width: 410px) {
    .epg__months {
        display: none;
    }
}

.epg__squares {
    display: grid;
    grid-template-rows: repeat(7, 1fr);
    grid-auto-flow: column;
    margin-bottom: 10px;
    grid-gap: 2px;
}

.epg__box {
    aspect-ratio: 1 / 1;
    background: var(--epg-box);
}

.epg__box--empty {
    background: none;
}

.epg__hasPost {
    background: var(--epg-box-highlight);
}

Advanced Usage

Multiple graphs with different styles on one page

If you try to render two graphs on one page, the second graph's CSS will override the first so they both look the same. To get around this, you can pass in a prefix:

{% postGraph collections.posts %}

{% postGraph collections.posts, { "highlightColor": "#e33d94", "prefix": "pinkone" } %}

Classnames and variables will be pinkone-epg instead of epg on the second one:

2023

Jan
Feb
Mar
Apr
May
Jun
Jul
Aug
Sep
Oct
Nov
Dec

2023

Jan
Feb
Mar
Apr
May
Jun
Jul
Aug
Sep
Oct
Nov
Dec

Custom Data

"I have an unusual 11ty setup and I can't just pass in a collection, please help?".

Good news! You can pass in your own data and the plugin will use that instead. The format of the data should have an object with years as keys, and each year should have an object with days and offset.

The counts object has a key which is the year plus the day of the year and the number of posts.

const data = {
    years: {
        2023: {
            days: 365,
            offset: 6
        },
    },
    counts: {
        '2023-1': 1,
        '2023-5': 3,
        // and so on
    },
}

Pass null as the collection, and pass your data to the plugin:

{% postGraph null, { "data": myDataObject } %}