I really wanted a code demo component for this site so I could create HTML, CSS, and JavaScript code demos. But I didn't want to use CodePen. As good as it is, I really wanted an in-house solution.
So I made my own static code demo component like CodePen, but for this Eleventy site! Here it is in action:
<div>
<h1 id="heading">This is my static code demo!</h1>
<p>Super fancy, right?</p>
<button onclick="makeHeadingFancy()">Press Me!</button>
</div>
body {
font-family: sans-serif;
}
h1 {
margin-top: 0;
font-size: 1.5rem;
}
.fancy {
background-color: #9BEE00;
}
function makeHeadingFancy() {
document.getElementById("heading").classList.add("fancy");
}
The remainder of this article will go over how I made this and all things that should be considered when adding this to your own site.
Personally, I use Eleventy to generate my sites, but these steps should be similar for other static site generators.
CodePen is what I've noticed sites normally use. And I have nothing against it—in fact, I love CodePen.
To figure out if I wanted an in-house solution or not, I imagined the advantages for going with either option:
Make our own | CodePen |
---|---|
No third-party scripts | Easy to implement |
Control over aesthetics | Familiar to users |
This table looks pretty neck and neck. So really, it depends on what you value most. For me, I wanted to save on third-party scripts.
For 99% of my projects, I would probably use CodePen. But for this site, I am developing it with a Privacy-first approach. And third-party scripts usually track your visitors. And even if they don't when you install them, they could down the line.
I really want this site to be super clean with no need for Cookie popups or Privacy Policies.
This is what led me to design my own static code demo component for Eleventy.
I had a few ideas on how to design this system. I thought shortcodes might be the play, but I ultimately decided on the following approach by 11ty Rocks:
- We'll create a
demo.njk
template inside of our Eleventy-specific_includes/
folder - Wherever we want to add a code demo, we define and set three variables for the HTML, CSS, and JavaScript
- We then
include
ourdemo.njk
template so it inherits our HTML, CSS, and JavaScript variables
I'll be using Nunjucks, but its about the same process for other templating engines.
Here's an example:
{% set demo_html %}
<h1 id="heading">HTML Code</h1>
{% endset %}
{% set demo_css %}
.fancy {
color: #FF0000;
}
{% endset %}
{% set demo_js %}
function changeColor() {
document.getElementById("heading").classList.add("fancy");
}
{% endset %}
{% include "components/demo.njk" }
There are just a few things to note:
- These HTML, CSS, and JavaScript variables must be reset for any subsequent use of our code demo component. Otherwise, it would take on the values that the previous code demo used.
- If for whatever reason we don't need to set a certain variable, such as if we don't need any JavaScript code in the demo, we should set its variable to
false
so that it doesn't take on any previously set values. - Changing the folder path to
demo.njk
will result in all instances of this code breaking, but a careful find and replace should be enough to fix it.
One important HTML element that makes all this possible is the <iframe>
element. It not only allows us to embed other HTML pages, but we can also embed HTML code with the srcdoc
attribute. This allows us to render our HTML, CSS, and JavaScript in a safe environment that won't affect the rest of the page.
Here's a possible code demo component for our demo.njk
template:
<div class="demo">
{% if demo_html %}<div>{% highlight "html" %}{{ demo_html | safe }}{% endhighlight %}</div>{% endif %}
{% if demo_css %}<div>{% highlight "css" %}{{ demo_css | safe }}{% endhighlight %}</div>{% endif %}
{% if demo_js %}<div>{% highlight "js" %}{{ demo_js | safe }}{% endhighlight %}</div>{% endif %}
<iframe srcdoc="{% if demo_css %}<style>{{ demo_css }}</style>{% endif %}{{ demo_html }}{% if demo_js %}<script>{{ demo_js }}</script>{% endif %}"></iframe>
</div>
First things first: we're going to need the Eleventy Syntax Highlighting plugin in order to get the {% highlight %}
shortcode.
This is necessary when displaying our HTML, CSS, and JavaScript code outside the <iframe>
. This shortcode converts our code to safe strings, such as by replacing the <
and >
characters in HTML with their respective HTML entities. It also has the nice bonus of allowing us to preview our code with Prism's syntax highlighting!
We also need to apply the | safe
filter to our variables outside the <iframe>
. Otherwise, characters like <
and >
will appear as <
and >
.
Now, we can include
this template using Nunjucks anywhere we need. For example:
{% include "demo.njk" %}
But don't forget to set our HTML, CSS, and JavaScript variables before it:
{% set demo_html %}
<h1 id="heading">HTML Code</h1>
{% endset %}
{% set demo_css %}
.fancy {
color: #FF0000;
}
{% endset %}
{% set demo_js %}
function changeColor() {
document.getElementById("heading").classList.add("fancy");
}
{% endset %}
{% include "demo.njk" %}
And remember, if we don't want to include a certain variable, we need to set it to false
. So if we want to include HTML and CSS but not JavaScript:
{% set demo_html %}
<h1 id="heading">HTML Code</h1>
{% endset %}
{% set demo_css %}
.fancy {
color: #FF0000;
}
{% endset %}
{% set demo_js = false %}
{% include "demo.njk" }
You may have noticed my code demo example at the start of this article as an option to expand it. What this does is open the output code in a new tab so it can use the entire width of the site.
This is useful because keeping the code contained inside a small <iframe>
can be limited at times, so allowing your users to open your code in a new tab can be useful.
Here's how I did it:
- I saved the value of the
srcdoc
attribute tolocalStorage
- I then loaded a specific code demo page with a bigger
<iframe>
and loaded the value we saved tolocalStorage
into that<iframe>
'ssrcdoc
attribute
I considered using URL parameters but couldn't quite get them working. For small bits of data, they probably work fine, but I couldn't get them to work when sending large code snippets.
This kind of sucks. If I got this working with URL parameters, then if the user shares the page link, it'd load the same code demo they had.
I did get around this somewhat by saving the article URL to a URL parameter so that if the user does share the link, I can direct any new visitors back to the article where they got the code demo from.
And there you go! There's the basics for a simple static code demo component for Eleventy.
You can also add your own CSS and JavaScript to make it match the style of your site. But if you plan on doing that, here's some things to note:
- For CSS: styling the
<iframe>
element can be fun. I recommend setting a width of 100% and scale the height to fit the container height, with amin-height
in case the container is too small. - For JavaScript: make sure your functions that mess with certain elements are relative. For example, if you want a button to toggle on/off a
<div>
, don't rely on element IDs, because there could be many code demos on a single page. I instead used anonclick="someFunction(this)"
function call on my buttons and used combinations of nextSibling and previousSibling to toggle on/off the element I wanted.