Always Twisted

Adding Random Content Using Eleventy

Since I've had a rejuvenation in writing again after I finally pushed this sites redesign live I have had a link component at the bottom of every article stating that I have availabilty for new client work.

Fast forward ~9 weeks from the website going live and 15 articles later I thought it would be neat to have a beter 'call out' at the bottom of my articles. A "question and answer" that could show potential cliens how I can help them. Because I have ended up, and will carry on, writing on a variety of topics I thought it would be even cooler if I had targetted "Q and As" depending on the topic of the article.

What I cam up with was 24 different questions and answers for Design Systems, CSS, Design Tokens and Front-End Development. The idea would be that every time my site gets rebuild each article page would have one of these different "Q and As" generated at random.

Using Eleventy I have distilled that code to help you add a random horror movie quote to your website pages (using Eleventy).

A purple box with text: Need to integrate design tokens into your build tools or component libraries? I'll integrate tokens into your build process and component libraries for smooth workflows. With a lighter purple button that has text of 'get in touch' to the right.

What We'll Build

For this article we will build a component that:

Setting Up The Quotes

First, we will need to create a .json file to store our film quotes. We can add a new file (film-quotes.json) in the _data folder of your Eleventy project and populate it with your favourite film qoutes. I'm a big horror fan, so for me it would look somehing like this:

Code languagejson
{
"quotes": [
{
"quote": "They're here.",
"film": "Poltergeist"
},
{
"quote": "Do you like scary movies?",
"film": "Scream"
},
{
"quote": "Whatever you do, don’t fall asleep.",
"film": "A Nightmare on Elm Street"
},
{
"quote": "Be afraid… Be very afraid.",
"film": "The Fly"
},
{
"quote": "It’s alive! It’s alive!",
"film": "Frankenstein"
},
{
"quote": "Here’s Johnny!",
"film": "The Shining"
},
{
"quote": "We all go a little mad sometimes.",
"film": "Psycho"
},
{
"quote": "I see dead people.",
"film": "The Sixth Sense"
},
{
"quote": "You’re gonna need a bigger boat.",
"film": "Jaws"
},
{
"quote": "In space, no one can hear you scream.",
"film": "Alien"
}
]
}

Creating The Randomiser

Next, we’ll create a shortcode to fetch a random quote from our JSON file.

This shortcode will pick a single random quote during the build process, ensuring that the selected quote changes only when the site is rebuilt. The setup is simple, using the Math.random() function to select a quote from the list.

Add the following code to your .eleventy.js file:

Code languagejavascript
const fs = require("fs");
const path = require("path");
module.exports = function (eleventyConfig) {
// Load film quotes
const filmQuotes = require("./website/_data/film-quotes.json");
// Register random film quote shortcode
config.addShortcode("randomFilmQuote", () => {
const randomIndex = Math.floor(Math.random() * filmQuotes.quotes.length);
const randomQuote = filmQuotes.quotes[randomIndex];
// Return the quote directly
return `
<blockquote>
${randomQuote.quote}
<cite>— ${randomQuote.film}</cite>
</blockquote>
`;
});
};

Using The Shortcode In Your Template

In your Nunjucks template (or any Eleventy compatible emplate), use the randomFilmQuote shortcode wherever you want the quote to appear.

<div class="quote-of-the-day"> <h2>Film Quote of the Day</h2> {% randomFilmQuote %} </div>

Updating the Randomiser to Use a Nunjucks Component

I don't know about you, but I prefer to have things as 'close to the metal' as possible, so let's pull the HTML generated in the shortcode out and put it into a Nunjucks component that can be called instead.

Moving the HTML into a Nunjucks Component

Create a Nunjucks component template in your project. Let’s call it film-quotes.njk, and place it in your components folder (src/incldues/components/film[quotes.njk):

{% set randomQuote = randomFilmQuote() %} <blockquote>   {{ randomQuote.quote }} <cite>— {{ randomQuote.film }}</cite> </blockquote>

Updating the Shortcode to Use The Component

As we're removing the HTML from he shortcode we will need to update it so that it can render the Nunjucks component instead of directly generating the HTML. To do this, we can use Eleventy's built-in Nunucks rendering

Code languagejavascript
const fs = require("fs");
const path = require("path");
module.exports = function (config) {
// Load film quotes
const filmQuotes = require("./website/_data/film-quotes.json");
// Register random film quote shortcode
config.addShortcode("randomFilmQuote", () => {
const randomIndex = Math.floor(Math.random() * filmQuotes.quotes.length);
return filmQuotes.quotes[randomIndex];
});
// Expose the shortcode for Nunjucks
config.addNunjucksGlobal(
"randomFilmQuote",
() => {
const randomIndex = Math.floor(Math.random() * filmQuotes.quotes.length);
return filmQuotes.quotes[randomIndex];
}
);
};

Updating the Template to Use The Shortcode

With this update to the Shortcode we don't need to make any changes to our template

<div class="quote-of-the-day"> <h2>Film Quote of the Day</h2> {% randomFilmQuote %} </div>

Creating choices With Sets Of Quotes

With my sites component I mentioned I have 4 categories that can either be chosen per page or per component instance or left to be completely random. Let's see what we need to do to that if we have quotes from horror movies and sci-fi movies.

Updating the .json

Let's split the quotes out into two categories (I've shortened the .json for this example):

Code languagejson
{
"horror": [
{
"quote": "They're here.",
"film": "Poltergeist"
},
{
"quote": "Do you like scary movies?",
"film": "Scream"
}
],
"scifi": [
{
"quote": "In space, no one can hear you scream.",
"film": "Alien"
},
{
"quote": "The Force will be with you. Always.",
"film": "Star Wars"
}
]
}

Now we need to add a bunch more JavaScript to our shortcode to allow for the multiple categories and the option to choose which category or not:

Code languagejavascript
const fs = require("fs");
const path = require("path");
module.exports = function (config) {
// Load film quotes
const filmQuotes = JSON.parse(
fs.readFileSync(path.resolve(__dirname, "website/_data/film-quotes.json"))
);
// Register random film quote shortcode
config.addShortcode("randomFilmQuote", (category = null) => {
let selectedQuotes;
if (category) {
selectedQuotes = filmQuotes[category];
if (!selectedQuotes) {
throw new Error(
`Category '${category}' not found. Available categories: ${Object.keys(
filmQuotes
).join(", ")}`
);
}
} else {
// Use all quotes if no category specified
selectedQuotes = Object.values(filmQuotes).flat();
}
const randomIndex = Math.floor(Math.random() * selectedQuotes.length);
return selectedQuotes[randomIndex];
});
// Expose the shortcode for Nunjucks
config.addNunjucksGlobal(
"randomFilmQuote",
(category = null) => {
let selectedQuotes;
if (category) {
selectedQuotes = filmQuotes[category];
if (!selectedQuotes) {
throw new Error(
`Category '${category}' not found. Available categories: ${Object.keys(
filmQuotes
).join(", ")}`
);
}
} else {
selectedQuotes = Object.values(filmQuotes).flat();
}
const randomIndex = Math.floor(Math.random() * selectedQuotes.length);
return selectedQuotes[randomIndex];
}
);
};

Now we can add an if statement to our nunjucks to see if there is a category in the frontmatter

{% if category %} {% set randomQuote = randomFilmQuote(category) %} {% else %} {% set randomQuote = randomFilmQuote(null) %} {% endif %} <blockquote class="film-quote"> {{ randomQuote.quote }} <cite>— {{ randomQuote.film }}</cite> </blockquote>

If we set the category in the frontmatter of the page it will then pick a random quote from that category

--- quoteCategory: "scifi" ---
a light orange box with a heading of Film Quote of the Day - The Force will be with you. Always. - Star Wars

The approach we've looked at in this article could be applied to a wide variety of uses cases beyond film quotes, or questions and answers. You could use it to display a random testimonial, rotate customer feedback on your landing pages. Alternatively you could implement it to showcase random facts or trivia. Or, like I do, you could use this to power targeted calls to action, displaying relevant messages to specific blogposts.

Is your CSS architecture scalable enough to support multiple teams and projects?

I can restructure your CSS for better scalability and maintainability.

get in touch!