Website stack
You behold the origin...
How I built this website
There are two truths behind this website:
- I wanted to revive my old website, pgabriel.gitlab.io , over a single weekend.
- 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
- Github for deploying; you need an account
- Download Obsidian for writing
- Obsidian Digital Garden for publishing
- Vercel for hosting; you can use your Github account
- Obsidian Digital Garden for publishing
- ImprovMX for custom domain email forwarding
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:
- Create a new note; optional - use template to populate properties
- Write
- Set
df-publish: true
- 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
---
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.
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:
- Use favicon.io to create a custom text icon pack (I used my initials)
- This produces a zip file of several formats, including PNG
- Use an online file converter to turn one of the smaller PNG files into SVG
Custom footer
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:
- Clone the github repo of your website to your local machine
- Create the following folder(s) that complete the path:
src/site/_includes/components/user/common/footer/
common
here means the code will inserted into all footers; you can replace withindex
ornotes
to limit scope
- Create a new file for each code block you want to inject
- Re-build the website
Custom footer - links
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>
Custom footer - analytics
- You create a separate file called
analytics.njk
and add:
<script defer src="/_vercel/insights/script.js"></script>
- Then you need to enable Analytics in Vercel
Custom footer - theme switcher
The icon in the bottom right corner is another custom footer. See the discussion here, and reference from pkms-notes.
- 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>
- 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;
}
}
}
Hermitage forest
See this discussion. You also need to copy over the image files from topobon
repo and tag your notes.
Overall, I had to:
- Copy over the svg files from
topobon
repo - Add the
forest.njk
to notes header slot - Enable all front-matter to be passed through from obsidian
- Add
dg-note-icon
parameter to your note
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,