├── src ├── src.json ├── components │ ├── hello-world.njk │ └── components.json ├── _includes │ ├── components │ │ └── hello-world │ │ │ ├── script.js │ │ │ └── style.css │ ├── page.njk │ └── component.njk ├── app.css └── index.njk ├── .gitignore ├── netlify.toml ├── .eleventy.js ├── package.json └── README.md /src/src.json: -------------------------------------------------------------------------------- 1 | { 2 | "layout": "page.njk" 3 | } 4 | -------------------------------------------------------------------------------- /src/components/hello-world.njk: -------------------------------------------------------------------------------- 1 |

Hello World

-------------------------------------------------------------------------------- /src/components/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "layout": "component.njk", 3 | "permalink": "/components/{{ page.fileSlug }}.js", 4 | "tags": "components" 5 | } 6 | -------------------------------------------------------------------------------- /src/_includes/components/hello-world/script.js: -------------------------------------------------------------------------------- 1 | const hw = shadowRoot.querySelector(".hello-world"); 2 | 3 | setTimeout(() => { 4 | hw.classList.add('hello-world__visible'); 5 | }, 1000); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies installed by npm 2 | node_modules 3 | 4 | # build artefacts 5 | public 6 | 7 | # secrets and errors 8 | .env 9 | .log 10 | 11 | # macOS related files 12 | .DS_Store -------------------------------------------------------------------------------- /src/_includes/components/hello-world/style.css: -------------------------------------------------------------------------------- 1 | .hello-world { 2 | background-color: var(--hw-bgcolor, rgb(25, 25, 155)); 3 | color: var(--hw-color, white); 4 | font-size: var(--hw-font-size, 3rem); 5 | text-align: var(--hw-text-align, center); 6 | opacity: 0; 7 | transition: opacity 180ms ease-in; 8 | } 9 | 10 | .hello-world__visible { 11 | opacity: 1; 12 | } 13 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # Directory to change to before starting a build. 3 | # This is where we will look for package.json/.nvmrc/etc. 4 | base = "" 5 | 6 | # Directory (relative to root of your repo) that contains the deploy-ready 7 | # HTML files and assets generated by the build. If a base directory has 8 | # been specified, include it in the publish directory path. 9 | publish = "public" 10 | 11 | # Default build command. 12 | command = "npm run build" -------------------------------------------------------------------------------- /src/_includes/page.njk: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{ title }} 7 | 8 | 9 | 10 |
11 |

{{ title }}

12 |
13 |
14 | {{ content | safe }} 15 |
16 | 17 | {%- if component %} 18 | {% set componentUrl %}/components/{{ component}}.js{% endset %} 19 | 20 | {% endif -%} 21 | 22 | -------------------------------------------------------------------------------- /.eleventy.js: -------------------------------------------------------------------------------- 1 | module.exports = function (eleventyConfig) { 2 | // Optional, used for the main application styles 3 | eleventyConfig.addWatchTarget("./src/app.css"); 4 | eleventyConfig.addPassthroughCopy("./src/app.css"); 5 | 6 | // Allows transforming ex. `hello-world` into `HelloWorld` 7 | // for the component `class` export 8 | eleventyConfig.addFilter("createClass", (str) => { 9 | if (!str) { 10 | return; 11 | } 12 | return str.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(''); 13 | }); 14 | 15 | return { 16 | dir: { 17 | input: "src", 18 | output: "public", 19 | }, 20 | }; 21 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "11ty-web-component-generator", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "eleventy --serve", 8 | "build": "eleventy" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/5t3ph/11ty-web-component-generator.git" 13 | }, 14 | "keywords": [ 15 | "11ty", 16 | "eleventy", 17 | "web components", 18 | "custom elements" 19 | ], 20 | "author": "5t3ph", 21 | "license": "ISC", 22 | "dependencies": { 23 | "@11ty/eleventy": "^0.11.0" 24 | }, 25 | "browserslist": [ 26 | "last 2 versions" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /src/_includes/component.njk: -------------------------------------------------------------------------------- 1 | {%- set componentName = page.fileSlug %} 2 | {%- set componentClass = page.fileSlug | createClass %} 3 | {%- set componentScript %}components/{{componentName}}/script.js{% endset %} 4 | {%- set componentCss %}components/{{componentName}}/style.css{% endset -%} 5 | 6 | export class {{ componentClass }} extends HTMLElement { 7 | constructor() { 8 | super(); 9 | 10 | this.attachShadow({ mode: "open" }); 11 | } 12 | 13 | connectedCallback() { 14 | 15 | const { shadowRoot } = this; 16 | 17 | shadowRoot.innerHTML = ` 18 | 24 | {{ content | safe}} 25 | `; 26 | {% include componentScript %} 27 | } 28 | } 29 | 30 | window.customElements.define("{{ componentName }}", {{ componentClass }}); -------------------------------------------------------------------------------- /src/app.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | font-family: system, -apple-system, ".SFNSText-Regular", "San Francisco", 7 | "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif; 8 | color: #222; 9 | width: 80ch; 10 | max-width: 100%; 11 | margin: 0 auto; 12 | padding: 0 1.25rem; 13 | line-height: 1.5; 14 | } 15 | 16 | h1, 17 | code, 18 | a { 19 | color: rgb(25, 25, 155); 20 | } 21 | 22 | .button { 23 | background-color: rgb(25, 25, 155); 24 | color: #fff; 25 | text-decoration: none; 26 | padding: 0.25em 0.5em; 27 | border-radius: 0.15em; 28 | font-size: 1.125rem; 29 | display: inline-flex; 30 | } 31 | 32 | h1 { 33 | line-height: 1; 34 | margin-left: -0.5rem; 35 | } 36 | 37 | small { 38 | color: #777; 39 | } 40 | 41 | code { 42 | font-weight: bold; 43 | font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", 44 | "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", 45 | "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, 46 | monospace; 47 | } 48 | 49 | pre { 50 | background-color: #f9f9f9; 51 | border: 1px solid #eee; 52 | border-radius: 4px; 53 | padding: 0.5em; 54 | overflow-x: auto; 55 | } 56 | 57 | pre, 58 | code { 59 | font-size: 0.9rem; 60 | } 61 | 62 | pre code { 63 | font-size: inherit; 64 | line-height: 1.3; 65 | } 66 | 67 | hello-world { 68 | /* Custom variables can pierce the shadow DOM to style web components */ 69 | /* --hw-color: yellow; */ 70 | } 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://repository-images.githubusercontent.com/303895742/105a8780-0da7-11eb-8c1f-7ca10b6e3713) 2 | 3 | # ⚡️ 11ty Web Component Generator 4 | 5 | > [View the demo Hello World component >](https://11ty-web-component-generator.netlify.app/) 6 | 7 | ## Using the Generator 8 | 9 | [Eleventy (11ty) is a static site generator](https://www.11ty.dev/docs/) that makes it possible to mix templating languages. More importantly to this generator is that we can customize the _output file type_ and composite a file from _includes_. 10 | 11 | The web components are generated within a Nunjucks (`.njk`) template that outputs the final `.js` file. 12 | 13 | This generator works with the inherent features of Eleventy, including that it expects templates and template partials to be placed in `_includes`. 14 | 15 | This leads to the following file structure to create a web component with styles and additional scripting. 16 | 17 | First, create the component template file in `src/components/[component-name].njk`. _It is important to kebab-case the file name_ 18 | 19 | Then within `_includes/components/` create: 20 | 21 | ```bash 22 | [component-name]/ 23 | script.js 24 | style.css 25 | ``` 26 | 27 | Keep the names of `script.js` and `style.css` so that the `_includes/component.njk` template can successfully include their contents to generate the web component. 28 | 29 | ## Using a Generated Web Component 30 | 31 | The final web component will be output within `public/components/[component-name].js` and is ready to be included in another project such as: 32 | 33 | ```html 34 | 35 | 36 | ``` 37 | 38 | ### Display a Component Within This Generator Project 39 | 40 | Create additional pages in this project directly within `src` as Nunjuck (`.njk`) files and add the following frontmatter in addition to any HTML and Nunjuck template tags. 41 | 42 | ```md 43 | title: Page Title 44 | component: component-name 45 | ``` 46 | 47 | Then the `page.njk` template will use the `component` value to include the relative path to the web component script. 48 | 49 | ## Project Scripts 50 | 51 | - **`npm start`** - run Eleventy on localhost with included Browsersync hot-reload 52 | - **`npm run build`** - run only Eleventy for creating a production build of the generator project 53 | 54 | ## Web Component Resources 55 | 56 | First a little disclaimer - I am brand new to web components, so I know this doesn't cover all the things you may want to do, or the best way to generically composite them. Submit a PR if you want to help extend this generator! 57 | 58 | Here are some resources that helped me put together [my first web component](https://github.com/5t3ph/css-webring): 59 | 60 | - [Encapsulating Style and Structure with Shadow DOM](https://css-tricks.com/encapsulating-style-and-structure-with-shadow-dom/) 61 | - [Using custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#Working_through_some_simple_examples) 62 | - [Creating a Custom Element from Scratch](https://css-tricks.com/creating-a-custom-element-from-scratch/) 63 | - [Custom Elements v1: Reusable Web Components](https://developers.google.com/web/fundamentals/web-components/customelements) 64 | 65 | ## 11ty Resources 66 | 67 | I'm very fond of 11ty, so here's a list of my other resources: 68 | 69 | **Learn to build an 11ty site in 20 mins** with my [egghead video course](https://5t3ph.dev/learn-11ty) and see how to add a blog and custom data. 70 | 71 | **Add auto-generated social media images** by following [my tutorial](https://dev.to/5t3ph/automated-social-sharing-images-with-puppeteer-11ty-and-netlify-22ln) 72 | 73 | **Explore advanced setup of custom data** through my [tutorial on building a community site](https://css-tricks.com/a-community-driven-site-with-eleventy-building-the-site/) 74 | 75 | **For a full-featured starter** check out my [11ty Netlify Jumpstart](https://11ty-netlify-jumpstart.netlify.app/) (also works for hosts other than Netlify). 76 | 77 | **For a featureless Sass starter** grab the template for my [11ty Sass Skeleton](https://github.com/5t3ph/11ty-sass-skeleton) 78 | -------------------------------------------------------------------------------- /src/index.njk: -------------------------------------------------------------------------------- 1 | --- 2 | title: ⚡️ 11ty Web Component Generator 3 | component: hello-world 4 | --- 5 | {# Using Nunjucks to avoid Markdown wrapping the web component in a paragraph tag #} 6 | 7 |

Made by Stephanie Eckles - @5t3ph

8 | 9 | Fork on GitHub 10 | 11 |

Demo Component: Hello World

12 | 13 |

This very basic component will display a "Hello World" banner 1 second after load.

14 | 15 | 16 | 17 |

Using the Generator

18 | 19 |

Eleventy (11ty) is a static site generator that makes it possible to mix templating languages. More importantly to this generator is that we can customize the output file type and composite a file from includes.

20 | 21 |

The web components are generated within a Nunjucks (.njk) template that outputs the final .js file.

22 | 23 |

This generator works with the inherent features of Eleventy, including that it expects templates and template partials to be placed in _includes.

24 | 25 |

This leads to the following file structure to create a web component with styles and additional scripting.

26 | 27 |

First, create the component template file in src/components/[component-name].njk. It is important to kebab-case the file name

28 | 29 |

Then within _includes/components/ create:

30 | 31 |
32 | 
33 | [component-name]/
34 |   script.js
35 |   style.css
36 | 
37 | 
38 | 39 |

Keep the names of script.js and style.css so that the _includes/component.njk template can successfully include their contents to generate the web component.

40 | 41 |

Using a Generated Web Component

42 | 43 |

The final web component will be output within public/components/[component-name].js and is ready to be included in another project such as:

44 | 45 |
46 | 	
47 | {{ '' }}
48 | {{ '' }}
49 | 	
50 | 
51 | 52 | Fork on GitHub 53 | 54 |

Display a Component Within This Generator Project

55 | 56 |

Create additional pages in this project directly within src as Nunjuck (.njk) files and add the following frontmatter in addition to any HTML and Nunjuck template tags.

57 | 58 |
59 | 	
60 | {{ 'title: Page Title' }}
61 | {{ 'component: component-name' }}
62 | 	
63 | 
64 | 65 |

Then the page.njk template will use the component value to include the relative path to the web component script.

66 | 67 |

Project Scripts

68 | 69 | 73 | 74 |

Web Component Resources

75 | 76 |

First a little disclaimer - I am brand new to web components, so I know this doesn't cover all the things you may want to do, or the best way to generically composite them. Submit a PR if you want to help extend this generator!

77 | 78 |

Here are some resources that helped me put together my first web component:

79 | 80 | 86 | 87 |

11ty Resources

88 | 89 |

I'm very fond of 11ty, so here's a list of my other resources:

90 | 91 |

Learn to build an 11ty site in 20 mins with my egghead video course and see how to add a blog and custom data.

92 | 93 |

Add auto-generated social media images by following my tutorial

94 | 95 |

Explore advanced setup of custom data through my tutorial on building a community site

96 | 97 |

For a full-featured starter check out my 11ty Netlify Jumpstart (also works for hosts other than Netlify).

98 | 99 |

For a featureless Sass starter grab the template for my 11ty Sass Skeleton

--------------------------------------------------------------------------------