Sat, Oct 23, 21, secret receipe of customizing 11th and this jekyll
This is a draft, the content is not complete and of poor quality!

creating_your_own_plugin

table formatting

example

Installed
Installed
Installed
    installed
  • matlab
  • 5g toolbox
  • autosar blockset
  • aerospace blockset
  • aerospace toolbox
  • antenna toolbox
  • audio toolbox
  • automated driving toolbox
  • communications toolbox
  • computer vision toolbox
  • control system toolbox
  • curve fitting toolbox
  • dsp system toolbox
  • data acquisition toolbox
  • database toolbox
  • deep learning toolbo
  • embedded coder
  • filter design hdl coder
  • fixed-point designer
  • fuzzy logic toolbox
  • global optimization toolbox
  • iec certification kit
  • image acquisition toolbox
  • image processing toolbox
  • instrument control toolbox
  • lte toolbox
  • matlab coder
  • matlab compiler
  • matlab parallel server
  • matlab report generator
  • mapping toolbox
  • mixed-signal blockset
  • model predictive control toolbox
  • model-based calibration toolbox
  • motor control blockset
  • navigation toolbox
  • opc toolbox
  • optimization toolbox
  • px4 psp
  • parallel computing toolbox
  • phased array system toolbox
  • polyspace bug finder
  • polyspace bug finder server
  • polyspace code prover
  • polyspace code prover server
  •   
  • powertrain blockset
  • predictive maintenance toolbox
  • rf blockset
  • rf toolbox
  • ros toolbox
  • reinforcement learning toolbox
  • robotics system toolbox
  • robust control toolbox
  • sensor fusion and tracking toolbox
  • serdes toolbox
  • signal processing toolbox
  • simbiology
  • simevents
  • simscape
  • simscape driveline
  • simscape electrical
  • simscape fluids
  • simscape multibody
  • simulink 3d animation
  • simulink check
  • simulink code inspector
  • simulink coder
  • simulink compiler
  • simulink control design
  • simulink coverage
  • simulink design optimization
  • simulink design verifier
  • simulink desktop real-time
  • simulink real-time
  • simulink report generator
  • simulink requirements
  • simulink test
  • *stateflow
  • statistics and machine learning toolbox
  • symbolic math toolbox
  • system identification toolbox
  • text analytics toolbox
  • vehicle dynamics blockset
  • vehicle network toolbox
  • wlan toolbox
  • wireless hdl toolbox
  • simulink
  •   
  • 5g toolbox
  • autosar blockset
  • aerospace blockset
  • aerospace toolbox
  • antenna toolbox
  • audio toolbox
  • automated driving toolbox
  • communications toolbox
  • computer vision toolbox
  • control system toolbox
  • curve fitting toolbox
  • dsp system toolbox
  • data acquisition toolbox
  • database toolbox
  • deep learning toolbox
  • embedded coder
  • filter design hdl coder
  • fixed-point designer
  • fuzzy logic toolbox
  • iec certification kit
  • image acquisition toolbox
  • image processing toolbox
  • instrument control toolbox
  • lte toolbox
  • matlab coder
  • matlab compiler
  • matlab parallel server
  • matlab report generator
  • mapping toolbox
  • mixed-signal blockset
  • model predictive control toolbox
  • model-based calibration toolbox
  • motor control blockset
  • navigation toolbox
  • opc toolbox
  • optimization toolbox
  • px4 psp
  • parallel computing toolbox
  • phased array system toolbox
  • polyspace bug finder
  • polyspace bug finder server
  • polyspace code prover
  • polyspace code prover server
  • powertrain blockset   
  • predictive maintenance toolbox
  • rf blockset
  • rf toolbox
  • ros toolbox
  • reinforcement learning toolbox
  • robotics system toolbox
  • robust control toolbox
  • sensor fusion and tracking toolbox
  • serdes toolbox
  • signal processing toolbox
  • simbiology
  • simevents
  • simscape
  • simscape driveline
  • simscape electrical
  • simscape fluids
  • simscape multibody
  • simulink 3d animation
  • simulink check
  • simulink code inspector
  • simulink coder
  • simulink compiler
  • simulink control design
  • simulink coverage
  • simulink design optimization
  • simulink design verifier
  • simulink desktop real-time
  • simulink report generator
  • simulink requirements
  • simulink test
  • stateflow
  • statistics and machine learning toolbox
  • symbolic math toolbox
  • system identification toolbox
  • text analytics toolbox
  • vehicle dynamics blockset
  • vehicle network toolbox
  • Wireless HDL Toolbox   
kramdown_table      
|---
| {::nomarkdown}<div width="380px"></div>{:/}   | **Installed** {::nomarkdown}<div width="200px"></div> {:/}  | **Installed** {::nomarkdown}<div width="200px"></div> {:/}  | **Installed** {::nomarkdown}<div width="200px"></div>  {:/} |
| - | - |  - |  - |
| {::nomarkdown}<ul width="380px"> <em> installed</em>  <li>matlab</li> <li> 5g toolbox</li> <li>autosar blockset </li> <li>aerospace blockset</li> <li>aerospace toolbox</li>   <li>antenna toolbox</li> <li>audio toolbox</li>   <li>automated driving toolbox</li>  <li> communications toolbox</li>    <li> computer vision toolbox</li>    <li>control system toolbox</li>  <li>curve fitting toolbox</li>     <li>dsp system toolbox</li>   <li>data acquisition toolbox</li>  <li>database toolbox</li>  <li>deep learning toolbo</li>  <li>embedded coder</li> <li>filter design hdl coder</li> <li>fixed-point designer</li>     <li>fuzzy logic toolbox</li>  <li>global optimization toolbox</li> <li>iec certification kit</li> <li>image acquisition toolbox</li>    <li>image processing toolbox</li>  <li>instrument control toolbox</li>   <li>lte toolbox</li> <li>matlab coder</li> <li> matlab compiler</li><li>matlab parallel server</li>  <li>matlab report generator</li>  <li>mapping toolbox</li> <li>mixed-signal blockset</li>   <li> model predictive control toolbox</li>   <li>model-based calibration toolbox</li><li>motor control blockset </li>  <li>navigation toolbox</li>  <li>opc toolbox </li>    <li>optimization toolbox</li> <li>px4 psp </li>    <li>parallel computing toolbox</li>  <li>phased array system toolbox</li> <li>polyspace bug finder</li>  <li>polyspace bug finder server</li> <li>polyspace code prover </li><li>polyspace code prover server </li>  &nbsp;&nbsp;</li> </ul> {:/}

Thi

This is not a tutorial to create an 11ty website, this’s a note! You can find some very new and useful techniques on this note alongside the official documentation.

This note will be always updated!

local-dev and deployed as site

github_actions

Install && Set-up with Netlify

👉 First, install nodejs. 👉 Using this starter template or Google’s high performance statrer theme (recommended).

# Install dependencies
npm install

Depend on each theme, you should follow the installation steps given in that theme.

Sometimes, 11ty takes too much time to build (especially on the task of optimizing images. On my site, it takes almost 10 minutes). You shouldn’t use branch master to build you site because every time you make a push, Netlify will rebuild your whole site. You should create and use a new branch, so-called prod instead.

Idea 1 – manually build but should not use many times”

On Netlify, go to Site settings > Build & deploy:

  • Build settings:
    • Build command: npm run build (depends on your site)
    • Publish directory: _site (depends on your site)
    • Builds: Active builds
  • Deploy contexts:
    • Production branch: prod (the same as the your created branch)
    • Deploy previews: Don’t deploy pull requests (you don’t want someone pull request and it auto make a deploy)
    • Branch deploys: Deploy only the production branch.

Idea 2 – build locally and push _site only”

You should know that, even if your site contains only html files, netlify is able to make it live as usual.

  1. Working mainly on branch dev like in Idea 1.
  2. Create a branch, so-called _site from dev. In this branch, delete all folders except _site, node_modules, .git, .gitignore.
  3. Modify .gitignore (exclude all except _site folder to push to github),

    /*
    /*/
    !/_site/
    
  4. Now, we tell netlify build our site from branch _site (which contains html files only so it doesn’t take time to build anything, it’s really fast!)
    • Build settings:
      • Base directory: Not set.
      • Build command: Not set.
      • Publish directory: _site/
      • Builds: Active.
    • Deploy context: - Production branch: site - Deploy previews: Don’t deploy pull requests - Branch deploys: Deploy only the production branch


“Example workflow with dinhanhthi.com”

From the main repo, I clone to 2 different folders

|- pf3.36io.co # <- branch "dev"
|- github.com/aiegoo/documentation 				# <- branch "_site"

On github.com, I create a script called ud_site.sh which contains,

echo "Start building site"
cd ../pf3.36io.co/
npm run build
cd ../github.com/aiegoo/documentation/
echo "Start copying...."
cp -Rf ../pf3.36io.co/_site/* _site/
echo "End copying"
git add .
git commit -m "Updated: `date +'%Y-%m-%d %H:%M:%S'`"
git push

For covenience, I create an alias in bash shell,

alias update_dat='cd ~/git/github.com && sh ud_site.sh && cd -1'

From now, whenever I wanna build and deploy my site on netlify, I just run,

update_dat

I saved from 1h of building to 2m of building on netlify with this method!

Templating

SCSS to CSS & Using postcss

“If you use node-sass

# Folder's structure
css/main.scss
css/components/_fonts.scss # with _
css/components/....
// in main.scss
@import "./components/font"; // without extension

// package.json
{
  "scripts": {
    "js-build": "node-sass css/main.scss -o css",
  }
}
<!-- in <head> -->
<link rel="stylesheet" href="css/main.css" />


“If you use rollup (like this site)”

# Folder's structure
css/main.scss
css/components/_fonts.scss
css/components/....
css/main_input.js
// in main.scss
@import "./components/font"; // without extension

// package.json
{
  "scripts": {
    "js-build": "rollup -c rollup.config.js",
  }
}
import scss from "rollup-plugin-scss";

export default [
  {
    // plugin 1
  },
  {
    input: "css/main_input.js", // where the input file containing import of main.scss
    output: {
      file: "css/main.js", // intermediate file which can be translated to css/main.css
      format: "esm", // still not know
    },
    plugins: [
      scss(), // there are other configs
    ],
  },
];
<!-- in <head> -->
<link rel="stylesheet" href="css/main.css" />


“If you use parcel

# install
npm i parcel-bundler
npm i npm-run-all -D
# folder structure
_assets/css/main.scss
___________/_bootstrap.scss
_______/js/main.js
# main.scss
@import "./bootstrap";
# main.js
import "./../css/main.scss";
# package.json
"scripts": {
    "start": "npm-run-all --parallel dev:*",
    "build": "run-s prod:*",
    "dev:eleventy": "eleventy --serve",
    "dev:parcel": "parcel watch ./_assets/js/main.js --out-dir ./_site/assets",
    "prod:eleventy": "eleventy",
    "prod:parcel": "parcel build ./_assets/js/main.js --out-dir ./_site/assets",
},
# run
npm start

Using postcss
# Install it and its plugin first
npm install autoprefixer postcss postcss-cli
# Create postcss.config.js on the same folder as package.json
module.exports = {
  plugins: [require("autoprefixer")],
};
// npm command in package.json
"css:prefix": "postcss src/css/main.css --use autoprefixer --replace true"
# Watch (cannot use with `--replace`)
postcss --watch main.css -o main_.css --use autoprefixer
Nunjucks inside css
<style>
  .bg {
    background-image: url();
  }
</style>

<!-- or -->
<div style="background-image: url();"></div>

Bootstrap + 11ty

👉 Bootstrap’s homepage 👉 How to Isolate Bootstrap CSS to Avoid Conflicts (using LESS)

# install
npm i bootstrap jquery popper.js

Using alongside with section “SCSS to CSS”.

# folder structure
_assets/css/main.scss
_______/vender/_bootstrap.scss
// main.scss
@import "./vender/bootstrap";
// _bootstrap.scss
// all components
// @import "./../../../node_modules/bootstrap/scss/bootstrap.scss";

// Required
// check more: https://getbootstrap.com/docs/4.5/getting-started/theming/#importing
@import "./../../../node_modules/bootstrap/scss/functions";
@import "./../../../node_modules/bootstrap/scss/variables";
@import "./../../../node_modules/bootstrap/scss/mixins";

Google Fonts

  • Put fonts in fonts/ and use this tool to generate .woff, woff2 from Google Fonts. Be careful on the location will be used on your site.
  • Copy and paste to css/main.scss.
  • If you have a problem with Content-Security-Policy, check this section.

Using fontello icons

More icon fonts than Fontawesome. Choose fonts on fontello > “Get config only”.

# install fontello-cli
npm install -g fontello-cli

# Remove old session (if changing the current icons, not adding new ones)
rm .fontello-session

# install / update new icon
fontello-cli --config src/fontello/config.json --css src/fontello/css --font src/fontello/font install
// .eleventy.js
module.exports = function (eleventyConfig) {
  eleventyConfig.addPassthroughCopy("src/fontello");
};
<head>
  <link rel="stylesheet" href="/src/fontello/css/fontello.css" />
</head>
<!-- usage -->
<i class="icon-doc"></i>

Check the code -doc in src/fontello/config.json, field "css".

Layout

mkdir _includes/layouts
touch _includes/layouts/post.njk
// create an alias
module.exports = function (eleventyConfig) {
  eleventyConfig.addLayoutAlias("post", "layouts/post.njk");
};
# update changes
touch .eleventy.js
# then use
---
layout: post
---
Includes

Split layout into parts and include them in the main file.

// in _includes/components/head.njk
{% include "custom/head.html" %}

// custom parameter
{% assign customClass = 'list-homepage' %}
{% include "custom/postslist.html" %}
// inside postlist.njk, just use {{ customClass }}

Template inheritance

Read this tutorial.

<!-- _includes/layouts/base.njk -->
<body>
  <header>{% include topnav.html %}</header>
</body>

<!-- _includes/layouts/post.njk -->
--- --- {% include "layouts/base.html" %} {% include topnav.html %}
<!-- only appear on post layout -->

Post’s components

👉 Page variable components.

// URL can be used in <a href> to link to other templates
url: "/current/page/myFile/",

// For permalinks: inputPath filename minus template file extension (New in v0.3.4)
fileSlug: "myFile",

// For permalinks: inputPath minus template file extension (New in v0.9.0)
filePathStem: "/current/page/myFile",

// JS Date Object for current page (used to sort collections)
date: new Date(),

// The path to the original source file for the template
// Note: this will include your input directory path!
inputPath: "./current/page/myFile.md",

// Depends on your output directory (the default is _site)
// You probably won’t use this: `url` is better.
outputPath: "./_site/current/page/myFile/index.html"

templateContent: //the rendered content of this template. This does not include layout wrappers.

data: // all data for this piece of content (includes any data inherited from layouts)
// self-defined frontmatter tags can be found here
For ones who wanna get only the content (escape HTML tags and special characters) of the post:
{{ page.templateContent | dump | safe | striptags(true) }}

Custom frontmatter fields

Suppose you use specialTitle in the frontmatter of your page (eg. index.js). You can use it in the template, eg. header.njk, as

{% if specialTitle %} {# content #} {% endif %}

Recognize home page

{% assign pageUrlLength = page.url | length %}

If pageUrlLength > 1, it’s not home page!

Frontmatter

---
title: Title of the post
description: description of the post
date: 2020-09-11
layout: layouts/post.html
---
---
tags:
  - default
# or
tags: [tag 1, tag 2]
---

List of posts

Normal,

<ul>
  {% for post in collections.posts %}
  <li>
    <a href="{{ post.url | url }}"> {{ post.data.title }} </a>
  </li>
  {% endfor %}
</ul>

Other default variable (like post.url) can be found here. Note that, you can use page.templateContent for the content of a page in some collections (not tested yet but you can try link).

Sort posts by titles

<!-- Create a new list -->
{% assign newPostList = [] %} {% for post in collections.posts %} {% assign
newPostList = (newPostList.push({title: post.data.title, url: post.url}),
newPostList) %} {% endfor %}

<!-- list of post by alphabet -->
<ul>
  {% for post in newPostList|sort(attribute='title') %}
  <li>
    <a href="{{ post.url | url }}"> {{ post.title }} </a>
  </li>
  {% endfor %}
</ul>

Posts by categories / tags

In this case, ==we consider a category as the first tag== of a post. For example, if a post has tags tags: [tag 1, tag 2, tag 3], then tag 1 will be its category!

// _data/categories.json
[
  {
    "name": "Cat 1",
    "icon": "/img/cats/cat_1.svg"
  },
  {
    "name": "Cat 2",
    "icon": "/img/cats/cat_2.svg"
  }
]
{% for item in categories %}
<div class="category-wrapper">
  <h2>{{ item.icon }} {{ item.name }}</h2>
  <ul class="post-list">
    {% for post in collections[item.name] %}
    <li class="post-item">
      <a href="{{ post.url }}"><h3>{{ post.data.title }}</h3></a>
    </li>
    {% endfor %}
  </ul>
</div>
{% endfor %}

:::

External posts

If you wanna add external posts (not generated by 11ty from .md files), do below steps. For example, on this site, I cretae some pages created by Notion and I wanna add them to category MOOC.

Remark: This section is used with section “posts by categories” and section “sort posts by title”.

// _data/cat_ex_posts.json
[
  {
    "title": "DL4 - Convolutional Neural Network (CNN)",
    "url": "https://www.notion.so/dinhanhthi/CNN-by-deeplearning-ai-a081d253fc2c4c0b99edd2757c759b9e",
    "tags": ["MOOC", "Deep Learning"]
  },
  {
    "title": "DL5 - Sequence models",
    "url": "https://www.notion.so/dinhanhthi/CNN-by-deeplearning-ai-a081d253fc2c4c0b99edd2757c759b9e",
    "tags": ["MOOC", "Deep Learning"]
  },
  {
    "title": "NLP by HSE",
    "url": "https://www.notion.so/dinhanhthi/NLP-by-HSE-20cec3e92201475eb4d48787954f3aa4",
    "tags": ["MOOC", "NLP"]
  }
]
{% for item in categories %} {% assign postslist = collections[item.name] %} {% assign
newPostListCat = [] %} {% for post in cat_ex_posts %} {% for tg in post.tags %}
{% if tg == item.name %} {% assign more_post = true %} {% assign newPostListCat =
(newPostListCat.push({title: post.title, url: post.url, tags: post.tags}),
newPostListCat) %} {% endif %} {% endfor %} {% endfor %} {% assign newPostList = []
%} {% for post in postslist %} {% assign newPostList = (newPostList.push({title:
post.data.title, url: post.url}), newPostList) %} {% endfor %} {% if more_post
%} {% for post in newPostListCat %} {% assign newPostList =
(newPostList.push({title: post.title, url: post.url, tags: post.tags}),
newPostList) %} {% endfor %} {% endif %}
{% endhighlight %}
<div class="category-wrapper">
  <h2>{{ item.icon }} {{ item.name }}</h2>
  <ul class="post-lihsboxst.url }}"><h3>{{ post.title }}</h3></a>
    </li>
    {% endfor %}
  </ul>
</div>
{% endfor %}

Next / Previous post

<ul>
  {%- assign nextPost = collections.posts | getNextCollectionItem(page) %} {%- if
  nextPost %}
  <li>
    Next: <a href="{{ nextPost.url | url }}">{{ nextPost.data.title }}</a>
  </li>
  {% endif %} {%- assign previousPost = collections.posts |
  getPreviousCollectionItem(page) %} {%- if previousPost %}
  <li>
    Previous:
    <a href="{{ previousPost.url | url }}">{{ previousPost.data.title }}</a>
  </li>
  {% endif %}
</ul>

Custom js scripts

# Put scripts in
# /src/main.js
<!-- in <head> -->
<script async defer src="{{ '/js/min.js' | addHash }}"></script>

Using rollupjs,

// rollup.config.js
export default [
  {
    input: "src/main.js",
    output: [
      {
        file: "js/min.js",
        format: "iife",
        sourcemap: true,
        plugins: [terser()],
      },
    ],
  },
];
Using `rollup` as above way, we have only one output file `js.min.js`!

Using custom js files for each page

Suppose that you have a custom frontmatter customJS: ["file1.js, file2.js"] containing all custom js files. It means that the files file1.js and file2.js are presented only in this page! (If we integrate them in main.js, they will be integrated ub all other pages after being rendered even if they’re useless in those pages).

{% if customJS %} {% assign js %} {% for file in customJS %} {% include "js/"
+ file %} {% endfor %}
<script>
  {
    {
      js | jsmin | safe;
    }
  }
</script>
{% endif %}

Where jsmin is a filter created in next section. All files file1.js, file2.js are stored in _includes/_scripts/.

Minify js files{:#minify-js-file}
module.exports = function (eleventyConfig) {
  eleventyConfig.addNunjucksAsyncFilter(
    "jsmin",
    async function (code, callback) {
      try {
        const minified = await minify(code);
        callback(null, minified.code);
      } catch (err) {
        console.error("Terser error: ", err);
        callback(null, code);
      }
    }
  );
};

Usage (_includes/scripts/search.js),

{% assign js %} {% include "js/search.js" %}
<script>
  {
    {
      js | jsmin | safe;
    }
  }
</script>

Last modified date

// .eleventy.js
const { DateTime } = require("luxon");
module.exports = function (eleventyConfig) {
  eleventyConfig.addFilter("readableDate", (dateObj) => {
    return DateTime.fromJSDate(dateObj, { zone: "utc" }).toFormat(
      "dd LLL yyyy"
    );
  });

  // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-date-string
  eleventyConfig.addFilter("htmlDateString", (dateObj) => {
    return DateTime.fromJSDate(dateObj, { zone: "utc" }).toFormat("dd-LL-yyyy");
  });

  eleventyConfig.addFilter("sitemapDateTimeString", (dateObj) => {
    const dt = DateTime.fromJSDate(dateObj, { zone: "utc" });
    if (!dt.isValid) {
      return "";
    }
    return dt.toISO();
  });
};

Last modified date,

{{ page.inputPath | lastModifiedDate | htmlDateString }}

Insert code highlight

Code syntax highlight: Need this plugin. List of supported languages.

# Highlight line 2

```js/2
// lines of codes
```
# Highlight line 2 to 4

```js/2-4
// lines of codes
```
# Highlight line 2, 4

```js/2,4
// lines of codes
```
# Delete line 2 (red highlight)

# and add line 4 (green highlight)

```js/4/2
// lines of codes
```
Insert liquid / nunjuck code

Inline code, put {% raw %} and {% endraw %} around the keyword.

Code block,

~~~ js {% raw %}
// line of codes
{% endraw %}
~~~

Math equations Mathjax KaTeX (my choice)

Mathjax

Using markdown-it-mathjax

KaTeX (my choice)
“KaTeX: Use markdown-it-katex

Using markdown-it-katex (use this version only),

// .eleventy.js
const markdownIt = require("markdown-it");
const markdownItKatex = require("@iktakahiro/markdown-it-katex");

let markdownLibrary = markdownIt();
markdownLibrary.use(markdownItKatex);
<!-- inside <head> -->
<link
  rel="stylesheet"
  href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.11.1/katex.min.css"
/>

💡 Some tips: working with KaTeX

# working
$$
\dfrac{1}{2}
$$
# not working
- Item

	$$
	\dfrac{1}{2}
	$$
- Item
# working again
- Item

  $$\dfrac{1}{2}$$ # without \n
- Item

:::


In this site, I use markdown-it-texmath. I choose this because we can overcome the weakness of markdown-it-katex in case of breaking lines in list mode & it’s more flexible (you can try it online here).

An important note: the original version has a problem of whitespace before and after <eq> tag in the inline mode. That why instead of seeing aaa x aaa with aaa $x$ aaa, we see aaaxaaa. I’ve changed (and reported an issue). For a moment, I use a modified version here. Update: The author couldn’t reproduce the issue I met (with version 0.8), he keep updating the plugin but I didn’t try version 0.9. You better try it before trying my option!

# Install
# npm i markdown-it-texmath --save-dev # original, whitespace problem
npm i git+https://github.com/dinhanhthi/markdown-it-texmath.git --save-dev
// .eleventy.js
const tm = require("markdown-it-texmath"); // or local folder if you use a customized version
module.exports = function (eleventyConfig) {
  md = require("markdown-it")().use(tm, {
    engine: require("katex"),
    delimiters: "dollars",
    katexOptions: { macros: { "\\RR": "\\mathbb{R}" } },
  });
};
If you wanna modify yourself markdown-it-texmath

Copy the cloned folder markdown-it-temath (except .git, .gitignore, test/, package-lock.json) to a so-called /third-party/ folder in your project. In .eleventy.js, you import it as

const tm = require("./third_party/markdown-it-texmath");

:::

<!-- Add in <head> -->
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/katex/dist/katex.min.css"
/>

<!-- Save to local and modify (or just use) -->
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/markdown-it-texmath/css/texmath.min.css"
/>


Figures

# Insert normally,
![description](/path/to/image)

# With custom classes
# (using markdown-it-attrs)
![](){:.custom-class}
  • customzing image.html to act as an inline callout script for image files

    Add imagesurl frontmatter for the directory containing the images, and then assign the image files in filenames=" split: "," before including page_gallery.html See below for an example

<div class ="image-gallery-yoga">
{% for name in filenames %}
    <div class="gallery-box">
    <a href="{{ page.imagesurl }}{{ name }}">
      <img src="{{ page.imagesurl }}{{ name }} " alt="{{ name }}"  class="img-gallery" />
     </a>
    </div>
 {% endfor %}
</div>
{% assign filenames="123.jpg,345.jpg,567.jpg" %}
{% include page_gallery.html %}

:::

Using relative path?

In sert images with the path relative to the directory of .md files. Let’s use eleventy-plugin-page-assets.

|- sample_posts/
| |- post.md
| |- img/
| | |- figure1.png
| |- figure2.png
|- src/

In .eleventy.js,

const pageAssetsPlugin = require("eleventy-plugin-page-assets");

module.exports = function (eleventyConfig) {
  eleventyConfig.addPlugin(pageAssetsPlugin, {
    mode: "parse",
    postsMatching: "sample_posts/*.md",
    recursive: true,
  });
};

Markdown

markdown-it & its plugins

We use markdown-it and its plugins. Just use npm i <plugin-name> --save-dev to install.

My choices of useful plugins

Search on npm page with the same name.

  • markdown-it-anchor (use with eleventy-plugin-nesting-toc) to create anchor links for headings.
  • markdown-it-attrs to use something like {:#heading-id} (like in Jekyll).
  • markdown-it-emoji to insert emoji with shortcodes.
  • markdown-it-container to use something like :::info for blocks.
  • markdown-it-footnote
  • markdown-it-heading-wrapper to wrap heading with containers.
  • markdown-it-kbd to use [[Ctrl]] for keyboard-like style.
  • markdown-it-mark to use ==Text== for rendering to <mark>Text</mark>.
  • markdown-it-table-of-contents for using [[toc]] anywhere we want.
  • My customized version of markdown-it-texmath. Update: Check this section.
  • @gerhobbelt/markdown-it-inline-text-color for coloring inline text by using something like {color:red}Text{color}.


How to use markdown-it's plugins in 11ty?

Below are an example of inserting 2 plugins,

// .eleventy.js
// An example of using plugins
const markdownIt = require("markdown-it");
var markdownItp = require("markdown-it")();

module.exports = function (eleventyConfig) {
  let markdownLibrary = markdownIt({
    html: true, // html tag inside source
    breaks: true, // use '\n' as <br>
    linkify: true, // Autoconvert URL-like text to links
  })
    .use(require("markdown-it-emoji")) // emoji
    .use(require("markdown-it-table-of-contents")); // [[toc]] (no spaces)
  eleventyConfig.setLibrary("md", markdownLibrary);
};

Custom container

If you wanna create an advanced custom container, use plugin markdown-it-container. For example, you want export something like,

<div class="hsbox">
  <div class="hs__title">
    <!-- Custom Title -->
  </div>
  <div class="hs__content">
    <!-- Custom markdown texts -->
  </div>
</div>

Just by using,

::: hsbox Custom Title Custom markdown texts :::

You can put in .eleventy.js like,

.use(require('markdown-it-container'), 'hsbox', {
  validate: function (params) {
    return params.trim().match(/^hsbox\s+(.*)$/);
  },
  render: function (tokens, idx) {
    var m = tokens[idx].info.trim().match(/^hsbox\s+(.*)$/);
    if (tokens[idx].nesting === 1) {
      // opening tag
      return '<div class="hsbox"><div class="hs__title">'
        + markdownItp.renderInline(m[1])
        + '</div><div class="hs__content">';
    } else {
      // closing tag
      return '</div></div>';
    }
  }
})
Markdown inside .njk
// .eleventy.js

module.exports = function (eleventyConfig) {
  eleventyConfig.addPairedShortcode("markdown", (content, inline = null) => {
    return inline
      ? markdownLibrary.renderInline(content)
      : markdownLibrary.render(content);
  });
};
{% markdown %}
<!-- html tags -->
<!-- no need spaces before/after -->
{% endmarkdown %}
HTML/nunjucks tags inside .md
<!-- not working -->
<div>__abc__</div>
<!-- working -->
<div>__abc__</div>
<!-- not working -->
<div class="list-of">
  {% for item in cv.education.list %}
  <div class="where">{{ item.where }}</div>
  <div class="title">{{ item.title }}</div>
  <div class="date">{{ item.date }}</div>
  {% endfor %}
</div>

<!-- working -->
<div class="list-of">
  {% for item in cv.education.list %}
  <div class="where">{{ item.where }}</div>
  <div class="title">{{ item.title }}</div>
  <div class="date">{{ item.date }}</div>
  {% endfor %}
</div>

:::

Custom block shortcodes

To creata the same code block like above, i.e., ```html
```
Just by using, ```html {% hsbox "Custom Title" %} {% endhsbox %} ```
// .eleventy.js

module.exports = function (eleventyConfig) {
  eleventyConfig.addPairedShortcode("hsbox", (content, title) => {
    return (
      '<div class="hsbox"><div class="hs__title">' +
      markdownLibrary.renderInline(title) +
      '</div><div class="hs__content">' +
      markdownLibrary.render(content) +
      "</div></div>"
    );
  });
};

Custom inline shortcodes

If you wanna export something like,

<a href="custom-url">ref</a>

by using [link](custom-url) ("" is required). You can set,

// .eleventy.js

module.exports = function (eleventyConfig) {
  eleventyConfig.addShortcode("ref", function (url) {
    return (
      '<sup><a href="' +
      url +
      '" rel="noopener noreferrer" target="_blank">[ref]</a></sup>'
    );
  });
};

For me, the best choice for search feature in 11ty is using Elasticlunr with some customizations.

“Why not others?”

Based on the purpose of free, quick, full text search:

  • We don’t choose Google’s Programmable Search because: it contains ads, not index as we want, difficult to customize with personal theme,…
  • We don’t choose paid options like Agolia because the free option contains very few units. It’s absolutely not enough for your need. In case you still want to use it with less consumption, read this article.


Check this repository, I’ve pulled and modified from this one (The author takes so long to check my pull request ^^). My customization supports:

  • Index your customizable keywords.
  • Fix UX bugs in the main repo.
  • Highlight found keywords in the search result.
  • Limit the max number of texts in the result (show around the found keywords).
  • Adapt to the newest version of 11ty.

Data files

Apply data for all posts in a directory

👉 Check more in official doc.

For example, we wanna add tag “posts” for all posts in a folder named “sample_posts”. Inside /sample_posts/, create a file sample_posts.son (yes, the same name as “sample_posts”) with following content,

{
  "tags": ["posts"]
}

Using external data files with environment env

Suppose your data file _data is not in src/ but notes/_data/.

// .eleventy.js
module.exports = function (eleventyConfig) {
  return {
    dir: {
      data: dataDir,
    },
  };
};

You can change dataDir based on the settings of env.

In case you have a setting file in notes/_data/settings.json. Sometimes, you use it as a data files via dataDir (just settings.*), sometimes, you use it as a nseparated json file. For example, you use it in a separated js file toggle-notes.js,

// You CAN'T use below
import settings from "../../../notes/_data/settings.json";
// ❗ There will be an error
// Cannot use import statement outside a module

Solution, in .eleventy.js,

const settings = require("./" + thiDataDir + "/settings.json");
module.exports = {
  environment: process.env.ELEVENTY_ENV, // the same place with this
  settings: settings,
};

Then in toggle-notes.js,

env.settings.someThing;

Local data files

// .eleventy.js
module.exports = function (eleventyConfig) {
  return {
    dir: {
      input: ".",
      includes: "_includes",
      data: "_data",
      output: "_site",
    },
  };
};

You put all your data files (.js or .json) in _data, e.g.,

// _data/dataUrls.json
[
  {
    "name": "abc",
    "url": "http://abc.com"
  },
  {
    "name": "xyz",
    "url": "http://xyz.com"
  }
]
<!-- in a .njk file -->
{% for item in dataUrls %} {{ item.name }} {{ item.url }} {% endfor %}
// _data/cat_icon.json
{
  "Project-based Learning": {
    "svg": "/img/cats/project.svg"
  },
  "MOOC": {
    "svg": "/img/cats/mooc.svg"
  }
}
// .eleventy.js
const catIcon = require("./_data/cat_icon.json")
// usage
catIcon[page.data.tags[1]].svg,

// .njk
catIcon[page.data.tags[1]].svg,

For example, export a current year on site,

// _data/helpers.js
module.exports = {
  currentYear() {
    const today = new Date();
    return today.getFullYear();
  },
};
<!-- in a .njk file -->
<div>{{ helpers.currentYear() }}</div>

Fetched JSON from an external source

For example, this post displays a list of starred github repositories (by me) which is fetched from https://api.github.com/users/dinhanhthi/starred.

// in .eleventy.js
module.exports = function (eleventyConfig) {
  eleventyConfig.addShortcode("list_repos", getLiList);
  async function getRepoData(_url) {
    const response = await fetch(_url);
    return await response.json();
  }
  async function getLiList() {
    function compare(a, b) {
      if (a.name.toLowerCase() < b.name.toLowerCase()) {
        return -1;
      }
      if (a.name.toLowerCase() > b.name.toLowerCase()) {
        return 1;
      }
      return 0;
    }
    // escape HTML tags
    function htmlEntities(str) {
      return String(str)
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;");
    }
    var repos = "";
    const data = await getRepoData(
      "https://api.github.com/users/dinhanhthi/starred?page=1&per_page=10000"
    );
    data.sort(compare).forEach((obj) => {
      repos +=
        "<li>" +
        '<a href="' +
        obj.html_url +
        '" target="_blank">' +
        obj.name +
        "</a>" +
        " by <i>" +
        obj.owner.login +
        "</i> — " +
        htmlEntities(obj.description) +
        "</li>";
    });
    return "<ol>" + repos + "</ol>";
  }
};
// in post
{% list_repos %}

Working style

Custom environment

More info, read official doc. For example, we only perform something differently on local.

{
  "scripts": {
    "local-build": "ELEVENTY_ENV=local eleventy"
  }
}

An example of using in .eleventy.js,

// .eleventy.js
module.exports = {
  environment: process.env.ELEVENTY_ENV,
};

module.exports = function (eleventyConfig) {
  if (process.env.ELEVENTY_ENV == "local") {
    // do something locally
  } else {
    // do something on server
  }
};

Or using in the template,

// _data/myProject.js
module.exports = {
  environment: process.env.ELEVENTY_ENV,
};
{% if myProject.environment == "local" %}
  <style>{{ css | cssmin | safe }}</style>
{% else %}
  <style>{{ css | safe }}</style>
{% endif %}

Incremental build

❗ It’s impossible for the current version (up to^1.0.0)! (However, it’s on the list of priorities).

👎 Weakness of 11ty:

  1. There is some change in files, 11ty rebuilds the whole site. It’s painful if we work with markdown files and save them regularly!
  2. Cannot access the site while the building processing.

💡 Idea:

  1. Build manually, e.g. npm run build-local to _site folder.
  2. Copy all files in _site to a so-called folder _live
  3. Run a custom server on folder _site (install npm i http-server -g first)

An example of scripts,

{
  "scripts": {
    "local-build": "ELEVENTY_ENV=local eleventy && mkdir -p _live && cp -Rf _site/* _live/",
    "local-serve": "mkdir -p _live && cp -Rf _site/* _live/ && http-server _live"
  }
}

Working with themes locally?

From version 1.0.0 (currently, I use version 1.0.0-canary.38), we can customize eleventyConfig.ignores right in .eleventy.js. Use this to make different posts folders for local and for remote. Because there are too many posts which are going to be built, it takes a very long time to see the changes. If you just wanna make changes in theme, you need a separated folder (having less number of posts).

For example,

  • Only process posts in notes/* on remote (ignore sample_posts/*).
  • Only process posts in sample_posts/* on local (ignore notes/*).

Nunjucks things

Add a new item to a list,

{% assign a = [1,2] %}
{% assign a = (a.push(3),a) %}

Create a dictionary with nunjucks

{% assign items = {'a': 1, 'b': 2} %}

Add a new key-value to a dictionary,

// .eleventy.js -> create a new filter
eleventyConfig.addFilter('setAttribute', function(dictionary, key, value) {
	dictionary[key] = value;
	return dictionary;
});

// usage
{% assign myDict = {"key1": 0, "key2": 0, "key3": 0}%}
{% assign myDict = myDict|setAttribute('key2', 123) %}
{{myDict['key2']}} // pring "123" as expected

String concatenations,

{# Not working #} {% set url = "/bits/{{ data.slug }}" %} {# Working #} {% set
url = ["/bits/", data.slug] | join %} 

Errors

# TypeError: Cannot read property 'type' of undefined
# => Class comes before ![]() of an image!
# EISDIR: illegal operation on a directory
# Solution:
# Delete _site/ and rebuild!
# ENOTDIR: not a directory...
# Solution:
# Delete _site/ and rebuild!
# Invalid DateTime
# Just for new posts => try to commit (on branch "dev") before building!

References

  1. Official website.
  2. Nunjucks Documentation
  3. Moving from WordPress to Eleventy
  4. From Jekyll to Eleventy - Webstoemp
  5. Creating an 11ty Plugin - SVG Embed Tool - bryanlrobinson.com

The following wiki, pages and posts are tagged with

TitleTypeExcerpt
2021-09-27-jekyllvideo.md post videojs, plugin, include
2021-09-30-wiki-scrollspy.md post Implement Scroll Spy in Jekyll without Bootstrap
2021-10-01-wiki-magnific-popup.md post 지킬 블로그에 이미지 확대 기능 추가하기
2021-10-02-wiki-passing-parameters.md post to include the content from another file in `_includes` folder
Beautiful day it is post create tag-home with stackoverflow search platform
Benchmarking date.today and time.now in ruby post Fri, Oct 15, 21, Time.now is much faster than date.today
Jekyll forloop without post Saturday-where_exp, you can do forloop without a loop using where or where_exp filters
Jekyll + liquid post Monday-jekyll-liquid, jekyll install on mac and ubuntu using docker
how to sort and size tagged post sizes post Mon, Oct 18, 21, without using plugins, create array of posts to get the size of the tag
css tips for web development post Tue, Oct 19, 21, tools, fonts cascading and selectors
more to-dos or mini projects relating this jekyll site post Wed, Oct 20, 21, live-server mathjax issues-to-fix show-box photo TOC Github API
Search configuration post Wed, Oct 20, 21, The search feature uses JavaScript to look for keyword matches in a JSON file. The results show instant matches, but it doesn't provide a se...
Javascript tips at par with this jekyll post Tue, Oct 26, 21, reload page, lose focus, add MathJax to website, hover anchor links, default event, search result navigation, starred repo json
Rules background resources and links post Thu, Oct 28, 21, how to conduct unit testing for individual behavior, jes jasmine vs karma
11ty and jekyll post Sat, Oct 23, 21, secret receipe of customizing 11th and this jekyll
D3js trial and errors post Thursday, trying to integrate with jekyll and data mining
Pages in this site page This theme primarily uses pages. You need to make sure your pages have the appropriate frontmatter. One frontmatter tag your users might find helpful is the ...
jekyll-playground post md/kramdown table, datatable 마크다운 테이블 스타일링
wiki-toc2side.md post CSS및 SCSS에 대한 정리
일단 시작 post 위키의 기본 작성법및 기능 추가 방법