├── .eleventy.js
├── .eleventyignore
├── .github
├── actions
│ └── setup
│ │ ├── Dockerfile
│ │ └── entrypoint.sh
└── workflows
│ └── deploy-to-github-pages.yml
├── .gitignore
├── 404.md
├── README.md
├── _11ty
└── getTagList.js
├── _data
└── metadata.json
├── _includes
├── base.njk
├── home.njk
├── post.njk
└── postslist.njk
├── about
└── index.md
├── css
├── index.css
└── prism-base16-monokai.dark.css
├── feed
├── feed.njk
└── htaccess.njk
├── gulpfile.js
├── img
└── .gitkeep
├── index.njk
├── package-lock.json
├── package.json
├── posts
└── .gitkeep
├── sitemap.xml.njk
├── tags.njk
└── tagslist.njk
/.eleventy.js:
--------------------------------------------------------------------------------
1 | const { DateTime } = require("luxon");
2 | const fs = require("fs");
3 | const pluginRss = require("@11ty/eleventy-plugin-rss");
4 | const pluginSyntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight");
5 |
6 | module.exports = function(eleventyConfig) {
7 | eleventyConfig.addPlugin(pluginRss);
8 | eleventyConfig.addPlugin(pluginSyntaxHighlight);
9 | eleventyConfig.setDataDeepMerge(true);
10 |
11 | eleventyConfig.addLayoutAlias("post", "layouts/post.njk");
12 |
13 | eleventyConfig.addFilter("readableDate", dateObj => {
14 | return DateTime.fromJSDate(dateObj, { zone: "utc" }).toFormat(
15 | "dd LLL yyyy"
16 | );
17 | });
18 |
19 | // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-date-string
20 | eleventyConfig.addFilter("htmlDateString", dateObj => {
21 | return DateTime.fromJSDate(dateObj, { zone: "utc" }).toFormat("yyyy-LL-dd");
22 | });
23 |
24 | // Get the first `n` elements of a collection.
25 | eleventyConfig.addFilter("head", (array, n) => {
26 | if (n < 0) {
27 | return array.slice(n);
28 | }
29 |
30 | return array.slice(0, n);
31 | });
32 |
33 | eleventyConfig.addCollection("tagList", require("./_11ty/getTagList"));
34 |
35 | eleventyConfig.addPassthroughCopy("img");
36 | eleventyConfig.addPassthroughCopy("css");
37 |
38 | /* Markdown Plugins */
39 | let markdownIt = require("markdown-it");
40 | let markdownItAnchor = require("markdown-it-anchor");
41 | let options = {
42 | html: true,
43 | breaks: true,
44 | linkify: true
45 | };
46 | let opts = {
47 | permalink: true,
48 | permalinkClass: "direct-link",
49 | permalinkSymbol: "#"
50 | };
51 |
52 | eleventyConfig.setLibrary(
53 | "md",
54 | markdownIt(options).use(markdownItAnchor, opts)
55 | );
56 |
57 | eleventyConfig.setBrowserSyncConfig({
58 | callbacks: {
59 | ready: function(err, browserSync) {
60 | const content_404 = fs.readFileSync("_site/404.html");
61 |
62 | browserSync.addMiddleware("*", (req, res) => {
63 | // Provides the 404 content without redirect.
64 | res.write(content_404);
65 | res.end();
66 | });
67 | }
68 | }
69 | });
70 |
71 | return {
72 | templateFormats: ["md", "njk", "html", "liquid"],
73 |
74 | // If your site lives in a different subdirectory, change this.
75 | // Leading or trailing slashes are all normalized away, so don’t worry about it.
76 | // If you don’t have a subdirectory, use "" or "/" (they do the same thing)
77 | // This is only used for URLs (it does not affect your file structure)
78 | pathPrefix: "/blog",
79 |
80 | markdownTemplateEngine: "liquid",
81 | htmlTemplateEngine: "njk",
82 | dataTemplateEngine: "njk",
83 | passthroughFileCopy: true,
84 | dir: {
85 | input: ".",
86 | includes: "_includes",
87 | data: "_data",
88 | output: "_site"
89 | }
90 | };
91 | };
92 |
--------------------------------------------------------------------------------
/.eleventyignore:
--------------------------------------------------------------------------------
1 | README.md
2 | _11ty/
3 |
--------------------------------------------------------------------------------
/.github/actions/setup/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:12-alpine
2 |
3 | COPY entrypoint.sh /entrypoint.sh
4 | ENTRYPOINT ["/entrypoint.sh"]
5 | CMD ["node"]
6 |
--------------------------------------------------------------------------------
/.github/actions/setup/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | # eval $(ssh-agent -s)
6 | # echo -e "StrictHostKeyChecking no" >> /etc/ssh/ssh_config
7 | # mkdir -p ~/.ssh
8 | # printf "$GH_SSH_KEY" > ~/.ssh/id_rsa
9 | # chmod 600 ~/.ssh/id_rsa
10 | # ssh-add ~/.ssh/id_rsa
11 | # ssh-add -l
12 |
13 | npm ci
14 | sh -c "$*"
15 |
--------------------------------------------------------------------------------
/.github/workflows/deploy-to-github-pages.yml:
--------------------------------------------------------------------------------
1 | on: [issues, push]
2 | name: Deploy to GitHub Pages
3 | jobs:
4 | build:
5 | name: Run deploy.sh for embed apps
6 | runs-on: ubuntu-latest
7 | steps:
8 | - uses: actions/checkout@master
9 | - name: Get posts from GitHub Issues
10 | uses: ./.github/actions/setup
11 | env:
12 | GH_API_TOKEN: ${{ secrets.GH_API_TOKEN }}
13 | with:
14 | args: npx gulp github:issues
15 | - name: Deploy to GitHub Pages
16 | uses: JamesIves/github-pages-deploy-action@master
17 | env:
18 | ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
19 | BRANCH: gh-pages
20 | BUILD_SCRIPT: npm ci && npm run build
21 | FOLDER: _site
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | _site
3 | node_modules
4 |
--------------------------------------------------------------------------------
/404.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: home.njk
3 | permalink: 404.html
4 | eleventyExcludeFromCollections: true
5 | ---
6 |
Content not found.
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # @pioug/blog
2 |
3 | - Static site generated with [11ty](https://www.11ty.io/)
4 | - Content authored with [GitHub Issues](https://github.com/pioug/blog/issues)
5 | - Assets hosted with [GitHub Pages](https://pages.github.com/)
6 | - Assets deployed with [GitHub Actions](https://github.com/features/actions)
7 |
8 | ## Setup
9 |
10 | **GitHub Actions is required** (still in beta as far as know).
11 |
12 | 1. Fork (or copy) the repository.
13 | 2. Store GitHub access token as [Secrets](https://developer.github.com/actions/managing-workflows/storing-secrets/):
14 |
15 | - `GH_API_TOKEN`: See [Creating a personal access token](https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line#creating-a-token).
16 | - `ACCESS_TOKEN`: Same as above but since I reuse an [existing action](https://github.com/JamesIves/github-pages-deploy-action), the environment variable has a different name than mine.
17 |
18 | 3. Enable GitHub Pages in the repository settings and set the `gh-pages` branch as source.
19 |
20 | ## Workflow
21 |
22 | 1. Create (or edit) an issue in the GitHub repository.
23 | 2. GitHub Actions receives an `issues` event.
24 | 3. A workflow fetches all issues labelled as `posts` in the repository using the GraphQL API of GitHub then uses Eleventy to compile the Markdown files using the body of the issues. The front matter is built from the title, tags and creation date of the issues.
25 | 4. Another workflow git-commits and git-pushes the build folder to the GitHub Pages branch.
26 | 5. GitHub Pages assets are automatically refreshed.
27 |
28 | ## Why
29 |
30 | - Reuse the label system to manage the post tags (easy to assign, rename on GitHub).
31 | - Articles are updated automatically as soon as I finish editing the corresponding issues.
32 | - Image upload is simple as copy-pasting an image on GitHub
33 | - No web server, no file bucket, no CDN to manage.
34 | - The articles are editable in the browser.
35 | - Bonus: No vendor lock-in? I feel like I could easily change the Docker/scripting part (used by GH Actions) or the hosting (S3 + CF) or the static site generator or without too much trouble. I can always return to markdown files if GitHub issues are a bad idea.
36 | - Bonus: Giving editing permissions could be as simple as giving someone access
37 |
--------------------------------------------------------------------------------
/_11ty/getTagList.js:
--------------------------------------------------------------------------------
1 | module.exports = function(collection) {
2 | let tagSet = new Set();
3 | collection.getAll().forEach(function(item) {
4 | if( "tags" in item.data ) {
5 | let tags = item.data.tags;
6 |
7 | tags = tags.filter(function(item) {
8 | switch(item) {
9 | // this list should match the `filter` list in tags.njk
10 | case "all":
11 | case "nav":
12 | case "post":
13 | case "posts":
14 | return false;
15 | }
16 |
17 | return true;
18 | });
19 |
20 | for (const tag of tags) {
21 | tagSet.add(tag);
22 | }
23 | }
24 | });
25 |
26 | // returning an array in addCollection works in Eleventy 0.5.3
27 | return [...tagSet];
28 | };
29 |
--------------------------------------------------------------------------------
/_data/metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "@pioug/blog",
3 | "url": "https://pioug.github.io/",
4 | "description": "I am writing about my experiences as a naval navel-gazer.",
5 | "feed": {
6 | "subtitle": "I am writing about my experiences as a naval navel-gazer.",
7 | "filename": "feed.xml",
8 | "path": "/feed/feed.xml",
9 | "url": "https://pioug.github.io/feed/feed.xml",
10 | "id": "https://pioug.github.io/"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/_includes/base.njk:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ renderData.title or title or metadata.title }}
7 |
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 | {{ content | safe }}
19 |
20 |
21 |
22 | Navigation
23 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/_includes/home.njk:
--------------------------------------------------------------------------------
1 | ---
2 | layout: base.njk
3 | ---
4 |
5 | {{ content | safe }}
6 |
--------------------------------------------------------------------------------
/_includes/post.njk:
--------------------------------------------------------------------------------
1 | ---
2 | layout: base.njk
3 | ---
4 | {{ title }}
5 | {% if date %}
6 | {{ date | readableDate }}
7 | {% endif %}
8 |
9 | {% for tag in tags %}
10 | {%- if tag != "posts" -%}
11 | {%- if tag != "nav" -%}
12 | {% set tagUrl %}/tags/{{ tag }}/{% endset %}
13 |
#{{ tag }}
14 | {%- endif -%}
15 | {%- endif -%}
16 | {% endfor %}
17 |
18 |
19 | {{ content | safe }}
20 |
--------------------------------------------------------------------------------
/_includes/postslist.njk:
--------------------------------------------------------------------------------
1 |
2 | {% for post in postslist | reverse %}
3 |
4 | {{ post.data.title }}
5 | {% for tag in post.data.tags %}
6 | {%- if tag != "posts" -%}
7 | {% set tagUrl %}/tags/{{ tag }}/{% endset %}
8 | #{{ tag }}
9 | {%- endif -%}
10 | {% endfor %}
11 |
12 | {{ post.date | readableDate }}
13 |
14 | {% endfor %}
15 |
16 |
--------------------------------------------------------------------------------
/about/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: post.njk
3 | title: About
4 | tags:
5 | - nav
6 | navtitle: About
7 | ---
8 |
9 | I am a person.
10 |
--------------------------------------------------------------------------------
/css/index.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 |
5 | body {
6 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
7 | sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
8 | line-height: 1.4;
9 | max-width: 800px;
10 | padding: 0 1rem;
11 | margin: 1rem auto;
12 | }
13 |
14 | a {
15 | color: DodgerBlue;
16 | text-decoration: none;
17 | }
18 |
19 | main {
20 | margin: 3rem 0;
21 | }
22 |
23 | .nav-item-active a {
24 | color: MidnightBlue;
25 | }
26 |
27 | .tag {
28 | color: DeepPink;
29 | }
30 |
31 | time {
32 | font-weight: normal;
33 | }
34 |
35 | p img {
36 | display: block;
37 | margin: auto;
38 | max-width: 100%;
39 | }
40 |
41 | nav h2,
42 | .title-page {
43 | font-variant: small-caps;
44 | font-weight: normal;
45 | }
46 |
--------------------------------------------------------------------------------
/css/prism-base16-monokai.dark.css:
--------------------------------------------------------------------------------
1 | pre,
2 | code {
3 | font-family: Consolas, Menlo, Monaco, "Andale Mono WT", "Andale Mono",
4 | "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono",
5 | "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L",
6 | "Courier New", Courier, monospace;
7 | line-height: 1.5;
8 | }
9 | pre {
10 | font-size: 14px;
11 | line-height: 1.375;
12 | direction: ltr;
13 | text-align: left;
14 | white-space: pre;
15 | word-spacing: normal;
16 | word-break: normal;
17 | -moz-tab-size: 2;
18 | -o-tab-size: 2;
19 | tab-size: 2;
20 | -webkit-hyphens: none;
21 | -moz-hyphens: none;
22 | -ms-hyphens: none;
23 | hyphens: none;
24 | padding: 1em;
25 | margin: 0.5em 0;
26 | background-color: #f6f6f6;
27 | }
28 | .highlight-line {
29 | display: block;
30 | padding: 0.125em 1em;
31 | text-decoration: none; /* override del, ins, mark defaults */
32 | color: inherit; /* override del, ins, mark defaults */
33 | }
34 |
35 | /* allow highlighting empty lines */
36 | .highlight-line:empty:before {
37 | content: " ";
38 | }
39 | /* avoid double line breaks when using display: block; */
40 | .highlight-line + br {
41 | display: none;
42 | }
43 |
44 | .highlight-line-isdir {
45 | color: #b0b0b0;
46 | background-color: #222;
47 | }
48 | .highlight-line-active {
49 | background-color: #444;
50 | background-color: hsla(0, 0%, 27%, 0.8);
51 | }
52 | .highlight-line-add {
53 | background-color: #45844b;
54 | }
55 | .highlight-line-remove {
56 | background-color: #902f2f;
57 | }
58 | code[class*="language-"],
59 | pre[class*="language-"] {
60 | font-size: 14px;
61 | line-height: 1.375;
62 | direction: ltr;
63 | text-align: left;
64 | white-space: pre;
65 | word-spacing: normal;
66 | word-break: normal;
67 | -moz-tab-size: 2;
68 | -o-tab-size: 2;
69 | tab-size: 2;
70 | -webkit-hyphens: none;
71 | -moz-hyphens: none;
72 | -ms-hyphens: none;
73 | hyphens: none;
74 | background: #272822;
75 | color: #f8f8f2;
76 | }
77 | pre[class*="language-"] {
78 | padding: 1.5em 0;
79 | margin: 0.5em 0;
80 | overflow: auto;
81 | }
82 | :not(pre) > code[class*="language-"] {
83 | padding: 0.1em;
84 | border-radius: 0.3em;
85 | }
86 | .token.comment,
87 | .token.prolog,
88 | .token.doctype,
89 | .token.cdata {
90 | color: #75715e;
91 | }
92 | .token.punctuation {
93 | color: #f8f8f2;
94 | }
95 | .token.namespace {
96 | opacity: 0.7;
97 | }
98 | .token.operator,
99 | .token.boolean,
100 | .token.number {
101 | color: #fd971f;
102 | }
103 | .token.property {
104 | color: #f4bf75;
105 | }
106 | .token.tag {
107 | color: #66d9ef;
108 | }
109 | .token.string {
110 | color: #a1efe4;
111 | }
112 | .token.selector {
113 | color: #ae81ff;
114 | }
115 | .token.attr-name {
116 | color: #fd971f;
117 | }
118 | .token.entity,
119 | .token.url,
120 | .language-css .token.string,
121 | .style .token.string {
122 | color: #a1efe4;
123 | }
124 | .token.attr-value,
125 | .token.keyword,
126 | .token.control,
127 | .token.directive,
128 | .token.unit {
129 | color: #a6e22e;
130 | }
131 | .token.statement,
132 | .token.regex,
133 | .token.atrule {
134 | color: #a1efe4;
135 | }
136 | .token.placeholder,
137 | .token.variable {
138 | color: #66d9ef;
139 | }
140 | .token.deleted {
141 | text-decoration: line-through;
142 | }
143 | .token.inserted {
144 | border-bottom: 1px dotted #f9f8f5;
145 | text-decoration: none;
146 | }
147 | .token.italic {
148 | font-style: italic;
149 | }
150 | .token.important,
151 | .token.bold {
152 | font-weight: bold;
153 | }
154 | .token.important {
155 | color: #f92672;
156 | }
157 | .token.entity {
158 | cursor: help;
159 | }
160 | pre > code.highlight {
161 | outline: 0.4em solid #f92672;
162 | outline-offset: 0.4em;
163 | }
164 |
--------------------------------------------------------------------------------
/feed/feed.njk:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: feed/feed.xml
3 | eleventyExcludeFromCollections: true
4 | ---
5 |
6 |
7 | {{ metadata.title }}
8 | {{ metadata.feed.subtitle }}
9 |
10 |
11 | {{ collections.posts | rssLastUpdatedDate }}
12 | {{ metadata.feed.id }}
13 |
14 | {{ metadata.author.name }}
15 | {{ metadata.author.email }}
16 |
17 | {%- for post in collections.posts %}
18 | {% set absolutePostUrl %}{{ post.url | url | absoluteUrl(metadata.url) }}{% endset %}
19 |
20 | {{ post.data.title }}
21 |
22 | {{ post.date | rssDate }}
23 | {{ absolutePostUrl }}
24 | {{ post.templateContent | htmlToAbsoluteUrls(absolutePostUrl) }}
25 |
26 | {%- endfor %}
27 |
28 |
--------------------------------------------------------------------------------
/feed/htaccess.njk:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: feed/.htaccess
3 | eleventyExcludeFromCollections: true
4 | ---
5 | # For Apache, to show `{{ metadata.feed.filename }}` when browsing to directory /feed/ (hide the file!)
6 | DirectoryIndex {{ metadata.feed.filename }}
7 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const gulp = require("gulp");
4 |
5 | const GH_API_TOKEN = process.env.GH_API_TOKEN;
6 |
7 | gulp.task("github:issues", getGitHubIssues);
8 |
9 | async function getGitHubIssues() {
10 | const graphql = require("graphql.js");
11 | const graph = graphql("https://api.github.com/graphql", {
12 | headers: {
13 | Authorization: `Bearer ${GH_API_TOKEN}`,
14 | "User-Agent": "pioug"
15 | },
16 | asJSON: true
17 | });
18 | const query = graph(
19 | `
20 | query ($number_of_issues: Int!) {
21 | repository(owner:"pioug", name:"blog") {
22 | issues(last:20) {
23 | edges {
24 | node {
25 | title
26 | createdAt,
27 | body,
28 | bodyText,
29 | labels(first: $number_of_issues) {
30 | edges {
31 | node {
32 | name
33 | }
34 | }
35 | }
36 | }
37 | }
38 | }
39 | }
40 | }
41 | `
42 | );
43 | const {
44 | repository: {
45 | issues: { edges: issues }
46 | }
47 | } = await query({
48 | number_of_issues: 100
49 | });
50 |
51 | issues
52 | .filter(({ node: { labels: { edges: labels } } }) =>
53 | labels.some(({ name }) => "posts")
54 | )
55 | .forEach(function({ node: issue }) {
56 | const fs = require("fs");
57 | const slugify = require("slugify");
58 | const { title, createdAt, body, bodyText, labels } = issue;
59 | const description = bodyText
60 | .replace(/\s+/g, " ")
61 | .split(" ")
62 | .reduce(
63 | (res, token) => (res.length > 200 ? `${res}` : res + " " + token),
64 | ""
65 | );
66 | const md = [
67 | `---`,
68 | `title: ${title}`,
69 | `date: ${createdAt.toString()}`,
70 | `description: ${
71 | description.length >= 200 ? description + "..." : description
72 | }`,
73 | `layout: post.njk`,
74 | `tags:`,
75 | `${labels.edges.map(label => ` - ${label.node.name}`).join("\n")}`,
76 | `---`,
77 | `${body}`
78 | ].join("\n");
79 | const filename =
80 | slugify(title, {
81 | remove: /[.,\/#!$%\^&\*;:{}=\-_`~()]/g,
82 | lower: true
83 | }) + ".md";
84 | console.log(`Write ${filename}`);
85 | fs.writeFileSync(`./posts/${filename}`, md);
86 | });
87 | }
88 |
--------------------------------------------------------------------------------
/img/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pioug/blog/d851c33d2f072f653659e96e1bf550b918270ebb/img/.gitkeep
--------------------------------------------------------------------------------
/index.njk:
--------------------------------------------------------------------------------
1 | ---
2 | layout: home.njk
3 | tags:
4 | - nav
5 | navtitle: Home
6 | ---
7 |
8 | Home
9 | {% set postslist = collections.posts %}
10 | {% include "postslist.njk" %}
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "blog",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "build": "npx eleventy",
6 | "serve": "npx eleventy --serve",
7 | "watch": "npx eleventy --watch",
8 | "debug": "DEBUG=* npx eleventy"
9 | },
10 | "devDependencies": {
11 | "@11ty/eleventy": "0.8.3",
12 | "@11ty/eleventy-plugin-rss": "1.0.6",
13 | "@11ty/eleventy-plugin-syntaxhighlight": "2.0.3",
14 | "graphql.js": "0.6.5",
15 | "gulp": "4.0.2",
16 | "luxon": "1.17.2",
17 | "markdown-it": "9.0.1",
18 | "markdown-it-anchor": "5.2.4",
19 | "prettier": "1.18.2",
20 | "slugify": "1.3.4",
21 | "tokenize-english": "1.0.3"
22 | },
23 | "dependencies": {}
24 | }
25 |
--------------------------------------------------------------------------------
/posts/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pioug/blog/d851c33d2f072f653659e96e1bf550b918270ebb/posts/.gitkeep
--------------------------------------------------------------------------------
/sitemap.xml.njk:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /sitemap.xml
3 | eleventyExcludeFromCollections: true
4 | ---
5 |
6 |
7 | {%- for page in collections.all %}
8 | {% set absoluteUrl %}{{ page.url | url | absoluteUrl(metadata.url) }}{% endset %}
9 |
10 | {{ absoluteUrl }}
11 | {{ page.date | htmlDateString }}
12 |
13 | {%- endfor %}
14 |
15 |
--------------------------------------------------------------------------------
/tags.njk:
--------------------------------------------------------------------------------
1 | ---
2 | pagination:
3 | data: collections
4 | size: 1
5 | alias: tag
6 | filter:
7 | - all
8 | - nav
9 | - post
10 | - posts
11 | - tagList
12 | addAllPagesToCollections: true
13 | layout: home.njk
14 | permalink: /tags/{{ tag }}/
15 | ---
16 | #{{ tag }}
17 |
18 | {% set postslist = collections[tag] %}
19 | {% include "postslist.njk" %}
20 |
21 |
22 |
--------------------------------------------------------------------------------
/tagslist.njk:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: /tags/
3 | layout: home.njk
4 | ---
5 | Tags
6 |
7 |
8 | {% for tag in collections.tagList %}
9 | {% set tagUrl %}/tags/{{ tag }}/{% endset %}
10 | {{ tag }}
11 | {% endfor %}
12 |
13 |
--------------------------------------------------------------------------------