Pages

Table of Content

Obsidian Blog Manual

Intro

obsidian-blog is a simple static site generator inspired by Jekyll and written in Python. It allows you to use your atomic notes to compile static pages and posts as MoCs. Sounds interesting, isn't it?

Need to mention, obsidian-blog is not a one-click-ready-to-go blog. It requires some basic codding skills to tinker a bit with handlebars and css to cook what you need.


Installation

The simplest way is to run:

pip install obsidian-blog

Usage

The simplest way to start so far is to copy .blog dir from this repository and tweak css and templates to fit your design.

Writing

For writing purpose, you can start obsidian-blog to watch and serve mode:

obsidian-blog --serve --watch

This one will start a simple http server on the localhost:4200 and rebuild your content on any change.

Building

To build your notes into the static files just run obsidian-blog command. Static site will be in the .build directory.


Directory Structure

Directory Structure

The first thing you need to do is to create a _blog directory in the root of your vault with a structure like on the listing below. The simplest way is to copy it from this example vault.

_blog
├── Post.md
├── _assets
│   └── styles.css
├── _layouts
│   └── main.hbs
└── _pages
    ├── about.hbs
    └── index.hbs

Let me briefly explain each directory inside of _blog:

Note, that there is no posts directory, because, all your posts lives right inside the _blog directory.


Settings

obsidian-blog supports .env, and it seems a pretty good place to define your blog_title and other specific settings. A list of variables is here.

Another way is to use cli flags. Take a look on obsidian-blog --help for a list of supported options:

notes ❯ obsidian-blog --help
obsidian-blog

Static site generator for obsidian.md notes.

Usage:
  obsidian-blog [-d] [-w] [-s] [--port <number>] [--title <string>] [--posts_dir <directory>] [--pages_dir <directory>]

Options:
  -h --help                     Show this screen.
  -w --watch                    Enable watcher
  -s --serve                    Enable web-server
  -p --port=<number>            Web-server port [default: 4200]
  -d --drafts                   Render draft pages and posts

  --title=<string>              Blog title [default: My Blog]

  --version             Show version.

Features

Notes Parsing

Parser starts at 2 main directories: Pages and Posts. It recursively processes all embedded notes if they are placed on the new line and don't have any text around. Parser also supports cycles detection, and shouldn't fail in this case.

Parser recursively collects all entities (such as images, notes, links, etc) from your posts and pages and build a flat list of them.


Assets Processing

During the build, obsidian-blog takes all local files used in your posts and pages, copies them into .build directory and updates their links accordingly.


Headings Generation

obsidian-blog renders a header with note title for each note with a non-empty content. This header has id attribute and can be used as an anchor.

As it was mentioned before, there are plenty of ways to define titles, and you can even skip it to use filename instead.


Handlebars Templates

Both posts and pages are post-processed by handlebars.


Privacy

obsidian-blog don't publish anything explicitly marked as published in the frontmatter section:

---
published: True
---

Non-published notes are still being parsed, but ignored during the rendering.

There is also the draft feature. You can annotate your post draft: True to make it visible if you start obsidian-blog with the --drafts flag.


Title Detection

obsidian-blog has a title-detection mechanism:

  1. If note was included with inline title like [[this | one]], the one will be a title
  2. If note has a title attribute in the frontmatter section, it will be used.
  3. Otherwise a filename will be used as a title.

Filename Title Delimeters

If note has no other title then a filename, and filename contains the delimeter (-), the rightest section will be used as a title. This helps if you name your notes with a category prefix, like Prometheus - PromQL Selectors.md becomes PromQL Selectors. It's a bit opinionated, but this convention saves time.


Dev Mode

Dev mode is useful to write and check your drafts locally:

obsidian-blog -dws

Further Reading Section

If a note has links list in the frontmatter, this links will be rendered just under the note as the Further Reading block

links:
  - name: Example
    url: https://example.com

Further Reading:


Notes Linkification

Another opinionated thing: obsidian-blog doesn't consider obsidian links that has text around as notes that should be unwrapped into the post. It seems natural (at least for me), that this links should be processed as links. obsidian-blog checks if the notes have link attribute in the frontmatter section, and if they do, it replaces this includes as old good html links.

That's really useful on a large vault, when you links to a MoC in a specific note, and you don't want the whole MoC to be populated into your post.



Templates

Global Context

global_context is always accessible in all handlebars templates (layouts, pages) and includes few variables:

{
  "config": Config
  "self": Page, # refers to the active page
  "layouts": dict[str, Layout],
  "posts": list[Page], # list of posts
  "pages": list[Page], # list of pages
})

Config Context

Config is passed to handlebars templates as config variable.

Further Reading:


Layouts

Layouts are old good handlebars layouts compiled with a global_context and page_context or post_context accordingly. There is a simple example layout renders links to all pages and a content.

<!DOCTYPE html>

<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>{{ self.title }}</title>
    <link rel="stylesheet" href="{{ config.public_dir }}/styles.css">
  </head>
  <body>
    <h1 class="menu__header">
      <a href=/>{{ config.blog_title }}</a>
    </h1>
    <div class="menu">
      <ul class="links">
        {{#each pages}}
          <li class="link">
            <a href="/{{this.slug}}">{{this.title}}</a>
          </li>
        {{/each}}
      </ul>
    </div>
    <div class="content">
      <div class="page">
        {{{ content }}}
      </div>
    </div>
  </body>
</html>

In a post or a page, you can specify which one you'd like to use to render the content within the yaml-frontmatter block:

---
title: Hello World
date: 2021-01-09
layout: main
---

If layout is not specified, main will be used as default one.


Active Page Context

To get some data from the current page rendered in a handlebars template, you can refere to self variable, which has properties listed below:

Pages are handlebars templates support a yaml-frontmatter section. Pages stands for anything but posts.

This is an example of index.hbs page renders all posts from the blog:

---
title: Posts
---
<h1>{{ self.title }}</h1>
<ul>
  {{#each posts}}
      <li>
        <span>[{{ this.date }}]: </span>
        <a href="/{{this.slug}}">{{ this.title }}</a>
      </li>
  {{/each}}
</ul>

Further Reading:


Images

Images are processed in 2 steps:

Parsing

All images are parsed into asset_entity objects contains their placeholders, alts, and urls.

Building

During the build image files are copied under .build dir, and urls are updated accordingly. Then images are rendered into HTML.

To point to a specific image, use config.public_dir prefix in a handlebars template.



Deployment

It's quite easy to build and deploy a static site to your own domain:

name: Deploy GitHub Pages

on:
  push:
    branches:
      - "master"

env:
  PYTHON_VERSION: 3.10.2

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v2
        with:
          python-version: ${{ env.PYTHON_VERSION }}
      - name: install obsidian-blog
        run: pip install obsidian-blog

      - name: build static site
        run: obsidian-blog

      - name: Add gh-pages CNAME
        run: echo ${{ secrets.DOMAIN }} > .build/CNAME

      - uses: JamesIves/github-pages-deploy-action@4.1.4
        with:
          branch: gh-pages
          folder: .build

Further Reading: