How to write a Jekyll post
This guide walks through writing a post with the Chirpy template. Even if you’ve used Jekyll before, it’s worth a skim since some features need specific variables.
Naming and Path
Create YYYY-MM-DD-TITLE.EXTENSION in _posts at the root. EXTENSION must be md or markdown. To save time, use Jekyll-Compose.
Fonts
Front image: Coolvetica RG, size 36 in paint.net. Export to WebP at 1200x630.
Media conversion
Convert PNG files to WebP for this site:
1
2
sudo apt install webp
sudo pacman -Syu webp
To convert an image to WebP, -q sets output quality and -o sets the output file.
1
cwebp -q 85 myimg.png -o myimg.webp
Custom script to convert all .png files to .webp in a folder:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/bin/bash
usage() {
echo "Usage: $0 [-q quality]"
echo " -q quality : Set the quality for the webp conversion (default is 85)"
exit 1
}
quality=85
while getopts "q:" opt; do
case ${opt} in
q )
quality=$OPTARG
;;
\? )
usage
;;
esac
done
current_dir=$(basename "$PWD")
new_dir="${current_dir}_conv"
mkdir -p "$new_dir"
for file in *.png; do
if [ -f "$file" ]; then
new_file="${new_dir}/$(basename "${file%.png}.webp")"
cwebp -q "$quality" "$file" -o "$new_file"
echo "Converted $file to $new_file with quality $quality"
fi
done
for file in *.jpg *.jpeg; do
if [ -f "$file" ]; then
new_file="${new_dir}/$(basename "${file%.*}.webp")"
cwebp -q "$quality" "$file" -o "$new_file"
echo "Converted $file to $new_file with quality $quality"
fi
done
echo "Conversion complete!"
exit 0
Front Matter
Basically, you need to fill the Front Matter as below at the top of the post:
1
2
3
4
5
6
---
title: TITLE
date: YYYY-MM-DD HH:MM:SS +/-TTTT
categories: [TOP_CATEGORIE, SUB_CATEGORIE]
tags: [TAG] # TAG names should always be lowercase
---
The posts’ layout has been set to
postby default, so there is no need to add the variable layout in the Front Matter block.
Timezone of Date
To record the release date accurately, set timezone in _config.yml and include the post’s timezone in date in the Front Matter. Format: +/-TTTT, e.g., +0800.
Categories and Tags
categories should have up to two items; tags can have as many as you like. For example:
1
2
3
4
---
categories: [Animal, Insect]
tags: [bee]
---
Author Information
Author info usually comes from social.name and the first entry in social.links. If you want to override it, add author data in _data/authors.yml (create it if it doesn’t exist):
1
2
3
4
<author_id>:
name: <full name>
twitter: <twitter_of_author>
url: <homepage_of_author>
Then use author for a single entry or authors for multiple:
1
2
3
4
5
---
author: <author_id> # for single entry
# or
authors: [<author1_id>, <author2_id>] # for multiple entries
---
The author key can also point to multiple entries.
Reading author info from
_data/authors.ymladds thetwitter:creatormeta tag, which enriches Twitter Cards and helps SEO.
Post Description
By default, the first words of the post show up on the home page, in Further Reading, and in the RSS feed. To override the auto-generated description, set description in the Front Matter:
1
2
3
---
description: Short summary of the post.
---
That description also appears under the post title on the post page.
Table of Contents
By default, the Table of Contents (TOC) appears on the right. To turn it off globally, set toc to false in _config.yml. To disable it for one post, add this to its Front Matter:
1
2
3
---
toc: false
---
Comments
Set the global comments switch with comments.active in _config.yml. Once you pick a system, comments are on for all posts.
To turn off comments for a single post, add this to its Front Matter:
1
2
3
---
comments: false
---
Mathematics
We use MathJax for math. It’s off by default for performance, but you can enable it with:
1
2
3
---
math: true
---
After enabling math, use these patterns:
- Block math:
$$ math $$with mandatory blank lines before and after$$- Equation numbering:
$$\begin{equation} math \end{equation}$$ - Reference numbering:
\label{eq:label_name}in the block and\eqref{eq:label_name}inline
- Equation numbering:
- Inline math (in lines):
$$ math $$with no blank lines before or after - Inline math (in lists):
\$$ math $$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!-- Block math, keep all blank lines -->
$$
LaTeX_math_expression
$$
<!-- Equation numbering, keep all blank lines -->
$$
\begin{equation}
LaTeX_math_expression
\label{eq:label_name}
\end{equation}
$$
Can be referenced as \eqref{eq:label_name}.
<!-- Inline math in lines, NO blank lines -->
"Lorem ipsum dolor sit amet, $$ LaTeX_math_expression $$ consectetur adipiscing elit."
<!-- Inline math in lists, escape the first `$` -->
1. \$$ LaTeX_math_expression $$
2. \$$ LaTeX_math_expression $$
3. \$$ LaTeX_math_expression $$
Starting with
v7.0.0, MathJax options live inassets/js/data/mathjax.js. Adjust them as needed, e.g., add extensions.
If you’re building withchirpy-starter, copy that file from the gem install directory (seebundle info --path jekyll-theme-chirpy) into your repo.
Mermaid
Mermaid is great for diagrams. To enable it for a post, add this to the YAML block:
1
2
3
---
mermaid: true
---
Then use it like any other fenced block: wrap the graph code with ```mermaid and ```.
Images
Caption
Add italics on the line after an image to show a caption below it:
1
2

_Image Caption_
Size
Set width and height on each image to avoid layout shifts while it loads:
1
{: width="700" height="400" }
For an SVG, you have to at least specify its width, otherwise it won’t be rendered.
Starting from Chirpy v5.0.0, height and width support abbreviations (height → h, width → w). This has the same effect:
1
{: w="700" h="400" }
Position
Images center by default. To position them, use normal, left, or right.
Once the position is specified, the image caption should not be added.
Normal position
The image is left aligned in this example:
1
{: .normal }
Float to the left
1
{: .left }
Float to the right
1
{: .right }
Dark/Light mode
You can swap images for dark/light mode. Provide two images and assign the dark or light class:
1
2
{: .light }
{: .dark }
Shadow
Program window screenshots work well with the shadow effect:
1
{: .shadow }
CDN URL
If media lives on a CDN, set cdn in _config.yml to avoid repeating the URL:
1
cdn: https://cdn.com
Once set, the CDN URL prefixes all media paths starting with / (avatars, images, audio, video).
For instance, when using images:
1

The parsing result will automatically add the CDN prefix https://cdn.com before the image path:
1
<img src="https://cdn.com/path/to/flower.png" alt="The flower" />
Media Subpath
For posts with many images, avoid repeating paths by setting media_subpath in the YAML block:
1
2
3
---
media_subpath: /img/path/
---
Then reference images by filename only:
1

The output will be:
1
<img src="/img/path/flower.png" alt="The flower" />
Preview Image
For a top-of-post image, use a 1200 x 630 image. If it isn’t 1.91 : 1, it will be scaled and cropped.
With that in mind, set the image attributes:
1
2
3
4
5
---
image:
path: /path/to/image
alt: image alternative text
---
media_subpath also applies to the preview image. If it’s set, path only needs the filename.
For simple use, you can also just use image to define the path.
1
2
3
---
image: /path/to/image
---
LQIP
For preview images:
1
2
3
4
---
image:
lqip: /path/to/lqip-file # or base64 URI
---
You can see LQIP on the preview image of this post.
For normal images:
1
{: lqip="/path/to/lqip-file" }
Pinned Posts
You can pin one or more posts to the top of the home page; pinned posts sort in reverse release order. Enable with:
1
2
3
---
pin: true
---
Prompts
Prompt types include tip, info, warning, and danger. Add class prompt-{type} to a blockquote. For example:
1
2
> Example line for prompt.
{: .prompt-info }
Syntax
Inline Code
1
`inline code part`
Filepath Highlight
1
`/path/to/a/file.extend`{: .filepath}
Code Block
Markdown fences ``` create a code block:
1
2
3
```
This is a plaintext code snippet.
```
Specifying Language
Using ```{language} you will get a code block with syntax highlight:
1
2
3
```yaml
key: value
```
The Jekyll tag
{% highlight %}is not compatible with this theme.
Line Number
By default, all languages except plaintext, console, and terminal will display line numbers. When you want to hide the line number of a code block, add the class nolineno to it:
1
2
3
4
```shell
echo 'No more line numbers!'
```
{: .nolineno }
Specifying the Filename
You may have noticed that the code language will be displayed at the top of the code block. If you want to replace it with the file name, you can add the attribute file to achieve this:
1
2
3
4
```shell
# content
```
{: file="path/to/file" }
Liquid Codes
If you want to display the Liquid snippet, surround the liquid code with {% raw %} and {% endraw %}:
1
2
3
4
5
6
7
{% raw %}
```liquid
{% if product.title contains 'Pack' %}
This product's title contains the word Pack.
{% endif %}
```
{% endraw %}
Or adding render_with_liquid: false (Requires Jekyll 4.0 or higher) to the post’s YAML block.
Videos
Video Sharing Platform
You can embed a video with the following syntax:
1
{% include embed/{Platform}.html id='{ID}' %}
Platform is the lowercase platform name and ID is the video ID.
The table below shows how to grab those values from supported platforms:
| Video URL | Platform | ID |
|---|---|---|
| https://www.youtube.com/watch?v=H-B46URT4mg | youtube | H-B46URT4mg |
| https://www.twitch.tv/videos/1634779211 | twitch | 1634779211 |
| https://www.bilibili.com/video/BV1Q44y1B7Wf | bilibili | BV1Q44y1B7Wf |
Video File
To embed a video file directly, use:
1
{% include embed/video.html src='{URL}' %}
Where URL points to a video file, e.g., /assets/img/sample/video.mp4.
You can also pass attributes:
poster='/path/to/poster.png'- poster image for a video that is shown while video is downloadingtitle='Text'- title for a video that appears below the video and looks same as for imagesautoplay=true- video automatically begins to play back as soon as it canloop=true- automatically seek back to the start upon reaching the end of the videomuted=true- audio will be initially silencedtypes- specify the extensions of additional video formats separated by|. Ensure these files exist in the same directory as your primary video file.
Example with everything set:
1
2
3
4
5
6
7
8
9
10
{%
include embed/video.html
src='/path/to/video/video.mp4'
types='ogg|mov'
poster='poster.png'
title='Demo video'
autoplay=true
loop=true
muted=true
%}
Avoid hosting video files in
assetssince PWA won’t cache them and it may cause issues. Use a CDN instead, or a folder excluded from PWA (seepwa.deny_pathsin_config.yml).
Audios
Audio File
To embed an audio file directly, use:
1
{% include embed/audio.html src='{URL}' %}
Where URL points to an audio file, e.g., /assets/img/sample/audio.mp3.
You can also pass attributes:
title='Text'- title for an audio that appears below the audio and looks same as for imagestypes- specify the extensions of additional audio formats separated by|. Ensure these files exist in the same directory as your primary audio file.
Example with all options:
1
2
3
4
5
6
{%
include embed/audio.html
src='/path/to/audio/audio.mp3'
types='ogg|wav|aac'
title='Demo audio'
%}
Avoid hosting audio in
assetssince PWA won’t cache them and it may cause issues. Use a CDN instead, or a folder excluded from PWA (seepwa.deny_pathsin_config.yml).
Learn More
For more knowledge about Jekyll posts, visit the Jekyll Docs: Posts.