Note Boxes!
Published 2024-12-24 09:35:00 -05NOTE:
What I am referring to as note boxes are also referred to more generically as call-out boxes or admonitions boxes. I have not seen it referred to as note boxes before so I have taken it upon myself to contribute to linguistic diversity1.
I decided to write a shorter and more lighthearted blog post after my heaver and significantly darker blog post on foreign affairs (read it here), my topic, as the title suggests, is on note boxes, but not just any note boxes, the ones on my website!
Demo
The way that my blog works is that posts are written in markdown and processed with pandoc into HTML. (For more information on this process read this blog post I wrote.
Anyways, if I write markdown along the lines of this:
::: note
Hello World :::
It will evaluate to the following HTML:
<div class="note">
<p>NOTE:</p>
<p>Hello World</p>
</div>
Which will render like this:
NOTE:
Hello World
The Markdown Side of Things
Now, pandoc
interprets markdown in the
format as this:
::: <names>
<content>
:::
To mean HTML looking like this:
<div class="<names>">
<content>
</div>
The strengths of writing markdown like this is that it is
super easy to do and you don’t repeat yourself at all
(follows DRY). In other words, this type of markdown is very
elegant to write. The weaknesses of writing like this is of
course that by default and without
css
/js
this HTML displays exactly
the same as everything else.
The Style Side of Things
If we wanted to add a yellow background color to all note
boxes, we would write the following css
:
.note {
divbackground-color: yellow;
}
NOTE:
Realistically, that’s not all you would do to get a good
looking box but I want to keep the post simple. The actual
css
for this site can be viewed at my
GitHub
Additionally, each note box has a little tag describing
it (e.g. “Note”, “Warning”, etc). In order to add this tag
without js
we will write this code:
.note::before {
divcontent: "Note";
margin-left: -1em;
}
Since the first <p>
element is now
redundant, we can remove it like this:
.note > p:first-child { display: none } div
This is nice because boxes will be legible without
css
but will look nice with css
as
well. And all of this without a single line of
JavaScript!.
Generalizing
Now all of this is fine and dandy until we want to define
additional note boxes, like Warning
,
Info
, and etc2. As you can see with the
following css
, the naive approach can get
annoying quickly:
/*
* Notes
*/
.note {
divbackground-color: yellow;
/* ... */
}
.note::before {
divcontent: "Note";
margin-left: -1em;
/* ... */
}
.note > p:first-child { display: none; }
div
/*
* Warning
*/
.warning {
divbackground-color: red;
/* ... */
}
.warning::before {
divcontent: "Warning";
margin-left: -1em;
/* ... */
}
.warning > p:first-child { display: none; }
div
/*
* Info
*/
.info {
divbackground-color: gray;
/* ... */
}
.info::before {
divcontent: "Info";
margin-left: -1em;
/* ... */
}
.info > p:first-child { display: none; } div
Even if we use CSS nesting, this code looks no prettier:
.note {
divbackground-color: yellow;
/* ... */
::before {
& content: "Note";
margin-left: -1em;
/* ... */
}
> p:first-child {
& none;
display:
}
}
.warning {
divbackground-color: red;
/* ... */
::before {
& content: "Warning";
margin-left: -1em;
/* ... */
}
> p:first-child {
& none;
display:
}
}
.info{
divbackground-color: gray;
/* ... */
::before {
& content: "Info";
margin-left: -1em;
/* ... */
}
> p:first-child {
& none;
display:
} }
Additionally, we should be able to accommodate much more
boxes, like Hint
, Disclaimer
3, and so on. And we
should have a default box as any box that isn’t covered in
the CSS simply isn’t styled. The solution to this is to stop
repeating ourselves and to generalize. We can do this by
using the [class]
selector and the
attr()
function to abstract over class:
[class] {
divbackground-color: gray;
/* ... */
::before {
& content: attr(class);
text-transform: capitalize;
margin-left: -1em;
/* ... */
}
> p:first-child {
& none;
display:
} }
This is the generic solution, which covers all possible
classes. From here we can define specific cases (this is
because the div.<class>
selector is more
specific than the div[class]
selector):
.note {
divbackground-color: yellow;
}
.warning {
divbackground-color: red;
}
And so thus, our — one could say Object Oriented — CSS solution manages to solve the problem of code duplication by creating a default and implementation and “classes” that override this implementation.
The Pandoc
Side of Things
Now, the avid readers will wonder where the
NOTE:
is added in the build process. This is
something done note with Python (build script), JavaScript,
or CSS. This is actually done via a pandoc
filter written using lua
(filters are invoked
using pandoc --lua-filter <file>
). The
filter takes a
<text>
::: <content>
:::
and transforms it into
<text>
::: <TEXT>:
<content>
:::
Here is the code for the filter:
function Div(el)
if #el.classes > 0 then
local some_word = el.classes[1]
local capitalized_word = string.upper(some_word)
local capitalized_intro = pandoc.Para({pandoc.Str(capitalized_word .. ":")})
table.insert(el.content, 1, capitalized_intro)
end
return el
end
This filter checks for a div element with a non-zero amount of classes, and appends the class name to the content in uppercase.
Comments (Disqus)