Website stack

Signpost4 Signposts
Seedling2 Seedlings
Tree2 Trees
Chest2 Chests
Success

You behold the origin...

How I built this website

There are two truths behind this website:

  1. I wanted to revive my old website, pgabriel.gitlab.io , over a single weekend.
  2. I love Obsidian but don't want to pay for Obsidian Publish ($8 per month).

Here's how I did it for free (except for the custom domain name).

Requirements

Setup

To be honest, I just followed the steps in the getting started page of the Obsidian Digital Garden plugin. It was very straight-forward.

Publishing

My routine is simple:

  1. Create a new note; optional - use template to populate properties
  2. Write
  3. Set df-publish: true
  4. Use Digital Garden Publication Center to publish

Customization

Obsidian vault setup

My vault is very flat, it looks like:

static/ - for unchanging files like images
	template/ - for obsidian note templates
Swamp/ - contains most pages, except for below
About.md
Career.md
Home.md
Now.md

Make an Obsidian note template for new pages

The properties are kind of hard to find, here is the full set for convenience

---
dg-publish: false
dg-home: false
dg-home-link: false
dg-show-backlinks: false
dg-show-local-graph: false
dg-show-inline-title: false
dg-show-file-tree: false
dg-enable-search: false
dg-show-toc: false
dg-permalink: "mynote"
dg-path: "Advanced/Features.md"
dg-pinned: true
dg-hide: true
dg-hide-in-graph: true
dg-note-icon: 1
---
Attention

I tried using dg-path but it messed with Transclusion

More information about default and advanced properties.

Custom domain from Vercel

Buying a domain from Vercel was very easy, you just have to start here. By keeping the public web parts all on Vercel, it was trivial to add the custom domain to the web app.

Custom avatar

Confession, I did not draw my avatar. Instead, I used ChatGPT Image Generation to turn my LinkedIn profile picture.

My prompt looked something like this:

Please generate a simple cartoon version of the attached image. Keep it to two tones. 

After some adjustments, I then converted the output image to gray-scale.

Info

If you also use AI to make art, please consider supporting a local artist.

Custom favicon

Digital Garden's favicon support is limited to SVG files. I was able to make a custom text favicon in two steps:

  1. Use favicon.io to create a custom text icon pack (I used my initials)
    1. This produces a zip file of several formats, including PNG
  2. Use an online file converter to turn one of the smaller PNG files into SVG

This one is a little tricky... the Digital Garden plug-in supports something called "slots". In this case, it means that you can define custom snippets of code that will be inserted into specific parts of the webpage HTML.

In this case, the slot in question is <footer></footer>, and we want to add code inside this section.

To do this, you have to work with the github repository directly:

  1. Clone the github repo of your website to your local machine
  2. Create the following folder(s) that complete the path: src/site/_includes/components/user/common/footer/
    1. common here means the code will inserted into all footers; you can replace with index or notes to limit scope
  3. Create a new file for each code block you want to inject
  4. Re-build the website

I wanted to center some convenient external links, so I created a custom-footer.njk file and added the following:

<div style="text-align: center;">
    © 2013–2025 
    <a href="https://paologabriel.com">Paolo Gabriel</a> |
    <a href="https://www.linkedin.com/in/paolo-gabriel/">LinkedIn</a> |
    <a href="https://github.com/palol/">Github</a> |
    <a href="https://scholar.google.com/citations?user=x_E1WPAAAAAJ&hl=en">Google Scholar</a> |
    <a href="https://www.paologabriel.com/feed.xml">RSS feed</a>
</div>

See the discussion here

  1. You create a separate file called analytics.njk and add:
<script defer src="/_vercel/insights/script.js"></script>
  1. Then you need to enable Analytics in Vercel

The icon in the bottom right corner is another custom footer. See the discussion here, and reference from pkms-notes.

  1. Create a separate file called theme-switcher.njk and add:
<aside id="floating-control">
    <a  id="emailme" href="mailto:bruvistrue93@gmail.com?subject=Regarding {{title}}&body=Discussing {{meta.siteBaseUrl}}{{permalink}}"><i  icon-name="mail-plus" title="Discuss" aria-hidden="true"></i></a>
    <span id="theme-switch">
        <i class="svg-icon light" icon-name="sun" aria-hidden="true"></i>
        <i class="svg-icon dark" icon-name="moon" aria-hidden="true"></i>
        <i class="svg-icon auto" icon-name="sun-moon" aria-hidden="true"></i>
    </span>
</aside>

    
<script>
    function setThemeIcon(theme) {
        let toAdd;
        switch (theme) {
            case 'dark':
                toRemove = ['auto', 'light'];
                break;
            case 'light':
                toAdd = 'fa-adjust';
                toRemove = ['dark', 'auto'];
                break;
            default:
                toRemove = ['light', 'dark'];
                break;
        }
        document.getElementById('theme-switch').classList.add(theme);
        document.getElementById('theme-switch').classList.remove(...toRemove);
    }
    function setTheme(theme, setIcon) {
        if (setIcon) {
            setThemeIcon(theme);
        }
        if (theme == 'dark') {
            document.body.classList.remove('theme-light');
            document.body.classList.add('theme-dark');
        } else if (theme == "light") {
            document.body.classList.add('theme-light');
            document.body.classList.remove('theme-dark');
        } else {
            theme = (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) ? 'dark' : 'light';
            setTheme(theme, false);
        }
        
    }
    let theme = window.localStorage.getItem('site-theme') || "light";
    setTheme(theme, true);
    window.theme = theme;
    window.localStorage.setItem('site-theme', theme);

    window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(event) {
        const settings = window.localStorage.getItem('site-theme');
        if (!settings || settings == "auto") {
            window.localStorage.setItem('site-theme', "auto");
            setTheme("auto", true);
        }
    });
    document.getElementById('theme-switch').addEventListener('click', function() {
        let theme;
        if (window.theme == 'auto') {
            theme = "dark";
        } else if (window.theme == 'dark') {
            theme = 'light'
        } else {
            theme = 'auto';
        }
        setTheme(theme, true);
        window.localStorage.setItem('site-theme', theme);
        window.theme = theme;
    })
</script>

source

  1. Update custom-style.css by adding:
#floating-control {
  position: fixed;
  color: var(--link-color);
  bottom: 1vmax;
  right: 1vmax;
  font-size: 24px;
  z-index: 999999;
  display: flex;
  flex-direction: row;
  justify-content: flex-end;
  gap: 10px;

  .svg-icon,
  i {
      cursor: pointer;
      height: 24px;
      width: auto;
  }

  #theme-switch {
      .light {
          display: none;
      }

      .dark {
          display: none;
      }

      .auto {
          display: none;
      }
  }

  #theme-switch.light {
      .light {
          display: inline;
      }
  }

  #theme-switch.dark {
      .dark {
          display: inline;
      }
  }

  #theme-switch.auto {
      .auto {
          display: inline;
      }
  }
}

source

See this discussion. You also need to copy over the image files from topobon repo and tag your notes.

Overall, I had to:

  1. Copy over the svg files from topobon repo
  2. Add the forest.njk to notes header slot
  3. Enable all front-matter to be passed through from obsidian
  4. Add dg-note-icon parameter to your note
Tip

It helps to set defaults using the plug-in. For example, I want the "1" icon to be used by default for new notes. You can also add to a template, if that's your thing.

Creating an "index" page

I wanted a view where anyone could see all the published notes in a single, sorted table.

So I used dataform,

About

Generated using Dataview plugin.

Here is the code:

TABLE file.tags as Tags, file.mtime as "Last Modified", file.frontmatter.dg-pinned as Pinned
WHERE file.frontmatter.dg-publish
SORT file.name ASC


© 2013–2025  Paolo Gabriel | LinkedIn | Github | Google Scholar | RSS feed | Contact