A Potpourri of Things I Learned Making This Blog

While working on this blog I learned some cool stuff. I won’t go into detail explaining each thing, but simply knowing that something exists can be pretty useful. So here, in no particular order, I’ll mention the existence of some of the things I learned. If it’s something you haven’t heard of you can then consider it’s usefulness.

Implementing dark mode

I decided it would be cool and trendy to add dark mode to my website. I had no idea where to start, but in the end implementing a switch to toggle dark mode involved:

There are some other things to consider that I won’t go into detail in, such as:

There is no shortage of good articles explaining how to implement dark mode.

Anyway, the two new things I learned were the existence of CSS variables and the localStorage API.

CSS variables

This actually isn’t mandatory but using CSS variables makes it a whole lot easier to implement dark mode. You simply create two themes (light and dark) where each theme will have a set of variables.

For example just in the case of switching the background color of the html element:

:root {
    --dark-background: black;
    --light-background: white;
}

You can declare the variables in :root so as to add them to the global scope.

Then you can conditionally apply a styling depending on a data-* attribute of an element:

html {
    background-color: var(--light-background);
}

html[data-theme='dark'] {
    background-color: var(--dark-background);
}

The localStorage API

Once a user toggles a certain theme you need to remember what theme was chosen. Why? Well if you don’t and you reload the page or go to another page on your site then the theme will go back to the default option which may not be what the user wants.

How can you store the user’s chosen theme? With the localStorage API!

Using it is Super Simple™:

localStorage.setItem('theme', theme);
localStorage.getItem('theme');
localStorage.clear();

Service Workers

“Service workers essentially act as proxy servers that sit between web applications, the browser, and the network (when available).”
- MDN Web Docs

I didn’t know what service workers were, but it turns out they can be incredibly useful, they can:

Some peculiarities regarding service workers:

You can learn more about service workers with this great introductory article.

async and defer attributes for <script> tags

When including an external script in an HTML file, I always simply did this:

<script src="scripts/helloWorld.js"></script>

It turns out that most of the time this isn’t what you want. The way this script is included makes it a render-blocking resource, meaning that when the HTML document is parsed and the parser gets to the script, it will stop parsing the HTML and wait for the script to download and then also wait for the script to run. All of this delays the first paint of your page (when the browser renders the first pixels to the screen).

Both the async and defer attributes tell the browser to not wait on the script when parsing the initial HTML document. The difference between the two is that defer specifically waits to fetch the script until the DOM is fully built, while async just fetches the script in the background and runs it whenever it’s ready.

I now include most of my scripts with the async attribute, I organized them as independent modules that can run whenever the browser fetches them.

In the case of this site, the only case where I don’t use async is the critical JS that I included in the HTML document itself. This JS is important enough (and small enough in size) and I need it to run ASAP, that I put it at the end of the <body> tag. The script in question handles toggling dark mode.

Document state

Something that came up when using the async tag is that I was never sure what state the DOM was in when a certain script would run. This led me to discover that the document can be in any of 3 states when it is loading:

You can read the state by checking document.readyState.

The MDN Web Docs clearly explain the difference between the above three states. All I’ll say is that one state refers to when the document is being parsed, another when parsing is complete but not all sub-resources (such as images) have been loaded, and finally when the document has been parsed.

The key for me was figuring out if a script had a specific time I wanted it to execute and what that time corresponded to in terms of the document’s state. Usually I wanted to run a script that interacted with some elements in the DOM as soon as the DOM could be interacted with (I therefore listened for the DOMContentLoaded event).

A small bug I encountered was that while I had the listener, if I included the script using async, it was possible that the event in question had already been fired so the script wouldn’t do what I wanted it to. Fixing this simply involved checking the value of document.readyState.

-pointer-events CSS property

On the mobile version of this site, I wanted to prevent the user from clicking on anything on the page whenever the navigation menu was pulled out. At first I thought I could just put an element over the main content of the page at a higher z-index and that would prevent anything behind it from being clicked on, but I was wrong.

It turns out there’s a useful CSS property that can be set to achieve this.

.no-click {
    pointer-events: none;
}

min-width and min-device-width ignorance

Something that was driving me wild was that I couldn’t understand why some CSS properties would take effect when the screen width changed size and why others didn’t take effect unless I was in responsive mode in Chrome’s DevTools.

It turns out I was using min-width and min-device-width interchangeably when I really only wanted to use min-width. I hadn’t noticed the difference between the two and this is probably due to certain “inspirations” I got looking at code on Stack Overflow (i.e. copy/pasting code).

As you could probably guess by their respective names *-width activates when the width of the window changes while *-device-width changes according to the width of the device’s screen.

text size adjust

This may seem obvious but when developing a website it’s a good idea to interact with it via different devices and see how it behaves in different circumstances. At one point I noticed that when I browsed this site on my iPhone with Safari and I switched to landscape mode, the font size would blow up.

As it turns out this can be fixed via a CSS property:

.disable-text-inflation {
    text-size-adjust: none;
    -webkit-text-size-adjust: none;
}

This basically tells the browser to not use any text inflation algorithm to resize the text size in certain instances (e.g. mobile Safari when switching to landscape mode).

python server vs node server

For simplicity I was using python to run a local development server:

python3 -m http.server

However I found out I couldn’t play videos using the HTML5 video tag. I just switched to using the http-server npm package and HTML5 videos work fine now.

Icons using SVGs

At first I was using Google’s material icons to host icons on my site. But there were a couple of problems I had with this option:

The solution was to use the Google material icons in SVG format. The source code for the SVGs can be found on GitHub.

I just copy/pasted the SVG into the HTML document directly.

As far as styling the icons with CSS I found out you can use the fill property to color SVGs:

svg {
    fill: black;
}

svg[data-theme='dark'] {
    fill: white;
}

Side note you can’t use the title attribute with the <svg> tag, but there are other ways of giving titles to SVGs.

SASS to organize and minify CSS

When generating Lighthouse reports for my pages I focused on performance for a bit and ran into the conundrum of what to do with my CSS.

The right solution seems to be different for different situations. I read in some cases people wanted to bundle up all the CSS and put it in a <style> tag in the HTML document. However this would not allow for caching of different files. Bundling up all the CSS also isn’t necessarily faster than having separate files, what with parallel requests thanks to the HTTP2 protocol. Besides, in the end the same amount of CSS has to be downloaded.

I opted to split my CSS the following way. Have 1 file with all the CSS that is common to all pages. And another file that is page-specific. So basically each page gets 2 CSS files.

Obviously I didn’t want to actually have 1 giant file for all the CSS that needs to be on every page. While looking for options I stumbled upon SASS, which I had heard of, but this time I wanted to try it out specifically for its ability to combine multiple .scss files into one final .css file. It turned out to be a lovely tool, I split my CSS however was most convenient for development (e.g. having a file just for the style of the navigation bar).

To quickly explain what SASS does it compiles .scss files (which allow you to do cool stuff with easy syntax) into regular .css files. If you prepend your .scss filename with an underscore _ that also specifies to SASS that that file is only to be imported by other .scss and not directly parsed into a .css file itself.

So for example you could have the following files in /input_dir:

common.scss begins with some imports like these:

@use 'module1';
@use 'module2';

You can generate the .css files with the following command: (As a bonus the SASS compiler can also minify the generated CSS file.)

sass input_dir:output_dir --no-source-map --style compressed

In /output_dir you will find two minified CSS files: