Going Beyond Static With Eleventy

Stephanie Eckles @5t3ph

11ty.Rocks • ModernCSS.dev • StyleStage.dev

Do you enjoy being a maker of the web?

Key Features

Key Features

100% Static Output

⚡️
zero boilerplate client-side JavaScript

Key Features

Developer-Mode Conveniences

⚡️
includes Browsersync for hot-reload

Key Features

Minimal Opinions, Highly Configurable

⚡️
only an `index` required, config as needed

.eleventy.js

Key Features

Minimal Opinions, Highly Configurable

.eleventy.js

customize input/output dir, add plugins, filters, and shortcodes

Key Features

10 Templating Languages

⚡️
templating not required, add in as needed

Key Features

10 Templating Languages

Key Features

Mix Templating Languages

  • Mix in the same file (ex. Nunjucks and Markdown)
  • Mix between layouts (Markdown file uses Nunjucks template)

Key Features

Layouts

special templates that can be used to wrap other content

---
title: Hello World
---

Welcome to my amazing 11ty website!
<!DOCTYPE html>
<html lang="en">
<head>
  <title>{{ title }}</title>
</head>
<body>
  {{ content | safe }}
</body>
</html>
index.md
base.njk
---
title: Hello World
---

Welcome to my amazing 11ty website!
<!DOCTYPE html>
<html lang="en">
<head>
  <title>{{ title }}</title>
</head>
<body>
  {{ content | safe }}
</body>
</html>

➡️

➡️

index.md
base.njk
---
title: News Post
layout: page.njk
---

A news posting.
<!DOCTYPE html>
<html lang="en">
<head>
  <title>{{ title }}</title>
</head>
<body>
  {{ content | safe }}
</body>
</html>

➡️

➡️

base.njk

Layout Chaining

---
layout: base.njk
---

<header>
  <h1>{{ title }}</h1>
</header>
<article>
  {{ content | safe }}
</article>
page.njk
---
title: Article
layout: page.njk
---

A blog article.

➡️

Key Features

Extended Templating

  • include other files as partials
  • shortcodes for reusable content
  • filters to manipulate content
11ty.dev/docs/config/

Key Features

Template Engine Overrides

override per file or in .eleventy.js

Key Features

Template Engine Overrides

---
templateEngineOverride: njk, md
---

## Featured Person

{% for name in people | randomPerson %}

- {{ name }}

{% endfor %}

Key Features

Custom
Data

included in `_data/`, created as JSON or JS modules

Key Features

Custom
Data

module.exports = [
  "Mary", 
  "Claire", 
  "Steph", 
  "Andy"
];
_data/people.js
## Featured Person

{% for name in people %}

- {{ name }}

{% endfor %}

➡️

Key Features

External Data

fetch and format for your needs

Key Features

External Data

const axios = require("axios");

module.exports = async () => {
  const result = await axios.get("https://aws.random.cat/meow");

  return result.data.file;
  // Ex: https:\/\/purr.objects-us-east-1.dream.io\/i\/image1-4.jpg
};

fetched at build, not client-side

Key Features

Content Management

create `collections` for related content types using `tags`

Key Features

Collections
From Tags

can be added in
front matter

---
tags: post
# or
tags: ['post', 'webdev']
---

Key Features

Collections
From Tags

can be defined in directory data files

{
  "layout": "post.njk",
  "tags": "posts"
}
posts/posts.json

Key Features

Collections
From Tags

{
  "layout": "post.njk",
  "tags": "posts"
}
posts/posts.json
{% for post in collections.post -%}
- [{{ post.data.title }}]({{ post.url }})
{% endfor %}
[any template]

Key Features

Feature Integration

use anything in the Node/JS ecosystem at build time

<footer>
  &copy; {% year %} Acme Co.
</footer>

➡️

footer.njk

Integration Example: Date from JavaScript

eleventyConfig.addShortcode("year", 
  () => `${new Date().getFullYear()}`
);
.eleventy.js

Key Features

Eleventy
Plugins

  • included via the config
  • provide shortcodes, filters
  • other 11ty-aware transforms

Eleventy Plugins

Eleventy Plugins

Key Features

Flexible
Output

use `permalink` to change the file type and output location

Markdown

Nunjucks

Liquid

+7

➡️

.html

Flexible Output Example: Site RSS Feed

---
permalink: "feed/feed.xml"
eleventyExcludeFromCollections: true
---
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title>{{ metadata.title }}</title>
	<subtitle>{{ metadata.feed.subtitle }}</subtitle>
	{% set absoluteUrl %}{{ metadata.feed.path | url | absoluteUrl(metadata.url) }}{% endset %}
	<link href="{{ absoluteUrl }}" rel="self"/>
	<link href="{{ metadata.url }}"/>
	<updated>{{ collections.posts | rssLastUpdatedDate }}</updated>
	<id>{{ metadata.feed.id }}</id>
	<author>
		<name>{{ metadata.author.name }}</name>
		<email>{{ metadata.author.email }}</email>
	</author>
	{%- for post in collections.posts | reverse %}
	{% set absolutePostUrl %}{{ post.url | url | absoluteUrl(metadata.url) }}{% endset %}
	<entry>
		<title>{{ post.data.title }}</title>
		<link href="{{ absolutePostUrl }}"/>
		<updated>{{ post.date | rssDate }}</updated>
		<id>{{ absolutePostUrl }}</id>
		<content type="html">{{ post.templateContent | htmlToAbsoluteUrls(absolutePostUrl) }}</content>
	</entry>
	{%- endfor %}
</feed>
feed/feed.njk
[
  {
    "title":"Page A",
    "slug":"page-a"
  },
  {
    "title":"Page B",
    "slug":"page-b"
  },
  {
    "title":"Page C",
    "slug":"page-c"
  }
]

➡️

functions/pages.json

Flexible Output Example: Page Data JSON

---
permalink: functions/pages.json
permalinkBypassOutputDir: true
eleventyExcludeFromCollections: true
---
[{% for pages in collections.pages %}
  {
    "title":"{{ pages.data.title | jsonTitle}}",
    "slug":"{{ pages.data.title | slug}}"
  }{% if forloop.last == false %},{% endif %}
{% endfor %}] 
pagejson.njk
---
title: Page A
---

A nice page

➡️

---
title: Page B
---

A lovely page
---
title: Page C
---

The best page

Key Features

Active, Passionate Community

Key Features

Eleventy Starters

Key Features

Committed Creator:
Zach Leatherman

@eleven_ty

Build an Eleventy Site in 3 Minutes

Do you enjoy being a maker of the web?

Going Beyond Static With Eleventy

aka "How to Have Fun As A Web Maker Again"

Style Stage - Non-CMS Community Contributions

Style Stage - Non-CMS Community Contributions

Style Stage - Non-CMS Community Contributions

  • Allows contributions w/o a CMS or auth setup
  • PRs deployed to own preview branch (Netlify)
  • GitHub provides auth and review interface

Style Stage - Non-CMS Community Contributions

Generators

  1. Produce an asset
  2. Provide an environment

Email Generator

➡️

Email Generator

eleventyConfig.addNunjucksAsyncFilter("emailHtml", (raw, callback) => {
  if (meta.environment === "prod") {
    callback(null, juice(raw));
  } else {
    callback(null, raw);
  }
});
.eleventy.js
/*
 * Sass styling
 */
---
subject: Hello New Subscriber!
heroImg: http://site.com/image.png
headline: Welcome to 11ty Rocks!
# ... and more
---

➡️

➡️

Web Component Generator

Web Component Generator

⚙️

Web Component Generator

aka "Exploiting layout functionality"

➡️

{%- set componentName = page.fileSlug %}
{%- set componentClass = page.fileSlug | createClass %}
{%- set componentScript %}components/{{componentName}}/script.js{% endset %}
{%- set componentCss %}components/{{componentName}}/style.css{% endset -%}

export class {{ componentClass }} extends HTMLElement {
    constructor() {
    super();
		
    this.attachShadow({ mode: "open" });
  }

  connectedCallback() {

    const { shadowRoot } = this;

    shadowRoot.innerHTML = `
      <style>
      * {
        box-sizing: border-box;
      }
      {% include componentCss %}
      </style>
      {{ content | safe}}
    `;
    {% include componentScript %}
  }
}

window.customElements.define("{{ componentName }}", {{ componentClass }});
_includes/component.njk
<h1 class="hello-world">
  Hello World
</h1>
const hw = shadowRoot.querySelector(".hello-world");

setTimeout(() => {
  hw.classList.add('hello-world__visible');
}, 1000);

.hello-world {
  color: var(--hw-color, white);
  opacity: 0;
  transition: opacity 180ms ease-in;
}

.hello-world__visible {
  opacity: 1;
}

components/hello-world.njk
_includes/components/hello-world/script.js
_includes/components/hello-world/style.css

CSS Houdini Worklet Generator

CSS Houdini Worklet Generator

🎩

CSS Houdini Worklet Generator

🎩

  • Requires a secure/local server
  • Worklets created with JS
  • Paint API = current best support

CSS Houdini Worklet Generator

🎩

Generator's Example Worklet

CSS Houdini Worklet Generator

🎩

class HoudiniExample {
  static get inputProperties() {
    return [
      "--houdiniExampleWidth",
      "--houdiniExampleHeight",
      "--houdiniExampleX",
      "--houdiniExampleY",
      "--houdiniExampleColor",
    ];
  }

  paint(ctx, size, properties) {
    const width =
      parseInt(properties.get("--houdiniExampleWidth")) || size.width;
    const height =
      parseInt(properties.get("--houdiniExampleHeight")) || size.height;
    const x = parseInt(properties.get("--houdiniExampleX")) || 0;
    const y = parseInt(properties.get("--houdiniExampleY")) || 0;
    const color = String(properties.get("--houdiniExampleColor")) || "purple";

    ctx.fillStyle = color;
    ctx.fillRect(x, y, width, height);
  }
}

registerPaint("houdiniExample", HoudiniExample);
worklet.js

CSS Houdini Worklet Generator

🎩

CSS Houdini Worklet Generator

CSS Houdini Worklet Generator

CSS Houdini Worklet Generator

CSS Houdini Worklet Generator

ButtonBuddy.dev

ButtonBuddy - 11ty + Parcel

💜

ButtonBuddy - 11ty + Parcel

`Static Site` !== `No Modern Client-Side JS`

Are you a little more excited to be a maker of the web?

learn new things

get feedback

grow your network

meet new awesome web makers

CSS Houdini Worklet Generator

CSS Houdini Worklet Generator

  • Replace existing, older static setup
  • Upgrade your playground
  • Exploit 11ty's features

One more thing!

Publish presentation slides/notes on 11ty.Rocks

11ty.rocks

11ty.rocks

Going Beyond Static With Eleventy

Stephanie Eckles @5t3ph

11ty.Rocks • ModernCSS.dev • StyleStage.dev