Preface
It’s been a while since I had the idea of porting the handy shortcode feature of Hugo (current blog framework) to Obsidian (current note-taking app). But there is no such thing as shortcodes in Obsidian and I could not find the exact plugin to achieve this. Occupied with other stuff, I do not give it a try until today. Luckily I solved it with caveats to avoid developing a plugin myself.
Hugo Shortcode
Before I start to discuss the possible porting methods, you should have an idea of Hugo shortcode. I really appreciate the simplicity and possibility.
For example, Steam allows people to use spoiler in the following format: This line contains [spoiler]REDACTED[/spoiler], who is the murder of Sonic the Hedgehog.
And it works like REDACTED.
To achieve this in Hugo, put spoiler.html
in layouts/shortcodes/spoiler.html
:
<span class="spoiler">
{{ .Inner | safeHTML }}
</span>
<style>
.spoiler {
position: relative;
display: inline-block;
cursor: help;
}
.spoiler::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 1px;
color: white;
background: black;
opacity: 1;
transition: opacity 0.7s ease, transform 0.3s ease;
}
.spoiler:hover::before {
opacity: 0;
transform: translateY(-50%) rotateX(80deg);
transition: opacity 1.0s ease, transform 0.5s ease;
transition-delay: 0.3s;
}
</style>
To use spoiler shortcode, follow this syntax: {{< spoiler >}} real spoiler {{< /spoiler >}}
.
Hugo would format the content on the fly when building the site.
Obsidian Alternative
Comfortable with Vim to edit Hugo posts as I am, I still prefer the robust plugin ecosystem sometimes (without the need to mess with Neovim, vim-plug, lua scripts etc.). And I’d like to port Hugo shortcodes based on the fact that my Hugo post template is already replicated with the help of Templater.
Speaking of porting itself, there are several potential solutions in Obsidian:
- QuickAdd
- Linter custom regex replacement + custom CSS
- Callouts
- spoiler-block-obsidian
Overwhelmed by the well-done Movie & Series Script in QuickAdd documentation, I postponed the attempt of Option 1. Also, I do not like the indent style of callouts so Option 3 is called out. Similarly, I don’t buy the story of Option 4 judging from its GitHub repo.
My Solution
Obviously Option 2. I definitely prefer QuickAdd but since I tried the former first (and succeeded), never mind.
This may seem unfeasible in the first place, but is still possible (with caveats).
First, add spoiler.css
in <Vault>/.obsidian/snippets/spoiler.css
:
.spoiler {
position: relative;
display: inline-block;
cursor: help;
}
.spoiler::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 1px;
color: white;
background: black;
opacity: 1;
transition: opacity 0.7s ease, transform 0.3s ease;
}
.spoiler:hover::before {
opacity: 0;
transform: translateY(-50%) rotateX(80deg);
transition: opacity 1.0s ease, transform 0.5s ease;
transition-delay: 0.3s;
}
Then, use custom Regex replacement function provided by Linter. Guess what, you know, dirty hack.
- Create a rule labeled spoiler-prefix, replace
{{< spoiler >}}
with<span class="spoiler">
and keep the default flagsgm
. - Add another rule labeled spoiler-suffix, replace
{{< /spoiler >}}
with</span>
and keep the default flagsgm
. - Voilà!
2024 Update: After a quarter, I finally realized that it’s also possible to use only one rule: simply replace {{< spoiler >}}(.*?){{< /spoiler >}}
with <span class="spoiler">$1</span>
, still gm
of course, this would work as well.
Everything should work fine in this case, but make sure to choose the format wisely to avoid unexpected scenario.
One More Thing
Here is a practise you can try by yourself. This is actually easier as it does not involve custom CSS.
Let’s say I have a Hugo shortcode to add Ruby Text HTML element quickly (check real world example in Juliamo amrilata serpento - Ren’Py 初印象#得名):
<ruby>
{{ if .IsNamedParams }}
{{- .Get "rb" | markdownify -}}
<rp>(</rp>
<rt>{{- .Get "rt" | markdownify -}}</rt>
<rp>)</rp>
{{ else }}
{{- .Get 0 | markdownify -}}
<rp>(</rp>
<rt>{{- .Get 1 | markdownify -}}</rt>
<rp>)</rp>
{{ end }}
</ruby>
{{- print "" -}}
Now try to port it to Obsidian using whatever methods I mentioned in #Obsidian Alternative.
Rule label: `ruby-text`Regex to find: `{{< ruby (.*?) (.*?) >}}`
Mode: `gm`
Regex to replace: check page source ``