Happy New Year! A big welcome to 2022! I’ve finally updated the look of my blog with a new theme. Well, not really... you see, I’m not very creative, and so, my theme pretty much remains the same... just more purple.

I’ve been waiting for Bulma to support CSS variables before making changes, but at this rate that may never happen as it’s been in v0.93 since 19 June 2021...

So I spent my year end starting a new theme from scratch... and landing up with almost the same design. What a waste of time, no?

Bulma CSS

In addition to what I learnt creating My custom Grav theme back in 2019, this time, CSS enhancements include:

  • More strict use of Bulma’s classes, e.g. m-4 to set margins, rather than littering styles everwhere, plus customizations via saas
  • Use of CSS variables especially for colors, e.g. color:var(--col-text) - this makes it very easy to override colors for dark mode.
  • Use of filter properties like filter:brightness() or filter:hue-rotate() to automate color picking and for dark mode - however, filters apply to an element and its children, which is may be not what I want.
  • Use of transforms rather than positioning elements to avoid any repositioning if possible - and, discovered that Safari does not support using % in transforms, e.g. instead of scale(120%) use scale(1.2)
  • Use of :is() to simplify CSS selectors if possible, e.g. a:is(:hover,:focus,:active) or body :is(h1, h2, h3) - because when compared to a traditional selector list, :is() is forgiving and errors do not invalidate the entire selector list.
  • More thought into media queries, e.g.
    • @media (hover:hover) or not all and (hover:hover) to check that the primary input device supports hovering (i.e. mice and not touch),
    • @media (prefers-reduced-motion: no-preference) to kill unncessary animations / transitions (like the scrolling background image of the hero banner on my home page)
    • and @media print to hide stuff that I think is not worth printing!
  • And similarly, more thought for elements that are user-select-able (to allow copying) or requiring a pointer cursor (to indicate interactivity) using Bulma-supplied helper classes is-unselectable and is-clickable.

Terminal and Switch Components

I also created a terminal UI with just 4 lines of CSS and no images or SVGs. I can wrap markdown code fences with a <div>, so that:

<div class="terminal" markup="1">
<div class="title">hello.app</div>
<pre><code class="language-text">hello world</code></pre>

... gives me:

hello world

And a new style to override the standard check box and display a *switch (toggle) instead - test it here:

<label class="switch"><input type="checkbox"/>Show images</label>

... gives me:

I think these two methods are very unique! But the usual disclaimer: I barely test my code and there are no fallbacks for older browsers!

Rather than provide the CSS code here, go ahead and inspect the code in your browser - remember to check out the ::before, ::after and ::checked pseudo-elements.

I also have an expanding search input field in the Navbar. I wanted to use an in-line SVG for a CSS background-image property for the search icon - this turned out to be much harder than expected because I wanted the icon in white when not focused, but a back when :focus-within-ed, plus also taking care of dark mode which is white no matter what.

Depending on the element, there are various :focus, :focus-within and :active states to consider...

All of these do not need JavaScript.

Reading Process, Dark Mode and Lightbox

Apart from that I added a yellow reading progress indicator right at the top of the page.

Also new is a dark mode toggle button on the bottom left of the page. By default all CSS elements use colors defined for light mode in the :root node, all all colors for dark mode in a prefers-color-scheme: dark media query. I then use JavaScript to manipulate this scheme thereby overriding the system settings - this should work...

My lightbox is also updated - it now responds to both taps and swipes to navigate (tap on the left or right half, or swipe on the image, not on the background).

All these rely on JavaScript.


I also re-looked at my taxonomy of tags, re-tagging every page in the process. With that:

  • I have a re-designed the blog page to more easily understand my taxonomy via categorization of tags.
  • and the archive page lists posts by month (for recent months) and also by year (though I hard coded those)

Bugs and Issues

I simply copied my /user/theme/newtheme and /user/pages folders expecting everything to work, but somehow I had issues!

First off, I think the Grav Multi-site feature introduced recently must have created a bunch of directories based on alternative domains, overriding the settings in /user/config making everything much more complicated - getting rid of all those folders seemed to work best for me.

I also had issues with my archive plugin, and perhaps it has to do with page-specific configurations. Previously, I forked a customized version of the plugin so was not impacted by updates. Anyway, if you have problems, make sure your archives.yaml has a blank section for page_specific_config:.

After go-live, I realized that some images were not loading.

  • I initially thought it was a problem with the PHP GD extension as it seemed JPEGs were affected. I found some posts of users who ultimately discovered that GD was compiled without JPEG support - this is not the case for me.
  • Next, I thought it was a cache issue and spent time deleting, re-creating and altering permissions on the /images folder... to no avail.
  • Then I realized the 404 was due to images missing the cache, for images that did not need to be mainpulated (like resizing retina images). The page would point to /user/pages/xx.blog/xx.post/image.jpg instead of /images/0/1/2/3/4 somewhere. So I re-did my entire .htaccess file, thinking the folder must be blocked somehow - wrong!
  • I don’t know what it is, I think Grav is supposed to allow these URLs to work, but they don't in my case. So I started to re-configure every image to force it to be cached with the ?cache parameter. In my case since I use lightboxes, it’s ![image](image.jpg&lightbox&cache) so that the image shows. However, the order of the parameters matters - cache&lightbox configures the outer <a> to use the cache but not the inner <img> and vice versa. This means that no matter what, there is a link that is still invalid!
  • Finally, I stumbled upon a Gravconfiguration setting so that media is always cached, bypassing these direct links - Configuration > Media > check Cache all images. Sigh.

And even after all this, it is still broken! I have this consistent behaviour: the first time a page is accessed, the image will be retrieved from the cache, but refresh and the page with use the direct image URL instead. Finally, I hard coded the Twig code to force a cache hit. What!?

Finally I now have more pages that refer to other pages, like the home and /archive pages that show content under /blog. This causes issues where Grav does not rebuild these pages when new blog posts are added because it does not see any changes. The solution? Touch the page (or the modular) by adding or changing something in the front-matter, like by upading a date field.

Closing Words

I still like Grav as I’m invested, but it’s becoming a pain to configure Grav... Documentation is not sufficient for troubleshooting. Errors are not obvious at all. New features are barely documented e.g. watermarking. And may be breaking, e.g. the multi-site feature and page-specific configuration mentioned above.

Had I been evaluating a platform today, the fact that Grav now has a Premium tier would have disqualified Grav. Are the simple(r) pure PHP static-site blogging platforms?