├── .gitignore ├── .vscode └── extensions.json ├── LICENSE ├── README.md ├── eleventy-helpers ├── shortcodes │ ├── inline-css.cjs │ └── inline-js.cjs └── transforms │ └── minify-html.cjs ├── eleventy.cjs ├── esbuild.config.mjs ├── package-lock.json ├── package.json ├── site ├── _includes │ ├── default.html │ └── layouts │ │ └── about.html ├── about │ ├── about.json │ ├── hiring.md │ └── index.md ├── css │ ├── global.css │ └── home-page.css └── index.html ├── src ├── components │ ├── my-counter.ts │ └── not-ssrd-component.ts ├── hydration-entrypoints │ ├── about │ │ └── index.ts │ └── index.ts ├── pages │ ├── about.ts │ └── home-page.ts ├── ssr-utils │ ├── 11ty-is-land.d.ts │ ├── dsd-polyfill.ts │ ├── is-land.ts │ └── lit-hydrate-support.ts └── ssr.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | _dev 2 | _prod 3 | node_modules 4 | .wireit 5 | /lib 6 | /build 7 | *.tsbuildinfo -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "runem.lit-plugin", 4 | "google.wireit" 5 | ] 6 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Elliott Marquez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 11ty and Lit and Typescript 2 | 3 | This repo is an example of using 11ty and Lit together with typecript. 4 | 5 | ## Technologies 6 | 7 | - [Lit](https://lit.dev) 8 | - [11ty](https://www.11ty.dev/) 9 | - [11ty-islands](https://github.com/11ty/is-land) 10 | - [@lit-labs/ssr](https://github.com/lit/lit/tree/main/packages/labs/ssr) 11 | - [@lit-labs/eleventy-plugin-lit](https://github.com/lit/lit/tree/main/packages/labs/eleventy-plugin-lit) 12 | - [Wireit](https://github.com/google/wireit) 13 | - [Typescript](https://www.typescriptlang.org/) 14 | 15 | ## Quickstart 16 | 17 | Dev mode: 18 | 19 | ```bash 20 | npm i && npm run dev 21 | ``` 22 | 23 | Prod mode: 24 | 25 | ```bash 26 | npm i && npm start 27 | ``` 28 | 29 | ## Explanation 30 | 31 | 11ty is a static site builder. Lit is a library that makes it easy to build Web Components. TypeScript is a strongly typed programming language that builds on JavaScript. In this example, we combine the three so that you can sprinkle interactivity in a static site with Lit. We also use 11ty islands to make it easy to SSR your components and control how they are hydrated to provide an "island of interactivity". 32 | 33 | 34 | ### Directory Structure 35 | 36 | 37 | #### `site` directory 38 | 39 | This directory holds your 11ty templates. See the 11ty docs for more info. 40 | 41 | 42 | #### `site/_includes` directory 43 | 44 | This is for templates global to the 11ty templating system. Here we have a `default.html` layout and a `layouts` directory for layouts that are specific to a page or set of pages. 45 | 46 | Make sure to include `default.html` in your layouts and in your 11ty `json` config files or simply extend them in nunjucks. 47 | 48 | 49 | #### `site/css` directory 50 | 51 | This holds your css files. Later, you may want to minify these. 52 | 53 | 54 | #### `site/fonts` and `site/images` directories 55 | 56 | These hold your fonts and images. They are empty right now, but they will be copied over to the `dist` directory. 57 | 58 | 59 | #### `site/*` (everything else) 60 | 61 | Most of these are for templating in 11ty. See the 11ty docs for more info. If you'd like to hook into the 11ty data system you may want to add a `_data` directory and consult the 11ty docs. 62 | 63 | 64 | #### `src/components` directory 65 | 66 | This directory holds your Lit components. If you add a component here, to hook it up to SSR, add it to the `src/ssr.ts` file and make sure to add it to `tsEntrypoints` in `esbuild.config.mjs` to make sure it's included in the bundler. If you don't want to SSR a component, you can add it to the appropriate file in `src/pages/.ts`; 67 | 68 | 69 | #### `src/hydration-entrypoints` 70 | 71 | This holds files that include 'import' notation to import the components used in server side rendering (SSR) and require hydration. The pages with .ts extension match the structure and name as those files in the `site` folder. 72 | 73 | For example, if you have a site\index.md file and you want to include a component named 'my-counter' in that file and possibly use SSR, then you would have a file in this folder named 'index.ts' (i.e. src/hydration-entrypoints/index.ts) with the following import: 74 | 75 | ``` 76 | import '../components/my-counter.js'; 77 | ``` 78 | 79 | 80 | #### `src/pages` directory 81 | 82 | This is a place to put logic for individual pages. If you want to add a component to a page, you can import it here and just use it in your templates. These files are automatically added to the build. When referencing the .ts components in this folder, you must use the javascript location defined in the eleventy.cjs file, which is set to '/js/*/[page name]'. 83 | 84 | 85 | #### `src/ssr.ts` file 86 | 87 | This loads the files onto the server for Server-side rendering. 11ty will then server-side render any components on the page. 88 | 89 | 90 | #### `esbuild.config.mjs` file 91 | 92 | This creates your bundles. It uses esbuild to bundle your components and your pages. You can add more entrypoints here if you want to add more entrypoints for hydration. The output of this will then be copied over to 11ty's appropriate output directory into the `/js` directory. e.g. dev mode will output `_dev/js/` and prod mode will output `_prod/js/`. 93 | 94 | 95 | #### `eleventy.cjs` file 96 | 97 | This is the configuration file for 11ty. Please consult the docs. 98 | 99 | 100 | #### `eleventy-helpers` directory 101 | 102 | This directory includes some files that modularize and organize the 11ty config file so that it does not become a giant mess of a file. 103 | -------------------------------------------------------------------------------- /eleventy-helpers/shortcodes/inline-css.cjs: -------------------------------------------------------------------------------- 1 | const CleanCSS = require('clean-css'); 2 | 3 | /** 4 | * Bundle, minify, and inline a CSS file. Path is relative to ./site/css/. 5 | * 6 | * In dev mode, instead import the CSS file directly. 7 | */ 8 | function inlineCSS(eleventyConfig, isDev) { 9 | eleventyConfig.addShortcode('inlinecss', (path) => { 10 | if (isDev) { 11 | return ``; 12 | } 13 | const result = new CleanCSS({ inline: ['local'] }).minify([ 14 | `./site/css/${path}`, 15 | ]); 16 | if (result.errors.length > 0 || result.warnings.length > 0) { 17 | throw new Error( 18 | `CleanCSS errors/warnings on file ${path}:\n\n${[ 19 | ...result.errors, 20 | ...result.warnings, 21 | ].join('\n')}` 22 | ); 23 | } 24 | return ``; 25 | }); 26 | } 27 | 28 | module.exports = inlineCSS; 29 | -------------------------------------------------------------------------------- /eleventy-helpers/shortcodes/inline-js.cjs: -------------------------------------------------------------------------------- 1 | const fsSync = require('fs'); 2 | 3 | /** 4 | * Inline the Rollup-bundled version of a JavaScript module. Path is relative 5 | * to ./lib or ./build. 6 | * 7 | * In dev mode, instead directly import the module, which has already been 8 | * symlinked directly to the TypeScript output directory. 9 | */ 10 | function inlineJS(eleventyConfig, isDev, {jsDir}) { 11 | eleventyConfig.addShortcode('inlinejs', (path) => { 12 | if (isDev) { 13 | return ``; 14 | } 15 | const script = fsSync.readFileSync(`${jsDir}/${path}`, 'utf8').trim(); 16 | return ``; 17 | }); 18 | } 19 | 20 | module.exports = inlineJS; 21 | -------------------------------------------------------------------------------- /eleventy-helpers/transforms/minify-html.cjs: -------------------------------------------------------------------------------- 1 | const htmlMinifier = require('html-minifier'); 2 | 3 | /** 4 | * Minifies HTML in production mode. 5 | */ 6 | function inlineJS(eleventyConfig, isDev) { 7 | // minify the html in Prod mode 8 | eleventyConfig.addTransform('htmlMinify', function (content, outputPath) { 9 | if (isDev || !outputPath.endsWith('.html')) { 10 | return content; 11 | } 12 | const minified = htmlMinifier.minify(content, { 13 | useShortDoctype: true, 14 | removeComments: true, 15 | collapseWhitespace: true, 16 | }); 17 | return minified; 18 | }); 19 | } 20 | 21 | module.exports = inlineJS; 22 | -------------------------------------------------------------------------------- /eleventy.cjs: -------------------------------------------------------------------------------- 1 | const litPlugin = require('@lit-labs/eleventy-plugin-lit'); 2 | const inlineCss = require('./eleventy-helpers/shortcodes/inline-css.cjs'); 3 | const inlineJS = require('./eleventy-helpers/shortcodes/inline-js.cjs'); 4 | const minifyHTML = require('./eleventy-helpers/transforms/minify-html.cjs'); 5 | 6 | // dev mode build 7 | const DEV = process.env.NODE_ENV === 'DEV'; 8 | // where the JS files are outputted 9 | const jsDir = DEV ? 'lib' : 'build'; 10 | // where to output 11ty output 11 | const outputFolder = DEV ? '_dev' : '_prod'; 12 | 13 | module.exports = function (eleventyConfig) { 14 | // copy folders to the 11ty output folder 15 | eleventyConfig 16 | .addPassthroughCopy({ [`${jsDir}/`]: 'js/' }) 17 | .addPassthroughCopy('site/css') 18 | .addPassthroughCopy('site/fonts') 19 | .addPassthroughCopy('site/images'); 20 | 21 | // add the lit-ssr plugin 22 | eleventyConfig.addPlugin(litPlugin, { 23 | mode: 'worker', 24 | componentModules: [`./${jsDir}/ssr.js`], 25 | }); 26 | 27 | // add this for 11ty's --watch flag. We don't use it in this example 28 | eleventyConfig.addWatchTarget(`./${jsDir}/**/*.js`); 29 | 30 | // install shortcodes 31 | inlineCss(eleventyConfig, DEV); 32 | inlineJS(eleventyConfig, DEV, { jsDir }); 33 | 34 | // install transforms 35 | minifyHTML(eleventyConfig, DEV); 36 | 37 | // set output folders and use nunjucks for html templating engine. see 38 | // nunjucks docs and 11ty docs for more info on nunjucks templating 39 | return { 40 | htmlTemplateEngine: 'njk', 41 | dir: { 42 | input: 'site', 43 | output: outputFolder, 44 | }, 45 | }; 46 | }; 47 | -------------------------------------------------------------------------------- /esbuild.config.mjs: -------------------------------------------------------------------------------- 1 | import esbuild from 'esbuild'; 2 | import gzipPlugin from '@luncheon/esbuild-plugin-gzip'; 3 | import { minifyHTMLLiteralsPlugin } from 'esbuild-plugin-minify-html-literals'; 4 | import tinyGlob from 'tiny-glob'; 5 | 6 | // dev mode build 7 | const DEV = process.env.NODE_ENV === 'DEV'; 8 | const jsFolder = DEV ? 'lib' : 'build'; 9 | 10 | // can use glob syntax. this will create a bundle for those specific files. 11 | // you want to add SSR'd files here so that you can hydrate them later with 12 | // 13 | const tsEntrypoints = [ 14 | // entrypoints for hydrating lit-islands 15 | './src/hydration-entrypoints/**/*.ts', 16 | // also include a bundle for each individual page 17 | './src/pages/*.ts', 18 | // SSR stuff 19 | './src/ssr-utils/lit-hydrate-support.ts', 20 | './src/ssr-utils/is-land.ts' 21 | ]; 22 | const filesPromises = tsEntrypoints.map(async (entry) => tinyGlob(entry)); 23 | const entryPoints = (await Promise.all(filesPromises)).flat(); 24 | 25 | let config = { 26 | bundle: true, 27 | outdir: jsFolder, 28 | minify: false, 29 | format: 'esm', 30 | treeShaking: true, 31 | write: true, 32 | sourcemap: true, 33 | splitting: true 34 | }; 35 | 36 | let componentsBuild = Promise.resolve(); 37 | 38 | // development build 39 | if (DEV) { 40 | componentsBuild = esbuild 41 | .build({ 42 | ...config, 43 | entryPoints, 44 | }) 45 | .catch(() => process.exit(1)); 46 | 47 | // production build 48 | } else { 49 | // config must be same for SSR and client builds to prevent hydration template 50 | // mismatches because we minify the templates in prod 51 | config = { 52 | bundle: true, 53 | outdir: jsFolder, 54 | minify: true, 55 | format: 'esm', 56 | treeShaking: true, 57 | legalComments: 'external', 58 | plugins: [ 59 | minifyHTMLLiteralsPlugin(), 60 | gzipPlugin({ 61 | gzip: true, 62 | }), 63 | ], 64 | write: false, 65 | splitting: true, 66 | }; 67 | 68 | componentsBuild = esbuild 69 | .build({ 70 | ...config, 71 | entryPoints, 72 | }) 73 | .catch(() => process.exit(1)); 74 | } 75 | 76 | // seperate build so that the SSR bundle doesn't affect bundling for the frontend 77 | const ssrBuild = esbuild 78 | .build({ 79 | ...config, 80 | format: 'iife', 81 | splitting: false, 82 | treeShaking: false, 83 | platform: 'node', 84 | entryPoints: ['src/ssr.ts'], 85 | }) 86 | .catch(() => process.exit(1)); 87 | 88 | // this code is inlined into the HTML because it is performance-sensitive 89 | const inlineBuild = esbuild 90 | .build({ 91 | ...config, 92 | format: 'iife', 93 | splitting: false, 94 | entryPoints: ['src/ssr-utils/dsd-polyfill.ts'], 95 | }) 96 | .catch(() => process.exit(1)); 97 | 98 | await Promise.all([componentsBuild, ssrBuild, inlineBuild]); 99 | 100 | process.exit(0); 101 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "11ty-lit", 3 | "version": "1.0.0", 4 | "description": "An example 11ty repo using Lit", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "npm run serve", 8 | "dev": "npm run serve:dev --watch", 9 | "serve": "wireit", 10 | "serve:dev": "wireit", 11 | "build:dev": "wireit", 12 | "build:dev:eleventy": "wireit", 13 | "build:dev:ts": "wireit", 14 | "build:type-check": "wireit", 15 | "build:prod": "wireit", 16 | "build:prod:eleventy": "wireit", 17 | "build:prod:ts": "wireit" 18 | }, 19 | "wireit": { 20 | "build:dev": { 21 | "dependencies": [ 22 | "build:dev:eleventy", 23 | "build:dev:ts", 24 | "build:type-check" 25 | ] 26 | }, 27 | "build:dev:eleventy": { 28 | "command": "NODE_ENV=DEV eleventy --config=eleventy.cjs", 29 | "files": [ 30 | "site/**/*", 31 | "lib/**/*", 32 | "eleventy.cjs" 33 | ], 34 | "output": [ 35 | "_dev" 36 | ], 37 | "dependencies": [ 38 | "build:dev:ts" 39 | ] 40 | }, 41 | "build:dev:ts": { 42 | "command": "NODE_ENV=DEV node esbuild.config.mjs", 43 | "files": [ 44 | "src/**/*", 45 | "tsconfig.json", 46 | "esbuild.config.mjs" 47 | ], 48 | "output": [ 49 | "lib/**/*" 50 | ] 51 | }, 52 | "build:type-check": { 53 | "command": "tsc -p tsconfig.json --pretty --incremental", 54 | "files": [ 55 | "src/**/*", 56 | "tsconfig.json" 57 | ], 58 | "clean": "if-file-deleted", 59 | "output": [ 60 | "tsconfig.tsbuildinfo" 61 | ] 62 | }, 63 | "build:prod": { 64 | "dependencies": [ 65 | "build:prod:eleventy", 66 | "build:prod:ts" 67 | ] 68 | }, 69 | "build:prod:eleventy": { 70 | "command": "NODE_ENV=PROD eleventy --config=eleventy.cjs", 71 | "files": [ 72 | "site/**/*", 73 | "build/**/*", 74 | "eleventy.cjs" 75 | ], 76 | "output": [ 77 | "_prod" 78 | ], 79 | "dependencies": [ 80 | "build:prod:ts" 81 | ] 82 | }, 83 | "build:prod:ts": { 84 | "command": "NODE_ENV=PROD node esbuild.config.mjs", 85 | "files": [ 86 | "src/**/*", 87 | "tsconfig.json", 88 | "esbuild.config.mjs" 89 | ], 90 | "output": [ 91 | "build/**/*" 92 | ] 93 | }, 94 | "serve:dev": { 95 | "command": "wds --watch --root-dir _dev --open", 96 | "service": true, 97 | "dependencies": [ 98 | { 99 | "script": "build:dev", 100 | "cascade": false 101 | } 102 | ] 103 | }, 104 | "serve": { 105 | "command": "npm run build:prod && wds --watch --root-dir _prod", 106 | "service": true, 107 | "dependencies": [ 108 | { 109 | "script": "build:prod", 110 | "cascade": false 111 | } 112 | ] 113 | } 114 | }, 115 | "repository": { 116 | "type": "git", 117 | "url": "git+https://github.com/e111077/11ty-lit.git" 118 | }, 119 | "keywords": [ 120 | "11ty", 121 | "eleventy", 122 | "lit", 123 | "lit-element", 124 | "SSR" 125 | ], 126 | "author": "", 127 | "license": "MIT", 128 | "bugs": { 129 | "url": "https://github.com/e111077/11ty-lit/issues" 130 | }, 131 | "homepage": "https://github.com/e111077/11ty-lit#readme", 132 | "dependencies": { 133 | "@11ty/is-land": "^4.0.0", 134 | "@lit-labs/ssr-client": "^1.1.7", 135 | "@material/web": "^1.3.0", 136 | "lit": "^3.1.2" 137 | }, 138 | "devDependencies": { 139 | "@11ty/eleventy": "^2.0.1", 140 | "@lit-labs/eleventy-plugin-lit": "^1.0.3", 141 | "@luncheon/esbuild-plugin-gzip": "^0.1.0", 142 | "@web/dev-server": "^0.4.3", 143 | "@webcomponents/template-shadowroot": "^0.2.1", 144 | "clean-css": "^5.3.3", 145 | "esbuild": "^0.19.12", 146 | "esbuild-plugin-minify-html-literals": "^1.0.1", 147 | "html-minifier": "^4.0.0", 148 | "tiny-glob": "^0.2.9", 149 | "typescript": "^5.4.2", 150 | "wireit": "^0.14.4" 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /site/_includes/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {% if title %}{{ title }}{% endif %} 9 | 10 | 11 | {% inlinecss "global.css" %} 12 | 13 | 14 | 15 | 16 | 17 | {% inlinejs "dsd-polyfill.js" %} 18 | 19 | 20 | 21 | {% block head %}{% endblock %} 22 | 23 | 24 | 25 |
26 | 27 | {% block content %}{{ content | safe }}{% endblock %} 28 |
29 | 30 | -------------------------------------------------------------------------------- /site/_includes/layouts/about.html: -------------------------------------------------------------------------------- 1 | 2 | {% extends 'default.html' %} 3 | 4 | {% block head %} 5 | 6 | {% endblock %} 7 | 8 | {% block content %}{{ content | safe }}{% endblock %} -------------------------------------------------------------------------------- /site/about/about.json: -------------------------------------------------------------------------------- 1 | { 2 | "layout": "layouts/about.html" 3 | } -------------------------------------------------------------------------------- /site/about/hiring.md: -------------------------------------------------------------------------------- 1 | # Hiring 2 | 3 | This page uses the layout in `site/about/about.json`. 4 | This uses the same layout as the `/about` page 5 | 6 | [back to home page](/) 7 | 8 | [to about page](/about) -------------------------------------------------------------------------------- /site/about/index.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | This page uses the layout in `site/about/about.json`. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | [back to home page](/) 12 | 13 | [to hiring page](/about/hiring) -------------------------------------------------------------------------------- /site/css/global.css: -------------------------------------------------------------------------------- 1 | /* This is for global styles! loaded in site/_includes/default.html */ 2 | 3 | :root { 4 | --primary-color: #006e18; 5 | --on-primary-color: #ffffff; 6 | --secondary-container: #d6e8ce; 7 | --on-secondary-container: #111f0f; 8 | } 9 | 10 | body { 11 | padding: 0; 12 | } 13 | 14 | [dsd-pending] { 15 | display: none; 16 | } -------------------------------------------------------------------------------- /site/css/home-page.css: -------------------------------------------------------------------------------- 1 | /* loaded in site/index.html */ 2 | 3 | :not(:defined) { 4 | border: 1px solid red; 5 | } -------------------------------------------------------------------------------- /site/index.html: -------------------------------------------------------------------------------- 1 | 2 | {% extends 'default.html' %} 3 | 4 | {% block head %} 5 | 6 | 7 | {% endblock %} 8 | 9 | {% block content %} 10 |

Home

11 |

Home page content

12 |

13 | The red border means the component is not hydrated. Hover it so it can hydrate 14 |

15 | 16 | 17 | 18 | 19 | 20 | 21 | About 22 | 23 | {% endblock %} -------------------------------------------------------------------------------- /src/components/my-counter.ts: -------------------------------------------------------------------------------- 1 | import { html, LitElement, css } from 'lit'; 2 | import { customElement, property } from 'lit/decorators.js'; 3 | import '@material/web/button/filled-button.js'; 4 | 5 | @customElement('my-counter') 6 | export class MyCounter extends LitElement { 7 | @property({ type: Number }) count = 0; 8 | 9 | render() { 10 | 11 | return html` 12 | this.count++} 13 | >Increment 15 |
Count: ${this.count}
16 | `; 17 | } 18 | 19 | static styles = css` 20 | :host { 21 | display: block; 22 | --md-sys-color-primary: var(--primary-color); 23 | --md-sys-color-on-primary: var(--on-primary-color); 24 | } 25 | `; 26 | } 27 | 28 | declare global { 29 | interface HTMLElementTagNameMap { 30 | 'my-counter': MyCounter; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/components/not-ssrd-component.ts: -------------------------------------------------------------------------------- 1 | import { css, html, LitElement } from 'lit'; 2 | import { customElement, property } from 'lit/decorators.js'; 3 | import '@material/web/button/filled-tonal-button.js'; 4 | 5 | // md-filled-tonal-button is SSR-able, but this is an example on how 6 | // to use a component that you do not want to run through SSR. 7 | 8 | @customElement('not-ssrd-component') 9 | export class NotSsrdComponent extends LitElement { 10 | @property({ type: Number }) count = 0; 11 | 12 | render() { 13 | return html` 14 | this.count++} 15 | >Increment 17 |
Count: ${this.count}
18 | `; 19 | } 20 | 21 | static styles = css` 22 | :host { 23 | display: block; 24 | --md-sys-color-secondary-container: var(--secondary-container); 25 | --md-sys-color-on-secondary-container: var(--on-secondary-container); 26 | } 27 | `; 28 | } 29 | 30 | declare global { 31 | interface HTMLElementTagNameMap { 32 | 'not-ssrd-component': NotSsrdComponent; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/hydration-entrypoints/about/index.ts: -------------------------------------------------------------------------------- 1 | // You can make new or reuse these files, but making a file that follows the 2 | // page page layout is a pattern that I personally like. 3 | 4 | import '../../components/my-counter.js'; 5 | -------------------------------------------------------------------------------- /src/hydration-entrypoints/index.ts: -------------------------------------------------------------------------------- 1 | // This directory and its subdirectories create a new entrypoint for each file. 2 | // This means that you should generally point your s to these files. 3 | 4 | import '../components/my-counter.js'; 5 | -------------------------------------------------------------------------------- /src/pages/about.ts: -------------------------------------------------------------------------------- 1 | // this is used the /site/_includes/layouts/about.html layouts 2 | 3 | import '../components/not-ssrd-component.js'; -------------------------------------------------------------------------------- /src/pages/home-page.ts: -------------------------------------------------------------------------------- 1 | // this is used the /site/index.html 2 | 3 | import '../components/not-ssrd-component.js'; 4 | 5 | console.log('this has stuff for your home page'); 6 | -------------------------------------------------------------------------------- /src/ssr-utils/11ty-is-land.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@11ty/is-land' { 2 | export class Island extends HTMLElement { 3 | forceFallback(): void; 4 | connectedCallback(): void; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/ssr-utils/dsd-polyfill.ts: -------------------------------------------------------------------------------- 1 | // declarative shadow dom polyfill 2 | 3 | import {hydrateShadowRoots} from '@webcomponents/template-shadowroot/template-shadowroot.js'; 4 | 5 | if (!HTMLTemplateElement.prototype.hasOwnProperty('shadowRoot')) { 6 | hydrateShadowRoots(document.body); 7 | } 8 | document.body.removeAttribute('dsd-pending'); -------------------------------------------------------------------------------- /src/ssr-utils/is-land.ts: -------------------------------------------------------------------------------- 1 | import { Island } from '@11ty/is-land'; 2 | import '@11ty/is-land/is-land-autoinit.js'; 3 | 4 | customElements.define( 5 | 'lit-island', 6 | class extends Island { 7 | // Removes the feature in which 11ty island removes DOM to render a fallback. 8 | override forceFallback() {} 9 | } 10 | ); 11 | -------------------------------------------------------------------------------- /src/ssr-utils/lit-hydrate-support.ts: -------------------------------------------------------------------------------- 1 | // must be loaded before any lit element see /site/_includes/default.html for 2 | // usage. Allows hydrating SSRd lit elements. 3 | import '@lit-labs/ssr-client/lit-element-hydrate-support.js'; 4 | -------------------------------------------------------------------------------- /src/ssr.ts: -------------------------------------------------------------------------------- 1 | // this file imports only files that will be SSRd e.g. if you can't SSR a 2 | // component, don't import it here. 3 | import './components/my-counter.js'; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "es2022", 4 | "lib": ["es2022", "dom", "dom.iterable"], 5 | "declaration": true, 6 | "strict": true, 7 | "noUnusedLocals": true, 8 | "noUnusedParameters": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "moduleResolution": "node", 12 | "allowSyntheticDefaultImports": true, 13 | "experimentalDecorators": true, 14 | "useDefineForClassFields": false, 15 | "forceConsistentCasingInFileNames": true, 16 | "noEmit": true, 17 | }, 18 | "include": ["src/**/*"], 19 | "exclude": [] 20 | } --------------------------------------------------------------------------------