#software RSS

Programs, apps, scripts, etc. See also /uses.

Sassy dark mode

Backward compatible dark mode

The easiest way to implement dark mode these days is with CSS3 custom properties (variables). Just have a :root element with default colors, and then another one under a media query, and let the user preference do the work. Something like this:

:root {
    --bkg:          white;
    --txt:          black;
}

@media ( prefers-color-scheme: dark ) {
    :root {
        --bkg:      black;
        --txt:      white;
    }
}

body {
    background:     var(--bkg);
    color:          var(--txt);
}

And if the browser doesn’t understand or have the preference, it would take the first set of variables. But if it doesn’t understand variables at all, that’s a problem.

I really hope there is a better solution, because mine feels very silly, but it works. Sass variables. Bare minumum primer: Sass is a preprocessor, it creates a regular CSS file and all the magic happens before that. Also, there’s a pain point if you’re adapting an existing stylesheet: it’s better if the color information has its own section. Lucky for me, I have been doing this since time immemorial.

Ok, to get to the point, we need 6 files. Did I mention it’s silly? style.scss is our main file, which would be our actual style.css at the end. _colors.scss is our color information where we will write it with our variables. Two files for the light and dark variables themselves and two to make a magic trick work.

So, the magic trick is this: we import two files into our main file, and both of those files import a set of variables and our colors file:

_vars_light.scss + _colors.scss → _magic_light.scss

_vars_dark.scss + _colors.scss → _magic_dark.scss

_magic_light.scss + _magic_dark.scss → style.scss

So style.scss would have this:

@import "magic_light";
@media ( prefers-color-scheme: dark ) {
    @import "magic_dark";
}

_magic_light.scss would have this:

@import "vars_light";
@import "colors";

_vars_light.scss would have this:

$bkg:               white;
$txt:               black;

And _colors.scss would have this:

body {
    background:     $bkg;
    color:          $txt;
}

_magic_dark.scss and _vars_dark.scss would “mirror” _magic_light.scss and _vars_light.scss.

BTW, there may be different requirements for how Sass files are handled, I just go by what Hugo does with them and what works for me.

In the end, we have a rather large style.css file with a default light style and a dark style under a media query. Kind of like what we did with CSS3 variables, but that works without them. Silly!


A bit more about Sass.

Index for Hugo

There are many ways to organize your content in Hugo, including custom taxonomies. But I wanted something simpler: a page that lists all the “things” I have written about, with links to the corresponding posts. Here is how I figured it out.

Maybe not the best or most elegant way, but it works and since Hugo is a static site generator, no one will ever know! :)

“Index” is a bit confusing in the web world, but I mean it as “an alphabetical listing of items and their location”.

Content

So on the content side, we need to add this to front mater of each post:

items:
- Name of a thing
- Name of another thing
- Etc.

If there are some pesky characters like colon, the name should be enclosed in quotes: - "Thing: Not the other thing!"

Dictionary

Then we need a list.html in layouts/map/ folder. map can be whatever we want. I won’t cover how to deal with layouts. Here is the main thing, I will go through the details after that:

{{ $mapItems := dict }}

{{ range .Site.RegularPages }}
    {{ $mapDate := .Date | time.Format "060102150405" }}
    {{ $mapLink := .Permalink }}
    {{ $mapTitle := .Title }}
    {{ with .Params.items }}
        {{ range . }}
            {{ $mapItem := . }}
            {{ $mapUnique := printf "%s%s" $mapItem $mapDate }}
            {{ $mapUnique := replaceRE "^The |^An |^A " "" $mapUnique }}
            {{ $mapItems = merge $mapItems (
                dict $mapUnique (
                    slice $mapLink $mapItem $mapTitle
                )
            ) }}
        {{ end }}
    {{ end }}
{{ end }}

{{ $mapItems := dict }} creates a dictionary named $mapItems. Dictionary is a list of things where you have a key with attached value (one or many things).

I use $mapSomething for names. Can be anything.

With {{ range .Site.RegularPages }} we want to look through all the pages we have. It’s a loop that goes through them. Maybe that’s too much, there are ways to narrow it down. But if a page doesn’t have items: in its front matter, it won’t show up anyway.

Then we create three variables $mapDate, $mapLink, and $mapTitle that we are going to work with. With $mapLink and $mapTitle we are just assigning them to be (:=) link and title of our pages.

With $mapDate, we also pipe it (|) into time.Format to look like a string of numbers, with two for each year, month, day, hours, minutes, and seconds. We will use it to make our keys look unique, so if we have two posts that share the same item, they will not override each other.

{{ with .Params.items }} limits our pages to only those that have items: in the front matter.

Then we create another loop {{ range . }}. . (dot) represents the thing we just worked with from the previous command. So, the items:.

{{ $mapItem := . }} creates another variable. These are the items.

Then, for simplicity and readability, we create our unique key with two commands. First, we join the name of the item with the date of the post:

{{ $mapUnique := printf "%s%s" $mapItem $mapDate }}

Second, we remove articles (“A”, “An”, “The”) if the names of the items begins with them. This is obviously unnecessary, but why not!

{{ $mapUnique := replaceRE "^The |^An |^A " "" $mapUnique }}

Finally, we merge our dictionary (which is empty at the beginning) with the new entries we’ve created with our variables:

{{ $mapItems = merge $mapItems (
    dict $mapUnique (
        slice $mapLink $mapItem $mapTitle
    )
) }}

A bit of translation: $mapItems will now be equal to previous $mapItems plus new ( dict ), where the first thing $mapUnique is a key and a second thing ( slice ) is a value. The fact that it is a slice just means that there are multiple things in it and we can get them out individually.

To summarize, what we do is two loops. The first one goes through our posts and gets their date, link and title. Within it, so for each post, the second loop goes through our items: and adds them to our dictionary of things, with unique key and some stuff value that we will use to render our list.

List

Speaking of which, this is how we will render it:

{{ range $key, $value := $mapItems }}
    <li><a
        title="From {{ index $value 2 }}"
        href="{{ index $value 0 }}">
        {{ index $value 1 }}
    </a></li>
{{ end }}

We again use a loop, this time for the dictionary we created. It’s going to sort by key, that’s why we removed those articles so the list can be sorted in a nicer way.

In this loop, we use index to get the specific $value. They start from 0 and go as we set them with slice, post link, item, post title. Title is also unnecessary, but it will show up on hover and will be helpful if there are multiple items.

Conclusion

On my Inventory page, I decided to split all items into multiple categories. I did it very simply, but in a way that is easy for me to work with. Instead of having just items: in front matter, I have three different things: iGames:, iCinema: and iMisc:. Then in the list.html, I repeated the second loop three times, for each of these three things ({{ with .Params.iGames }}, etc.), and each of them adds items to the corresponding dictionary ({{ $mapItemsGames := dict }}, etc.). Then I render every dictionary as its own list.

I am more than sure that there are other ways to accomplish the same thing. There are always different ways to manipulate data. Dictionary just made the most sense to me, with a key that can be used to sort things nicely and then pull things up from value to display in a way I want.

My so called CLIfe

Clickity-clackity

Screenshot of a Windows terminal with a command prompt styled by Oh My Posh. Animated gift of a sleeping cat in the corner.
[Terminal companion]

When I started with HTML and CSS, I was using Dreamweaver. It was producing pretty clean code, so many people were recommending it. After some time and the Adobe acquisition, I moved away from visual editors to plain text editors. Now, while something like Visual Studio Code is preferable, I can basically use any text editor.

Something similar seems to have happened with the command line interface. Slowly, through some other programs like Launchy and Everything (alpha with dark mode), I got the idea that text is not only sufficient, but in many cases faster, better and more robust than GUI analogues.

I use Scoop for about 95% of my installs, winget for another few percent, and I would struggle to remember what I installed any other way, especially since creating your own Scoop bucket is pretty simple – here’s mine.

I check for Windows updates with a PowerShell module, I sync files with Robocopy and Rclone, I even install games from the Epic Store with Legendary, among other things.

Hey, I make this blog with Hugo and Git!

Is the CLI good for everything? Of course not! But it is pretty useful.


Cat in the image above, Shalquior from “Dark Souls II”, is by Zedotagger.