🎉 Chúc mừng năm mới 🎉

I’m from Vietnam so Tet is a big holiday. I and my family, we had a lot of fun gathering together. This is the first year Lina had growed up enough for me and my wife to take her travel (last year Tet holiday she was only 1 month old so we had to stay home). This year, we spent the holiday with my parents in an AirBnB at the heart of Ho Chi Minh City, Landmark 81.

A long holiday also means I had some time to do what like most = learning new things. And man, did I enjoy what I found, emacs, org mode, hugo, literate configuration. I was having a great time.

The personal website was a bit old. 2 years now I think. So It was the right time to improve it. I determined and set out about 2 hours a day whenever I can to build a new website during the holiday. And here are my notes on it:


Static site generator

Hugo is one of the most popular open-source static site generators. With its amazing speed and flexibility, Hugo makes building websites fun again.

Yes. All of the quote from their website is true. It was speedy, it was fun ⚡

Hugo quick start
Hugo quick start

I went through the Quick Start with ease and got the site runing.

Then I replaced the Anake theme with Terminal by panr.

And voilà, A cool looking website with a look and feel of my usual working environment as a software engineer.

Go module or Git clone

I was toying arround with Go Module at the start. But when try my hand on editting the theme, Git clone method was the better solution. It let me manage the theme implementation and kept track of my changes.

Hugo folder tree
Hugo folder tree

Note to self, should move the changed from inside the /themes/termial folder to the root folder to apply custom changes.

My understanding

My experience with Hugo was brief (1 week), but I feel the design was really well thought.

Hugo is the build tool that is very well documented, has incredible performance and has great community support. This act as a tool box for all your static website needs, all we have to do is provide the material (the content files) and the decoration (the theme).

Hugo equation
Hugo equation

Emacs and Org mode

Doom Emacs

I got hooked on emacs by watching Distro Tube video What Are The Benefits Of Emacs Over Vim? . Just a few demo of org-mode and I got down to my Macbook Air to it out. It was great experience: great help from the document from both Emacs GNU and Doom Emacs. Other than that, everytime I need something just need to ask Emacs it self by pressing <leader>h. Awesome!!!

I am a Vim user so I used to configure my own tool and tinker around all the internal parts and customization. The setup was not taking long, just a few days and I got the environment for typescript, vue and ready to spin up the website.

Org mode

One of the great feature of Emacs is org-mode I write all of my post in it.

As Emacs is a Graphical Editor it has the ability to display multiple font face, font type and even pictures. Editing in org-mode is a blast for me.

The features

I’d like my personal website to be simple


Ofcouse, it should contains all my writings and posts are must have.

I have all my post in org files and put in /content-org/ folder. Every time I save, I have a script to automatically export to markdown for Hugo to consume. Using ox-hugo.

Below is my example setup for a post

#+hugo_base_dir: ~/Sync/chop-ink/
#+hugo_tags: how howto build website
#+hugo_custom_front_matter: :cover /ox-hugo/howto-build-website_20220203_134312.png
#+hugo_custom_front_matter: :images /ox-hugo/howto-build-website_20220203_134312.png

#+TITLE: How This Website Was Built
#+AUTHOR: Chop Tr (chop.ink)
#+DATE: <2022-02-02 Wed>
#+DESCRIPTION: New year. New website. New journey.

...Page content...

Then everytime I need to export the post just press C-c C-e H h.I know, it’s a long keybind, I used to the acronyms method to remember so it comes very easy for me: Control > Export > Hugo -> hugo away!!!


I have a small script to copy and paste image into the content of a post.

Require: vips, vipsthumbnail, pngpaste


# Location: ~/bin/clipboard-image-paste
# Should be avaiable in PATH

function help() {
  echo "$0 <size> <output_file> <format>"
  echo "Example: $0 1280 example.png \"png[Q=85]\""
  echo "Note: The last argument need to have double quote"

if [[ -z $2 ]]; then
  exit 1

if [[ -z $3 ]]; then

pngpaste "/tmp/pngpaste.png"

# Resize the image if greater than $1 with given $format
output=$(echo "out_pngpaste.$format"| sed -E 's/(out_.*\.)(png|jpg|jpeg|webp).*/\1\2/g')
vipsthumbnail -s "$1x$1>" -o "out_%s.$format" "/tmp/pngpaste.png"

rm /tmp/pngpaste.png
mv "/tmp/$output" $2
(defun org-insert-clipboard-image (&optional file)
  (interactive "F")
  (setq filename (concat file (format-time-string "_%Y%m%d_%H%M%S") ".png"))
  (shell-command (concat "clipboard-image-paste 1280 " filename " \"png[Q=85]\""))
  (insert "#+attr_html: :width 720\n")
  (insert (concat "[[" filename "]]")))

Everytime I need to insert a image I just have to copy the image or screen select it. The image is temporary saved in the clipboard. Then I call the command org-insert-clipboard-image to save it to selected directory and paste the path to current position of the cursor.

My config for emacs for this feature is eplained more here: https://chop.ink/posts/doom.d/config/#insert-clipboard-image-into-org-file

Source code highlight

My current theme is terminal by panr and it use PrismJS to hightlight the source code block.

A little edit and configuration from the PrisimJS site to choose the optimized features for my site then I replace the static file.



I was choosing between Disqus , Utterances. And settle on the later as it use Github issue to manage the comment which is great for developer as all the information I get is in one place. I can check it more regularly than more social oriented solution like Disqus.

Setting it up is in the /layouts/partials/comments.html file

<script src="https://utteranc.es/client.js"
<style type="text/css"></style>

Then viewer of the site will have a nice window to leave comments like this

Image viewer

This bit is a bit more tricky. I found a minimal library https://adrianklimek.github.io/views/ which is just 5.4k and not depends on jquery.

I had to make 2 edits:

Wrap my image (figues) in <a> tag


{{ if .Get "src" }}
<a class="my-figure" href="{{ .Get "src" | safeURL }}">
<figure class="{{ with .Get "position"}}{{ . }}{{ else -}} center {{- end }}"
        {{ if .Get "width" }}
        style="max-width: {{ .Get "width" }}px"
        {{ end }}
  <img src="{{ .Get "src" | safeURL }}"
       {{ with .Get "alt" }} alt="{{ . | plainify }}" {{ end }}
       {{ with .Get "style" }} style="{{ . | safeCSS }}" {{ end }} />
    {{ if .Get "alt" }}
    <figcaption class="{{ with .Get "captionPosition"}}{{ . }}{{ else -}} center {{- end }}" {{ with .Get "captionStyle" }} style="{{ . | safeCSS }}" {{ end }}>
      {{ .Get "alt" | safeHTML }}
    {{ end }}
{{ end }}

This will wrap all my images in a <a> tags and give it a class my-figure.

Script to activate the images viewer

Then in the footer of the site I need to add in a small script so the library can find the images I want to show a nice looking image viewer.


<script src="{{ "/views.min.js" | absURL }}"></script>
<script defer type="text/javascript">
const figures = document.querySelectorAll('.my-figure');
figures.forEach(f => {
   new Views(f);


I use firebase hosting to host my site. It’s a convinient and easy to use option with very low cost.

I have an existing firebase project that I used for my old website. I use the same project so I don’t need to create one.

If you don’t have one, It can be created by following this console page: https://console.firebase.google.com/

Config and Deploy folder

With the firebase cli and firebase project setup I just need to edit the firebase.json file to deploy the build directory from hugo.


  "hosting": {
    "site": "chop-ink",
    "public": "public",
    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
    "headers": [
        "source": "**/*.@(json|ico|css|jpg|jpeg|gif|png)",
        "headers": [
            "key": "Cache-Control",
            "value": "max-age=86400"
        "source": "/prism.js",
        "headers": [
            "key": "Cache-Control",
            "value": "max-age=2592000"

Above, I have config for the cache time of images and the prism.js files which will almost never change.

Then everytime I like to make a new publish to my website I just need to run the commands:

hugo && firebase deploy


The couple of weeks I spend on renewing my website was a great time. I learned a lot of new architecture and have a totally awesome new tool Emacs in my arsenal.

I think this year will be the year I focus on content creating and this website will be the place I put all my writting to.

Chào 🖖