Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
283 changes: 141 additions & 142 deletions .eleventy.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
require('dotenv').config();
require("dotenv").config();
const Webmentions = require("eleventy-plugin-webmentions");
const pluginRss = require("@11ty/eleventy-plugin-rss");
const Image = require("@11ty/eleventy-img");
Expand All @@ -16,161 +16,160 @@ const pluginShortCodes = require("./_config/shortcode.js");
* representing those same attribute-value pairs.
*/
const stringifyAttributes = (attributeMap) => {
return Object.entries(attributeMap)
.map(([attribute, value]) => {
if (typeof value === 'undefined') return '';
return `${attribute}="${value}"`;
})
.join(' ');
return Object.entries(attributeMap)
.map(([attribute, value]) => {
if (typeof value === "undefined") return "";
return `${attribute}="${value}"`;
})
.join(" ");
};

// shortcode for using 11ty's Image plugin
// from https://www.aleksandrhovhannisyan.com/blog/eleventy-image-plugin/
const imageShortcode = async (
src,
alt,
className = undefined,
widths = [400, 800, 1280],
formats = ['webp', 'jpeg'],
sizes = '100vw'
src,
alt,
className = undefined,
widths = [400, 800, 1280],
formats = ["webp", "jpeg"],
sizes = "100vw"
) => {
const imageMetadata = await Image(src, {
widths: [...widths, null],
formats: [...formats, null],
outputDir: '_site/media/images',
urlPath: '/media/images',
});
const sourceHtmlString = Object.values(imageMetadata)
// Map each format to the source HTML markup
.map((images) => {
// The first entry is representative of all the others
// since they each have the same shape
const { sourceType } = images[0];

// Use our util from earlier to make our lives easier
const sourceAttributes = stringifyAttributes({
type: sourceType,
// srcset needs to be a comma-separated attribute
srcset: images.map((image) => image.srcset).join(', '),
sizes,
});

// Return one <source> per format
return `<source ${sourceAttributes}>`;
})
.join('\n');

const getLargestImage = (format) => {
const images = imageMetadata[format];
return images[images.length - 1];
}

const largestUnoptimizedImg = getLargestImage(formats[0]);
const imgAttributes = stringifyAttributes({
src: largestUnoptimizedImg.url,
width: largestUnoptimizedImg.width,
height: largestUnoptimizedImg.height,
alt,
loading: 'lazy',
decoding: 'async',
});
const imgHtmlString = `<img ${imgAttributes}>`;

const pictureAttributes = stringifyAttributes({
class: className,
});
const picture = `<picture ${pictureAttributes}>
const imageMetadata = await Image(src, {
widths: [...widths, null],
formats: [...formats, null],
outputDir: "_site/media/images",
urlPath: "/media/images",
});
const sourceHtmlString = Object.values(imageMetadata)
// Map each format to the source HTML markup
.map((images) => {
// The first entry is representative of all the others
// since they each have the same shape
const { sourceType } = images[0];

// Use our util from earlier to make our lives easier
const sourceAttributes = stringifyAttributes({
type: sourceType,
// srcset needs to be a comma-separated attribute
srcset: images.map((image) => image.srcset).join(", "),
sizes,
});

// Return one <source> per format
return `<source ${sourceAttributes}>`;
})
.join("\n");

const getLargestImage = (format) => {
const images = imageMetadata[format];
return images[images.length - 1];
};

const largestUnoptimizedImg = getLargestImage(formats[0]);
const imgAttributes = stringifyAttributes({
src: largestUnoptimizedImg.url,
width: largestUnoptimizedImg.width,
height: largestUnoptimizedImg.height,
alt,
loading: "lazy",
decoding: "async",
});
const imgHtmlString = `<img ${imgAttributes}>`;

const pictureAttributes = stringifyAttributes({
class: className,
});
const picture = `<picture ${pictureAttributes}>
${sourceHtmlString}
${imgHtmlString}
</picture>`;

return outdent`${picture}`;
return outdent`${picture}`;
};

module.exports = function (eleventyConfig) {
// 11ty plugins
eleventyConfig.addPlugin(pluginRss);

// Only add Webmentions if token is provided
if (process.env.WEBMENTIONS_TOKEN) {
eleventyConfig.addPlugin(Webmentions, {
domain: "benkutil.com",
token: process.env.WEBMENTIONS_TOKEN
});
}

eleventyConfig.addPlugin(pluginNavigation);
eleventyConfig.addPlugin(pluginSyntaxHighlight, {
preAttributes: { tabindex: 0 }
// 11ty plugins
eleventyConfig.addPlugin(pluginRss);

// Only add Webmentions if token is provided
if (process.env.WEBMENTIONS_TOKEN) {
eleventyConfig.addPlugin(Webmentions, {
domain: "benkutil.com",
token: process.env.WEBMENTIONS_TOKEN,
});
eleventyConfig.addPlugin(pluginTOC, {
tags: ['h2', 'h3', 'h4', 'h5'],
ul: true,
flat: false,
wrapper: 'div'
}

eleventyConfig.addPlugin(pluginNavigation);
eleventyConfig.addPlugin(pluginSyntaxHighlight, {
preAttributes: { tabindex: 0 },
});
eleventyConfig.addPlugin(pluginTOC, {
tags: ["h2", "h3", "h4", "h5"],
ul: true,
flat: false,
wrapper: "div",
});

// Add Tufte filters and shortcodes
eleventyConfig.addPlugin(pluginFilters);
eleventyConfig.addPlugin(pluginShortCodes);

// 11ty shortcodes
eleventyConfig.addShortcode("image", imageShortcode);

// Configure markdown-it
const markdownIt = require("markdown-it");
let options = {
html: true,
breaks: true,
linkify: true,
typographer: true,
};
let markdownLib = markdownIt(options).use(markdownItAttrs);
eleventyConfig.setLibrary("md", markdownLib);

eleventyConfig.amendLibrary("md", (mdLib) => {
mdLib.use(markdownItAnchor, {
permalink: markdownItAnchor.permalink.ariaHidden({
placement: "after",
class: "header-anchor",
symbol: "",
ariaHidden: false,
}),
level: [1, 2, 3, 4],
slugify: eleventyConfig.getFilter("slugify"),
});

// Add Tufte filters and shortcodes
eleventyConfig.addPlugin(pluginFilters);
eleventyConfig.addPlugin(pluginShortCodes);

// 11ty shortcodes
eleventyConfig.addShortcode('image', imageShortcode);

// Configure markdown-it
const markdownIt = require("markdown-it");
let options = {
html: true,
breaks: true,
linkify: true,
typographer: true,
};
let markdownLib = markdownIt(options).use(markdownItAttrs);
eleventyConfig.setLibrary("md", markdownLib);

eleventyConfig.amendLibrary("md", mdLib => {
mdLib.use(markdownItAnchor, {
permalink: markdownItAnchor.permalink.ariaHidden({
placement: "after",
class: "header-anchor",
symbol: "",
ariaHidden: false,
}),
level: [1,2,3,4],
slugify: eleventyConfig.getFilter("slugify")
});
});

// Pass through Tufte CSS and fonts
eleventyConfig.addPassthroughCopy("src/css");
eleventyConfig.addPassthroughCopy("src/et-book");

// run these configs in production only
if (process.env.ELEVENTY_ENV === 'production') {
eleventyConfig.addTransform("htmlmin", function (content, outputPath) {
// find html files
if (outputPath && outputPath.endsWith(".html")) {
// configure html-minify
let minified = htmlmin.minify(content, {
useShortDoctype: true,
removeComments: true,
minifyCSS: true,
collapseWhitespace: true
});
return minified;
}

return content;
});

// Pass through Tufte CSS and fonts
eleventyConfig.addPassthroughCopy("src/css");
eleventyConfig.addPassthroughCopy("src/et-book");

// run these configs in production only
if (process.env.ELEVENTY_ENV === "production") {
eleventyConfig.addTransform("htmlmin", function (content, outputPath) {
// find html files
if (outputPath && outputPath.endsWith(".html")) {
// configure html-minify
let minified = htmlmin.minify(content, {
useShortDoctype: true,
removeComments: true,
minifyCSS: true,
collapseWhitespace: true,
});
return minified;
}

}

// Directory changes
return {
dir: {
input: 'src',
includes: '_includes',
data: '_data'
}
}
};
return content;
});
}

// Directory changes
return {
dir: {
input: "src",
includes: "_includes",
data: "_data",
},
};
};
21 changes: 12 additions & 9 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,39 @@
---
name: Bug Report
about: Report a problem with the website
title: ''
title: ""
labels: bug
assignees: ''
assignees: ""
---

## Context
<!-- What were you trying to do when you encountered this issue? -->

<!-- What were you trying to do when you encountered this issue? -->

## Current Behavior
<!-- What is actually happening? -->

<!-- What is actually happening? -->

## Expected Behavior
<!-- What should happen instead? -->

<!-- What should happen instead? -->

## Steps to Reproduce
1.
2.
3.

1.
2.
3.

## Definition of Done

<!-- How will we know this is fixed? -->

- [ ] Bug is identified and root cause understood
- [ ] Fix is implemented
- [ ] Site builds without errors
- [ ] Changes are tested locally
- [ ] No regressions introduced

## Additional Context
<!-- Browser, screenshots, error messages, etc. -->

<!-- Browser, screenshots, error messages, etc. -->
12 changes: 7 additions & 5 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
---
name: Feature Request
about: Suggest an idea or improvement for this website
title: ''
title: ""
labels: enhancement
assignees: ''
assignees: ""
---

## Context
<!-- Brief description of what you're trying to accomplish or the problem you're solving -->

<!-- Brief description of what you're trying to accomplish or the problem you're solving -->

## Proposed Solution
<!-- What changes would you like to see? -->

<!-- What changes would you like to see? -->

## Definition of Done

<!-- How will we know this is complete? Check all that apply or add your own -->

- [ ] Feature is implemented
- [ ] Changes are documented (if applicable)
- [ ] Site builds without errors
- [ ] Changes are tested locally
- [ ] Code follows simplicity principles

## Additional Context
<!-- Add any other context, screenshots, or examples -->

<!-- Add any other context, screenshots, or examples -->
Loading
Loading