├── .gitignore ├── .editorconfig ├── .eleventyignore ├── _includes ├── post-layout.njk └── base-layout.njk ├── posts └── 2019 │ ├── blog-post-2.md │ └── blog-post-1.md ├── index.njk ├── About.md ├── README.md ├── package.json ├── tests └── .eleventy.test.js ├── LICENSE ├── .eleventy.js └── css └── site.css /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # _site is where the static generated files are copied to 3 | _site/ 4 | 5 | node_modules/ 6 | .idea/ 7 | 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | end_of_line = crlf 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 -------------------------------------------------------------------------------- /.eleventyignore: -------------------------------------------------------------------------------- 1 | # We don't want Eleventy to include the README.md as a website content file 2 | README.md 3 | 4 | # Unit tests should be ignored by Eleventy 5 | tests/ 6 | -------------------------------------------------------------------------------- /_includes/post-layout.njk: -------------------------------------------------------------------------------- 1 | --- 2 | layout: base-layout.njk 3 | --- 4 |
5 |

{{ title }}

6 | 7 | {{ content | safe }} 8 |
-------------------------------------------------------------------------------- /posts/2019/blog-post-2.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post-layout.njk 3 | title: How To Get Rich Quick 4 | date: 2019-06-01 5 | tags: ['post'] 6 | --- 7 | 8 | 9 | Buy the latest and greatest cryptocurrency that no-one has heard of. 10 | 11 | 12 | 13 | ## Legal Stuff 14 | We do not accept any liability for any loss or damage. -------------------------------------------------------------------------------- /index.njk: -------------------------------------------------------------------------------- 1 | --- 2 | layout: base-layout.njk 3 | pagination: 4 | data: collections.post 5 | size: 10 6 | reverse: true 7 | alias: posts 8 | --- 9 | {% for post in posts %} 10 |
11 | 12 |

13 | {{ post.data.title }} 14 |

15 | 16 | 17 | 18 | {% excerpt post %} 19 | 20 | Read more 21 | 22 |
23 | {% endfor %} -------------------------------------------------------------------------------- /posts/2019/blog-post-1.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post-layout.njk 3 | title: This Is My First Ever Post 4 | date: 2019-05-30 5 | tags: ['post'] 6 | --- 7 | 8 | 9 | This is the first post on my blog. 10 | 11 | 12 | 13 | Eleventy is super fresh init. 14 | 15 | 16 | ## Want your own blog? 17 | The code for this blog is available on GitHub: 18 | [https://github.com/JonUK/eleventy-blog](https://github.com/JonUK/eleventy-blog) 19 | 20 | 21 | -------------------------------------------------------------------------------- /About.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: base-layout.njk 3 | --- 4 | # About this blog 5 | 6 | This blog was created using the Static Site Generator [Eleventy](https://www.11ty.io/) (aka 11ty). 7 | New posts can quickly and easily be added to this blog just by adding new Markdown files. 8 | 9 | The following article details how this blog was created. 10 | 11 | ## Creating A Blog With Eleventy 12 | [https://keepinguptodate.com/pages/2019/06/creating-blog-with-eleventy/](https://keepinguptodate.com/pages/2019/06/creating-blog-with-eleventy/) 13 | 14 | 15 | -------------------------------------------------------------------------------- /_includes/base-layout.njk: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Eleventy Blog 7 | 8 | 9 | 10 | 11 | 12 |
13 | My Blog 14 | About 15 |
16 |
17 | {{ content | safe }} 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # eleventy-blog 2 | An example blog site using Eleventy that covers fundamental functionality. 3 | 4 | The following article accompanies this repo. 5 | 6 | ## Creating A Blog With Eleventy 7 | [https://keepinguptodate.com/pages/2019/06/creating-blog-with-eleventy/](https://keepinguptodate.com/pages/2019/06/creating-blog-with-eleventy/) 8 | 9 | A demo of the blog is hosted on Netlify: 10 | [https://dazzling-almeida-ca0492.netlify.com/](https://dazzling-almeida-ca0492.netlify.com/) 11 | 12 | ## Branches 13 | This repo contains several branches that allow you to checkout the code at various stages of development. 14 | 15 | ## How do I run the site? 16 | ``` 17 | npm install 18 | npm run serve 19 | ``` 20 | 21 | Then access the site with the URL [http://localhost:8080/](http://localhost:8080/) (your port may vary). -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eleventy-blog", 3 | "version": "1.1.0", 4 | "description": "An example blog site using Eleventy that covers fundamental functionality", 5 | "scripts": { 6 | "build": "npx eleventy", 7 | "serve": "npx eleventy --serve", 8 | "debug": "DEBUG=* npx eleventy", 9 | "test": "jest" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/JonUK/eleventy-blog.git" 14 | }, 15 | "author": "Jon Keeping", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/JonUK/eleventy-blog/issues" 19 | }, 20 | "homepage": "https://github.com/JonUK/eleventy-blog#readme", 21 | "devDependencies": { 22 | "@11ty/eleventy": "^0.12.1", 23 | "jest": "^26.6.3", 24 | "moment": "^2.29.1" 25 | }, 26 | "dependencies": { 27 | "@jest/globals": "latest" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/.eleventy.test.js: -------------------------------------------------------------------------------- 1 | const eleventyConfig = require('@11ty/eleventy/src/EleventyConfig'); 2 | const eleventy = require('../.eleventy.js'); 3 | 4 | let dateReadableFunc; 5 | 6 | beforeAll(() => { 7 | // Populate the config object with all the filters & shortcodes 8 | eleventy(eleventyConfig); 9 | 10 | dateReadableFunc = eleventyConfig.nunjucksFilters['dateReadable']; 11 | }); 12 | 13 | describe('dateReadable filter', () => { 14 | 15 | it.each([ 16 | ['2019-06-01T00:00:00.000Z', 'June 1, 2019'], 17 | ['2019-06-02T00:00:00.000Z', 'June 2, 2019'], 18 | ['2019-07-01T00:00:00.000Z', 'July 1, 2019'], 19 | ['2020-01-01T00:00:00.000Z', 'January 1, 2020'], 20 | ['2020-01-01T01:00:00.000Z', 'January 1, 2020'], 21 | ['2020-01-01T23:59:59.000Z', 'January 1, 2020'], 22 | ['2020-12-31T23:59:59.000Z', 'December 31, 2020'] 23 | ])('can parse %s to %s', (inputString, expected) => { 24 | const outputDateString = dateReadableFunc(inputString); 25 | expect(outputDateString).toEqual(expected); 26 | }); 27 | 28 | }); 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jon Keeping 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 | -------------------------------------------------------------------------------- /.eleventy.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment'); 2 | 3 | moment.locale('en'); 4 | 5 | module.exports = function (eleventyConfig) { 6 | 7 | eleventyConfig.addFilter('dateIso', date => { 8 | return moment(date).toISOString(); 9 | }); 10 | 11 | eleventyConfig.addFilter('dateReadable', date => { 12 | return moment(date).utc().format('LL'); // E.g. May 31, 2019 13 | }); 14 | 15 | eleventyConfig.addShortcode('excerpt', article => extractExcerpt(article)); 16 | 17 | // Folders to copy to output folder 18 | eleventyConfig.addPassthroughCopy("css"); 19 | }; 20 | 21 | function extractExcerpt(article) { 22 | if (!article.hasOwnProperty('templateContent')) { 23 | console.warn('Failed to extract excerpt: Document has no property "templateContent".'); 24 | return null; 25 | } 26 | 27 | let excerpt = null; 28 | const content = article.templateContent; 29 | 30 | // The start and end separators to try and match to extract the excerpt 31 | const separatorsList = [ 32 | { start: '', end: '' }, 33 | { start: '

', end: '

' } 34 | ]; 35 | 36 | separatorsList.some(separators => { 37 | const startPosition = content.indexOf(separators.start); 38 | 39 | // This end position could use "lastIndexOf" to return all the paragraphs rather than just the first 40 | // paragraph when matching is on "

" and "

". 41 | const endPosition = content.indexOf(separators.end); 42 | 43 | if (startPosition !== -1 && endPosition !== -1) { 44 | excerpt = content.substring(startPosition + separators.start.length, endPosition).trim(); 45 | return true; // Exit out of array loop on first match 46 | } 47 | }); 48 | 49 | return excerpt; 50 | } 51 | -------------------------------------------------------------------------------- /css/site.css: -------------------------------------------------------------------------------- 1 | * { 2 | -webkit-font-smoothing: antialiased; 3 | -moz-osx-font-smoothing: grayscale; 4 | } 5 | 6 | body { 7 | margin: 0; 8 | padding: 10px 8px; 9 | font-size: 19px; 10 | font-family: 'Roboto', sans-serif; 11 | } 12 | 13 | @media screen and (min-width: 768px) { 14 | body { 15 | max-width: 768px; 16 | margin: 0 auto; 17 | } 18 | } 19 | 20 | header { 21 | display: flex; 22 | align-items: center; /* Vertically align */ 23 | padding: 5px 16px; 24 | color: white; 25 | background-color: #0747A6; 26 | border-radius: 8px; 27 | } 28 | 29 | header > a:link, 30 | header > a:visited, 31 | header > a:hover { 32 | color: white; 33 | text-decoration: none; 34 | } 35 | 36 | header > a:hover { 37 | text-decoration: underline; 38 | } 39 | 40 | .link--home { 41 | flex-grow: 1; 42 | font-size: 2em; 43 | } 44 | 45 | .link--about { 46 | flex-grow: 1; 47 | font-size: 1em; 48 | text-align: right; 49 | } 50 | 51 | main { 52 | padding: 5px 16px; 53 | word-wrap:break-word; 54 | } 55 | 56 | footer { 57 | padding: 35px 0 25px 0; 58 | text-align: center; 59 | font-size: 0.8em; 60 | color: #666666; 61 | } 62 | 63 | h1, 64 | h2 { 65 | font-family: 'Roboto Slab', sans-serif; 66 | margin: 25px 0 2px 0; 67 | } 68 | 69 | h1 { 70 | font-size: 2em; 71 | } 72 | 73 | h1 > a:link, 74 | h1 > a:visited { 75 | text-decoration: none; 76 | } 77 | 78 | h1 > a:hover { 79 | color: #20399daa; 80 | text-decoration: underline; 81 | } 82 | 83 | h2 { 84 | font-size: 1.5em; 85 | } 86 | 87 | a:link, 88 | a:visited { 89 | color: #20399d; 90 | } 91 | 92 | a:hover { 93 | color: #20399daa; 94 | } 95 | 96 | time { 97 | color: #666666; 98 | } 99 | 100 | article img { 101 | width: 100%; 102 | height: auto; 103 | } --------------------------------------------------------------------------------