How I Made this Website
I am not qualified to teach you how to make a website. I am not a programmer or developer or anything like that. If you are making your first website, HTML For People by Blake Watson is the best place to start. After working through HTML for People, I found Eleventy. I'm using Eleventiy to generate this website. Before using Eleventy, I strongly recommend Zach Leatherman's great video 6 Minutes to Build a Blog from Scratch with Eleventy and Stephanie Eckles' fantastic class, Builld an Eleventy (11ty) Site from Scratch. Stephanie's website, 11ty Rocks!, well, rocks! The Learn Eleventy project is outstanding, even if their scope is quite a bit broader than what I needed. 11tybundle.dev shares the latest and greatest. Finally, but importantly, the folks at the 11ty Discord server have been welcoming, kind, and helpful - sometimes going out of their way to help me. I appreciate them very much.
This is what I did...
Installed Eleventy, Made Index Page
These are the prelimiary setps to set up Eleventy.
- Created a folder for the website (I called mine "Website").
- Opened the Website folder in VS Code.
- Confirmed that Node.js was installed with
node -v(v22.14.0 was installed). - Made
.eleventy.jsconfiguration file. Added code similar to what is used in the Learn Eleventy project:
module.exports = (eleventyConfig) => {
return {
dir: {
input: 'src',
output: 'public',
},
};
};
- Made
/srcfolder. - In
/srcmadeindex.mdwith Hello World placeholder. - Added
package.jsonwithnom init -y. - In
package.json, replaced test script with"start": "npx eleventy --serve". - Installed Eleventy with
npm install @11ty/eleventy. - Ran Eleventy with
npm start. This creates/publc. Checked http://localhost:8080.
Made a Base Template with CSS, Header, and Footer
This makes a template that will be used for most pages. This also adds CSS, a header, and a footer to all pages that use the template.
- Made folder
/src/css - Made
simple.cssfile and added Simple CSS from https://cdn.simplecss.org/simple.css. - In
.eleventy.jsadded a passthrough for CSS undermodule.exports, abovereturn:
// Passthroughs
eleventyConfig.addPassthroughCopy('./src/css/');
- Made folder
/src/_includes/layouts. - Made layout file
base.njkin/layoutsand added HTML5 boilerplate. - In the
<head></head>ofbase.njkin<title></title>replaced Document with Brian's Website. - In the
<body></body>ofbase.njk, added<header></header>,<main></main>, and<footer></footer>. - In
<main></main>added{{ content | safe }}.[1] - In
index.md, added front matter:
---
title: Home
layout: /layouts/base
---
- Made folder
/_includes/partials. - In
/partialsmadeheader.htmlwith<h1>Brian's Website</h1>. - In
/partialsmadefooter.htmlwith "Made with ☕ and ⌨️ by Brian" - Included
header.htmlandfooter.htmlinbase.njkwith{% include "partials/FILENAME.html" %}.
Added Navigation and This Page
This adds a navigation bar, shows the pattern for front matter that adds pages to the navigation bar. This is also where I crated this page (no point in having a navigation bar until I had two pages).
- In
/src, addedhowto.mdand copied/pasted my notes.[2] - Installed the Eleventy Navigation Plugin with
const eleventyNavigationPlugin = require("@11ty/eleventy-navigation");. - In
.eleventy.jsadded `` abovemodule.exportsand added the plugin abovereturn:
// Plugins
eleventyConfig.addPlugin(eleventyNavigationPlugin);
- In
/partials, madenavbar.htmland added code:[3]
<nav>{{ collections.all | eleventyNavigation | eleventyNavigationToHtml({ activeKey: eleventyNavigation.key, useAriaCurrentAttr: true }) | safe }}</nav>.
- In
index.mdfront matter, added:
eleventyNavigation:
key: Home
order: 1
- In
howto.mdfront matter, added:[4]
eleventyNavigation:
key: How To
order: 2
Add Date Filters
I wanted to make a blog, but displaying dates correctly in Eleventy is a common pitfall. To get around this, I used date filters. The first filter will make the date display correctly in ISO format (e.g. YYYY-MM-DD). The second filter will make the date display with the month spelled out (e.g. YYYY Month DD). I made the date filters first, and then the blog.
- In
.eleventy.js, abovemodule.exportsaddedconst { DateTime } = require("luxon"); - In
.eleventy.js, belowmodule.exportsbut abovereturn {added:
// Filters
eleventyConfig.addFilter("correctISO", (dateObj) => {
return DateTime.fromJSDate(dateObj, { zone: "utc"}).toFormat("yyyy-MM-dd");
});
eleventyConfig.addFilter("niceDate", (dateObj) => {
return DateTime.fromJSDate(dateObj, { zone: "utc"}).toFormat("dd MMMM yyyy");
});
Set Up the Blog
- Made folder
/src/blog. - In
/layoutsmadeposts.njkand copied content frombase.njk. - Edited
<main></main>inposts.njkas follows:
<main>
<h2>{{ title }}</h2>
<p><strong><time datetime="{{ date | correctISO }}">{{ date | niceDate }}</time></strong></p>
{{ content | safe }}
</main>
- Made file
blog.jsonin/blogand set layout and tags:
{
"layout": "/layouts/blogPost.njk",
"tags": "posts"
}
- Using a pattern that will repeat for all blog posts, in
/blog, made blog postYYYY-MM-DD-title-of-post.mdwith front matter:
title: The Title of the Post
date: YYYY-MM-DD
blurb: A blurb about the post.
tags:
- First Tag
- Second Tag
- Used Eleventy to make posts appear, newest to oldest, on
index.mdwith:
<h2>Blog</h2>
{% for posts in collections.posts reversed %}
**{{ posts.data.date | correctISO }} — [{{ posts.data.title }}]({{ posts.url }})**
_{{ posts.data.blurb }}_
{% endfor %}
- In
.eleventy.jsmade a filter to remove "posts" and "all" from the tags collection:
eleventyConfig.addFilter("filterTagList", function filterTagList(tags) {
return (tags || []).filter(tag => ["all", "posts"].indexOf(tag) === -1);
});
- In
.eleventy.jsmade another filter to sort tags into alphabetical order:
eleventyConfig.addFilter("sortTags", function(tags) {
if (!Array.isArray(tags)) return tags;
return tags
.filter(tag => typeof tag === "string") // Ensure only strings are processsed
.sort((a, b) => a.localeCompare(b));
});
- In
/srcmade the filetagpages.njk. Wrote front matter as follows:
---
pagination:
data: collections
size: 1
alias: tag
filter:
- posts
permalink: /tags/undefined/
layout: /layouts/base
---
- In
tagpages.njk, added a loop to display all posts with the specified tag:
<h2>Tagged: {{ tag }}</h2>
{% set taglist = collections[ tag ] %}
{% for post in taglist | reverse %}
<p>{{ post.data.date | correctISO }} — <a href="{{ post.url }}">{{ post.data.title }}</a><br>
<em>{{ post.data.blurb }}</em></p>
{% endfor %}
Made a Back-To-Top Button
This button stays invisable untill you scroll down the page. Then, it appears and takes you back to the top of the page.
- In
/partialsmadetopButton.htmlwith a version of code I found at W3Schools:
<!-- this positions the button and hides it until you scroll down -->
<style>
#topBtn {
display: none;
position: fixed;
bottom: 20px;
right: 30px;
z-index: 99;
}
</style>
<!-- this is the button -->
<button onclick="topFunction()" id="topBtn" title="Go to top">↑ Top</button>
<!-- this is the script that makes the button work -->
<script>
// Get the button
let mybutton = document.getElementById("topBtn");
// When the user scrolls down 20px from the top of the document, show the button
window.onscroll = function() {scrollFunction()};
function scrollFunction() {
if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
mybutton.style.display = "block";
} else {
mybutton.style.display = "none";
}
}
// When the user clicks on the button, scroll to the top of the document
function topFunction() {
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
}
</script>
- Added the back-to-top button to all pages by including it in the
<body></body>of thebase.njkandposts.njklayouts with{% include "partials/topButton.html" %}.
Made Images Work
- Made folder
/src/images. Images will go there. - In
.eleventy.js, added a new passthrough for images, just below the passthrough for CSS:[5]
eleventyConfig.addPassthroughCopy('./src/images/');
Homepage Adjustments
- Limited the number of posts displaying on the homepage to the three most recent by adding a collection to
.eleventy.jsand then editing the code in the homepage. The collection is:
eleventyConfig.addCollection("myPostsReverse", function (collectionsApi) {
return collectionsApi.getFilteredByTag("posts").reverse();
});
- and the edits to the homepage are to replace the prior blog display with this:
<h2>Recent Posts</h2>
{% for posts in collections.myPostsReverse limit: 3 %}
**{{ posts.data.date | correctISO }} — [{{ posts.data.title}}]({{ posts.url }})**<br>
_{{ posts.data.blurb }}_
{% endfor %}
- Added a list of blog post tags on the homepage by making a partial to list the tags, then using that partial in
base.njk(it won't work inindex.mdor any other Markdown file. For the partial, in/partialsmadeallPostTags.njk, which lists all post tags in alphabetical order with this code:
<!-- Get all the tags from posts, filter "all" an "posts", sort alphabetically -->
{% set allTags = [] %}
{% for post in collections.all %}
{% if post.data.tags %}
{% for tag in post.data.tags %}
{% if tag not in allTags %}
{% set allTags = allTags.concat([tag]) %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% for tag in allTags | filterTagList | sortTags %}
<a href="/tags/{{ tag | slugify }}/">{{ tag }}</a>{% if not loop.last %}, {% endif %}
{% endfor %}
- ... and in
base.njkadded in<main></main>:
<h2>🏷️ Tags</h2>
{% include "partials/allPostTags.njk" %}
Added an RSS Feed
- Followed the guide at the Eleventy RSS page, starting with installing the Eleventy's RSS plugin with
npm install @11ty/eleventy-plugin-rss. - In
.eleventy.jsadded a constant and a plugin to make a virtual template with this code:
<!--- This is the new constant -->
const { feedPlugin } = require("@11ty/eleventy-plugin-rss");
<!--- This goes with the filters previously added -->
eleventyConfig.addPlugin(feedPlugin, {
type: "atom", // or "rss", "json"
outputPath: "/feed.xml",
collection: {
name: "posts", // iterate over `collections.posts`
limit: 0, // 0 means no limit
},
metadata: {
language: "en",
title: "Brian's Website",
subtitle: "These are posts from Brian's Website.",
base: "https://brianjasonford.com",
author: {
name: "Brian Ford",
email: "", // Optional
}
}
});
- Ran
npm startto start Eleventy, which makesfeed.xmlin/public. - Added a link to the feed on the homepage in
homepage.njk. I did not do this inindex.mdbecause I wanted it at the bottom and editing the layout made that easier. This is the code:
<h2>📡 Feed</h2>
Subscribe: <a href="./feed.xml">RSS/Atom feed</a> foryour reader of choice.
- Edited the
posts.njklayout to also include a link to the feed at the bottom of every post.
Made an All Posts Page
- In
howto.mdchanged the front matter from navigation order 2 to 3. - In
/layouts, madeblog.njkand copiedbase.njkinto the new layout. - In
/srcmadeblog.mdwith front matter:
---
title: All Posts
layout: /layouts/blog
eleventyNavigation:
key: All Posts
order: 2
---
- Added all posts to
blog.njkwith this code:
<h2>✍️ All Posts</h2>
{% for posts in collections.myPostsReverse %}
<article>
<strong>{{ posts.data.date | correctISO }} — <a href="{{ posts.url }}">{{ posts.data.title }}</a></strong><br>
<em>{{ posts.data.blurb }}</em><br>
Tags: {% for tags in posts.data.tags | filterTagList | sortTags %}<a href="/tags/{{ tags | slug }}">{{ tags }}</a>{% if not loop.last %}, {% endif %}
{% endfor %}
</article>
{% endfor %}
That's it for the time being.
Footnotes[6]
-
To make code with curly brackets display correctly and not throw an error, but the code between
{% raw %}and{% endraw %}. ↩ - As I built this website, I took notes about what I was doing in Drafts, an app I love very much! ↩
-
This collects the EleventyNavigation keys and URLs (we are about to make those), and adds a function to correctly set
aria-current="page". Putting all of that in<nav></nav>makes everything display correctly. ↩ - This pattern will repeat in all new pages except for blog entries. ↩
-
To use an image, put the file in the `/images` folder and then add the image to whatever page you like using Markdown (e.g.
) or HTML (e.g.<img src="/images/FILENAME" alt="alt text">). I like HTML for this because it is easier to center images by placing the<img>within<p align="center"></p>.↩ -
I used HTML for these footnotes. It works like this: ↩
<!-- Main content section --> <p> This is the main body of the content. I have a footnote link for this line<sup id="ref-1"> <a href="#fn-1">[1]</a> </p> <!-- Footnotes Section --> <ol> <li id"fn-1"> Here is my first footnote.<a href="#ref-1" title="Jump back to the content">↩</a> </li> </ol>