Website... this website

You should start a website. It’s quite fun!

2025-05-11

This website is split between projects section and a blog. The first one is for long running ideas. Sometimes I build something without documenting the steps, but I’d like to be better about documenting the steps that got me there. Thing is, it’s difficult to follow any updates on my website. Say I update a project, with a new entry, how are you supposed to know?

New blog posts are easier to spot. “Oh I don’t remember reading this thing I see at the top of the list”. Project updates are buried inside the project page.

A while back I have added the “last updated” on the project banners, but that doesn’t really solve the issue. It’s more of a heuristic telling you if the project is alive or frozen.

Now you can see how long I have procrastinated on a project!
Now you can see how long I have procrastinated on a project!

Could I build something that lists any new entry, be it in the projects section or the blog section? Think chronological list of all new content entries.

Thing is, each project was always a single Markdown file, index.md. Since I’m using Hugo, to generate this website, index.md gives me a single index.html, for each project directory.

index.md used to look something like this:

Here's the steps I took:

{{< date_decorated year="2091" month="06" day="12" >}}

Ok, I think I know why no one did it yet.

{{< date_decorated year="2001" month="06" day="10" >}}

I have started working on an orbital laser. Thing seems easy as hell.
No idea why nobody did it yet.

Each step was included in the index.md with a custom shortcode, which inserted a pretty date separator, in between each entry. It’d generate HTML that looks like so:

Two project update entries.
Two project update entries.

With this setup the only thing I could do is to ask Hugo to split the contents of index.md, using a string separator. Treating that entire Markdown file like a raw string? Yuck!

A way to access these updates one by one, using Hugo, is to use Page Bundle. The idea is to split these updates into separate files. These files become page’s resources.

content/
└── project/
    └── that_cool_project/
        ├── update_001.md
        ├── update_002.md
        ├── image-1.jpg
        └── index.md

This isn’t perfect. Keeping all the content in one file was always convenient. This however enables me to achieve the original goal and brins some other benefits, e.g. putting date_decorated in the actual HTML template.

Since the content is split between multiple files, the HTML template needs to stitch them together. Here’s the implementation of that, stored in themes/my_theme/layouts/_default/single.html.

{{ define "main" }}

<article role="main" class="blog-post">
  <h1>{{ .Title }}</h1>

  {{ .Content }}

  {{ $fragments := .Resources.ByType "page" }}
  {{ $fragments := where $fragments "Draft" "ne" true -}}
  {{ range $frag := $fragments }}
    <div>
      {{ with $frag.Date -}}
      <a href="#{{ .Year }}-{{ .Format "01" }}-{{ .Format "02" }}">
        <div class="date-decorate" id="{{ .Year }}-{{ .Format "01" }}-{{ .Format "02" }}">
          <img src="/imgs/triangle.svg" style="transform: rotate(180deg); margin-top: 0px; height: 1em;">
          <div style="margin-top: 0px;">{{ .Year }}-{{ .Format "01" }}-{{ .Format "02" }}</div>
        </div>
      </a>
      {{- end }}
      {{ $frag.Content }}
    </div>
  {{- end -}}
</article>

...

{{ end }}

The {{ .Title }} and {{ .Content }} come from the project’s index.md. Standard stuff at this point. Then I grab all the resources of type page. That will give me all the .md files for that project, not including index.md.

A nice property pops up at this point. Each Markdown resource can have its own front matter, with a draft field. That means I can filter out resources that present themselves with a draft set to true.. I immediately started using it with some updates, I have failed to polish to a point to which they can be published.

For each fragment I grab its Date field, from its front matter, and print it using my pretty date separator. I then print the contents of the resource, using $frag.Content.

At this point everything works the same as before I have started this work, except the project updates are split into separate Markdown files, with their own front matters. It’s a good first step to implement the website’s activity log.

I have created a new page template theme/my_theme/layouts/timeline/single.html and its content page content/timeline.md, with contents:

---
title: Timeline
comments: false
type: timeline
---

Chronological list of activity of this website!

It explicitly asks for the timeline directory, from layouts, with the type field of its front matter. It doesn’t use the layout field so it’ll use the default single.html template.

Lets look at the template.

{{ define "main" -}}

<h1>{{ .Title }}</h1>

{{ .Content }}

{{- /*
Trying to get all contents + page resources to list all entries.
*/ -}}

{{- $top := . -}}
{{- $pages := where .Site.RegularPages "Section" "in" (slice "project" "post") -}}
{{- $top.Scratch.Set "all" (slice) -}}
{{- range $pg := $pages -}}
  {{- $fragments := $pg.Resources.ByType "page" -}}
  {{- if (gt (len $fragments) 0) -}}
    {{- range $fragments -}}
      {{- $top.Scratch.Add "all" . -}}
    {{- end -}}
  {{- end -}}
  {{- $top.Scratch.Add "all" $pg -}}
{{- end -}}

{{- $sorted := sort ($top.Scratch.Get "all") "Date" "desc" -}}
{{- $sorted := where $sorted "Draft" "ne" true -}}

{{ range $i, $group := $sorted.GroupByDate "2006" }}
  <div class="date-decorate">
    <img src="/imgs/triangle.svg" style="transform: rotate(180deg); margin-top: 0px; height: 1em;">
    <div style="margin-top: 0px;">{{ $group.Key }}</div>
  </div>
  {{ range $group.Pages }}
    <div>
      {{ $date := ( .Date | time.Format "2006-01-02" ) -}}
      {{- /*
      I want to detect a new project entry. I do it through
      checking if I'm handling index.md file.
      */ -}}
      {{- if eq .File.LogicalName "index.md" -}}
      <h1>{{ $date }} {{ .Title }} - NEW {{ .Section }}!</h1>
        {{ .Content | strings.Truncate 120 -}}
        {{/*
        I need to manually build a link here, because
        a resource of type page won't have .Permalink set.
        */}}
        <a href="{{ printf "%s/%s/%s/#%s" .Site.BaseURL .Section .Slug $date }}">more...</a>
      {{- else -}}
      <h1>{{ $date }} {{ .Title }} - {{ .Section }} update!</h1>
        {{ .Content | strings.Truncate 120 -}}
        {{- /*
        I need to manually build a link here, because
        a resource of type page won't have .Permalink set.
        */ -}}
        <a href="{{ printf "%s/%s/%s/#%s" .Site.BaseURL .Section .Slug $date }}">more...</a>
      {{- end }}
    </div>
{{- end -}}
{{- end -}}
{{- end }}

Once again we grab the {{ .Title }} and {{ .Content }}, from the index.md. Then I create a named object, so I can carry it’s Scratch throughout different scopes. It becomes a container for data, which stays valid throughout the entire template.

I fetch all the project and blog section (directory) pages. It’s a list of Page objects. For each of the pages I need to grab its page resources, and append them to the same list. At this point I have a flat list of both blog and project pages, with their page resources. I drop all those with draft field set to true. At this point the data is ready, to generate a list of blog posts, projects and their updates.

I group that data by year, just to bo able to add a year marker between the entries. In each year’s block I start outputting the actual entries, using their summaries, via {{ .Content | strings.Truncate 120 }}.

Two more things to mention. I detect if I’m handling a page with contents in index.md. These are the main files for a project, which I create when I add a new project to the content directory. I don’t use index.md for blog posts. Thus it’s fair for me to assume that whenever I handle a index.md file, I’m printing a new project entry, and I can mark it with a different message of <h1>{{ $date }} {{ .Title }} - NEW {{ .Section }}!</h1>. For all other entries I print <h1>{{ $date }} {{ .Title }} - {{ .Section }} update!</h1>. Which will expand to ... post update! or ... project update!. I can now style new project entires, which I consider the biggest and most interesting updates.

Second thing is that I add a <a href="...">more...</a> with a href pointing to an id generated in the previous template, which generates the actual project page. That means that when you press the more... text you’ll open the project’s page and be moved to that specific entry. I have to build the URL for that link manually, because a resource object doesn’t carry the Permalink field, from its parent index.md.

I add the href unconditionaly and the entries generated from index.md wont’ have the corresponding ids. That’s not a problem, because using href that isn’t present on the target page results in… nothing.

That’s it. While the https://ciesie.com/timeline requires some styling, for better readability and user experience, it does what I wanted to achieve.

This won’t show, an eye catching, “NEW” next to posts your browser hasn’t visited, because I’m not using any cookies in your browser (I don’t plan to). It’s still a nice thing I want to continue building on.

2024-10-24

Sometimes I get to improve this website by accident. I stumble on an interesting project, that fits well with what I still haven’t addressed. This time it’s superhtml. It checks HTML files for syntax errors and reformats them. It can be used as a cli application or as a LSP server.

After Hugo builds this website, the HTML isn’t that well formatted. It’s an eye sore that doesn’t have many functional consequences. It could be “solved” by minifying the HTML. However, I’m not really interested in minifying my HTML, for reasons that feel logical but I don’t have evidence to back them up.

The error checking that superhtml provides is more interesting to me. I have caught an embarrassing amount of issues, after adding this step to my CI:

superhtml fmt public/

More importantly, since it’s part of my CI, I’ll notice any future mistakes I might introduce in my HTML.

It’s also great to see that superhtml was written in Zig, considering I support Zig financially. It also means that I don’t have to touch npm or any other cancerous environment. It’s a single binary that you pull, unpack and run. What’s more is that it has no dynamic dependencies.

mc@computer:~/Downloads/x86_64-linux-musl$ ldd superhtml
	not a dynamic executable

Very zen.

2023-12-29

This article, titled “CSS is fun again”, put the OKLCH color space on my radar. It’s a color space, created in 2020, which covers capabilities of the most common, sRGB screens, but also the newer ones, like those using P3 or Rec. 2020 color spaces.

Oklab and OKLCH were created by Björn Ottosson, to fix issue in the CIE LAB and LCH color spaces. That matters if you’re curious why there’s so many abbreviations: CIE LAB (or Lab), Oklab, OKLCH. Let’s not even touch the topic of varying capitalization…

Oklab uses Cartesian coordinates to encode a point in its color space. OKLCH uses polar coordinates. That’s why an OKLCH encoded color, in CSS syntax, looks like so oklch(65.39% 0.3 10.2) - the first value being lightness (it’s polar coordinates, so imagine it’s moving on its vertical axis, often label Z), the second chroma (a bit of an arbitrary value that, for sRGB and P3, stays below 0.37 - that’s the radius from the cylinder’s center), the third hue (this maps to the angle, often labeled as the theta angle - this one can be between 0 and 360, which covers the entire cylinder).

If you’d like to know more about the math and history then you should check this article, by Evil Martians.

Let’s go back to the practical side of things. I’m using SASS for this website (well it’s SCSS to be more precise - there are two different syntaxes one can use). SASS extends CSS, through new functionality but it also adds a build step, where it translates it’s own syntax into the currently, widely supported CSS. It’s an additional dependency that, while useful, adds complexity. I’d like to drop SASS completely.

One of the features that SASS brought were variables. Those can be used like so:

$deep-red: #d30054;

.content {
  background: $deep-red;
}

CSS managed to catch up and nowadays it supports variables, albeit using a different syntax:

:root {
  --deep-red: #d30054;
}

.content {
  background: var(--deep-red);
}

Variables are just one feature. Another one was CSS selectors nesting. By now it’s also supported in the vanilla CSS. There’s way more to SASS, but I’m not the person to explain those features and their benefits - I just don’t use them.

I use a few color modification functions, like desature, darken, etc., that are still missing in plain CSS. SASS team can move quicker than W3C, the developer of the CSS technical specification, and the browsers’ teams. As soon as the CSS Color Module Level 5 gets finalized and gets implemented, in all major browsers, I will be able to fully drop SASS.

Here is an example of something you can do in SASS, right now:

$deep-red: oklch(55.39% 0.221 10.2)

$bright-red: scale-color($deep-red, $lightness: 20%);

Here is plain CSS, that does the same, which isn’t yet supported by Firefox:

:root {
  --deep-red: oklch(55.39% 0.221 10.2);
  --light-red: oklch(from var(--deep-red), calc(l + 20%) c h);
}

Here’s the link to check which browsers support the Relative Oklch colors.

I have moved my CSS file closer to plain CSS. One things that’s worth mentioning is that I might be excluding users with older versions of browsers installed. In order to address that I’d like to explore Lightning CSS. That might become my new dependency, but one that solves and issue I have ignored for quite a while now.

2023-08-23

It’s been quite rewarding to improve this website here and there. My new, HTMX based comment system isn’t fully ready. The functionality is there but it’s lacking a good CSS. You might notice that the comments are visually very… basic.

Working on my comments system required some NGINX work. I had to prepare a config file which correctly redirects the traffic to my comments server. I have found bug in my configuration file for this website, which made www.ciesie.com not redirect to the http://ciesie.com. That’s fixed now.

I have also experimented with HTML’s <picture> element. It allows you to implement a responsive images system - the browser chooses an appropriate image, from a provided list. The basic setup looks like this:

<picture>
  <img src="small_img.webp"
       height="420"
       srcset="small_img.webp 400w, medium_img.webp 800w, big_img.webp 1200w"
       alt="this should be a responsive image"
</picture>

The values 400w, 800w, 1200w are actually breakpoints for the browser of the viewport width. It’s not actually describing the image. For example, if the viewport size crosses 800px, load the big_img.webp. Between 800px and 400px it’ll use medium_img.webp and below 400px, the small_img.webp.

Before the changes I have recently implemented, the resolutions and proportions of the projects' thumbnails were all over the place. Now I used the small, medium and a big variant, for each project. All those variants share the same proportions and use predefined resolutions.

I don’t make three variants for each project thumbnail myself, but generate them. You can actually do that using Hugo’s image processing. I found that to be a bit too limited. I have decided to experiment with imagemagick. I’ll just share the most crucial part of my bash script, which generates the thumbnails.

gen_thumb() {
  local thumb=$1
  local prefix="sm_"
  local thumb_ext="webp"
  local output=$(dirname $thumb)/$prefix$(basename "${thumb%.*}").$thumb_ext
  local text_debug="SMALL, $thumb_ext"

  local width='400'
  convert_command+="convert $thumb -resize ${width}x -filter Lanczos"
  convert_command+=" -font Liberation-Mono-Bold"
  convert_command+=" -pointsize 32 -density 100 -gravity Center"
if [ $DEBUG = true ] ; then
  convert_command+=" -draw \"fill white text 0,0 '$text_debug'\""
fi
  convert_command+=" -quality 98"
  convert_command+=" $output"

  eval $convert_command
}

This block repeats for medium and big thumbnail. I don’t store the thumbnail variants in the repository. I store a single image - the one I have used as a thumbnail before I have introduce the responsive images changes. I generate the small, medium and big variant when I build the website with Hugo.

When I was working on this it was very useful to put a clearly visible text, on the image, to easily identify which variant is the browser loading. That part of the convert command is behind a $DEBUG flag, since I don’t want that text to be present on the images I deploy to my web server.

If I run that bash function with a path of content/project/my_thumbnail.png, I’ll end with four files content/project/my_thumbnail.png (original, untouched), content/project/sm_my_thumbnail.webp, content/project/mid_my_thumbnail.webp and content/project/big_my_thumbnail.webp.

Since the thumbnails are generated, based on the original image’s name, that has to be taken into account by Hugo (I might actually simplify that because every project directory can have it’s own thumb.png file).

{{ $thumb_path := .Params.thumb }}
{{ $image := .Resources.GetMatch $thumb_path | default ( resources.Get "imgs/thumb.webp" ) }}
<!-- debug tool -->
<!-- {{ printf "%s" $image }} -->

{{ $smallPermalink := replace $image.Permalink (path.Base $image.Permalink) (printf "sm_%s%s" (path.BaseName $image.Permalink) ".webp") }}
{{ $mediumPermalink := replace $image.Permalink (path.Base $image.Permalink) (printf "mid_%s%s" (path.BaseName $image.Permalink) ".webp") }}
{{ $bigPermalink := replace $image.Permalink (path.Base $image.Permalink) (printf "big_%s%s" (path.BaseName $image.Permalink) ".webp") }}

<div class="project-card">
  <a href="{{ .Permalink }}">
    {{- with $image -}}
    <picture>
      <img src="{{ $smallPermalink }}"
           height="420"
           srcset="{{ $smallPermalink }} 400w, {{ $mediumPermalink }} 800w, {{ $bigPermalink }} 1200w"
           alt="thumbnail">
    </picture>
    {{ end -}}
    <div class="projects-piece-banner">
      <div>
        <p>
          {{ .Title }}
        </p>
      </div>
    </div>
  </a>
</div>

This code grabs the thumbnail path, from the project’s file YAML header, and generates the small, medium and big paths, based on the naming convention of prefixing the original thumb’s name with sm_, mid_ and big_. This code actually fetches the original thumbnail as a resource via .Resources.GetMatch. That’s important if you want to process the image with Hugo’s image processing features, which I don’t do in this example. It’s also important because the resources that are not referenced in your code, won’t be transferred into the build directory. I don’t reference the sm_, mid_ and big_ variants as a resources, which means I have to generate those variants after running Hugo’s build command, in the build directory.

In the end the system works well. The width breakpoints might need some tuning. It definitely brings in some complexity but I also learned a lot, and that for me is the most important part. I want this website to be informational and technically optimized but in the end it has to be fun for me to develop and maintain. Maybe at some point this website will matter to an extent that someone will feel it necessary to tell me that my image responsive image system doesn’t work well.

2023-08-13

I have turned off the Cactus comments system, on this website. I’ll soon be moving to my own comments system. For now the old comments should show up, without the ability to submit a comment.

2023-07-09

Some time ago I’ve embedded a toot, from my main Mastodon account, at the top of the projects page. As you can see below, it wasn’t the best execution. It was embedded through the HTML’s iframe and it took all the space it could, mainly because of the link preview.

I have moved to a custom setup, where I pull my latest toot, into a JSON file, and embed it into website’s code, using Hugo.

import feedparser
import json
import sys

MASTODON_LINK = 'https://mastodon.social/@mronetwo'
feed = feedparser.parse(MASTODON_LINK + '.rss')

# Just a way to look up what's in the entry.
# print(feed.entries[0])

toot_data = dict()
toot_data['summary'] = feed.entries[0].summary
toot_data['date'] = feed.entries[0].published
toot_data['base_url'] = feed.href
toot_data['mastodon'] = MASTODON_LINK

out_data = json.dumps(toot_data, indent=2)
sys.stdout.write(out_data)

It was trivial to pull the latest toot, thanks to the Mastodon’s RSS feed. I grab the latest toot, pick the relevant data and save it as a JSON string. I then output that string to a standard output, which I can easily pipe into a file.

python3 mastodon_rss.py > data/toot.json

I have added a small piece of code in the theme/layouts/index.html which, if the data/toot.json exists, embeds that data into website’s code. The .Site.Data.toot searches for the toot file in the data directory. The actual toot contents are already HTML, so I embed it using Hugo’s safeHTML function.

{{ with .Site.Data.toot }}
<div class="toot-container">
  <p class="toot-header">
  <a href="{{- .mastodon -}}">{{- .mastodon -}}</a>
  <div class="toot-content">
    {{- safeHTML .summary -}}
  </div>
  </div>
{{ end }}

The result is way more subtle than the original.

There’s definitely space to improve it, but for now I’ll leave it at that.

I have also improved the CSS in quite a few places. I’m leveraging Hugo’s toCSS, to convert the SCSS stylesheet into CSS document. Previously I have used Python’s boussole to do that. It makes CI significantly slower to install boussole, since it has to compile the libSass. It also reloads the CSS whenever I modify the main.scss file. That wasn’t the case with the boussole setup.

One last thing I’d like to mention is that I have written my own comment system, using HTMX. I’d like to migrate to it soon. Since it’s extremely simple, it will be very easy to keep current comments - there aren’t many.

2022-12-11

I’m not updating this project entry too often. Not surprisingly, working on my projects, content for this website and its form is not easy. In spite of that, it’s sometimes very calming to sit down and fight with the CSS a bit. Those last two days I managed to simplify the page’s layout - it’s less buggy and works better with mobile devices. I don’t feel like spending time on explaining those problems provides much value for anyone except for very unexperienced web developers. I’m sorry but this is not the place to learn advanced web development. If there is one thing that I might bestow upon you, after my latest work, it’d be to check the SASS’s ampersand operator.

I started using the ampersand operator and I quite like it:

a {
  color: $leftpane-bg;
  text-decoration: none;

  &:hover, &:focus {
    color: lighten(saturate($leftpane-bg, 20%), 10%);
    text-decoration: none;
  }
}

One other things I have changed is styling for the comments section. Here is how it looked before.

Just several tweaks later it’s speaking the same design language as the rest of this website.

I’m using Cactus comments, which pulls its own CSS stylesheet. My own stylesheet then overrides classes’ parameters, thanks to CSS’s !important keyword.

Fixing comments’ elements took way too much time, but sometimes things just need to wait their turn. I have moments when maintaining a website frustrates me, but I try to remind myself that this is a learning experience. Nothing I make here gets to be perfect on the first try.

If you want a great final result, from the get go, spin up your website using one of the proven themes. Otherwise, be patient and let your space evolve with time.

2021-11-28

This website has always been a project and currently it’s my longest running one (I’ve bought the ciesie.com domain on 25th of January, 2018). Feels only natural to create a project entry for it.

I’ve attempted to start a website/blog several times. It took a while to find a setup that I enjoy maintaining. After bouncing between Content Management Systems, I’ve found the static website generators. Currently I’m using Hugo - probably the most popular static website generator. It took a bit of time to learn it and I’m still learning anytime I come back to improve this website.

This is how the website looks on the 28th of November, 2021:

The red pane is quite distracting...
The red pane is quite distracting...

Consider this to be an archive for this website. This theme has been written by me but it’s soon time to move to something new. I always wanted to have a website which I designed and wrote. This is the space that I fully own and I want it to reflect my sensibilities. This look was inspired by terminal windows, which for me translated to monospaced fonts, limited color palette, text on a grid, minimalism etc. While I’m proud of it, I feel like it has run its course. I haven’t even started working on a new theme but redesigning this website has been in the back of my head for a long while now.

The contemporary web design doesn’t focus on something that I value - performance. This one is difficult for me because on one hand I really want this website to be lightweight and fast but on the other hand I’m a sucker for pretty things. I don’t want your browser to pull megabytes of data while navigating this website but having a 3D model of a PCB, embedded in an article, feels nice. Does it add proportional amount of value compared to the performance hit? Difficult to say. Do I like it there? Yes.

While this website is very minimalistic, I don’t want to be dogmatic about this. Some of the designs that I’d like to try in future might be less restrained but performance will always be on the top of my priorities list.

I won’t talk much about content here but one thing that those past few years of writing has taught me is the importance of clear writing and how difficult it is to achieve. English isn’t my first language and I have never been a great writer. I’m still not and I don’t think I’ll ever be, but I do see improvement. I notice that mostly when I go back to a piece I’ve written some time ago and I find it difficult to understand and follow. It doesn’t happen nearly as often as it should but I do rewrite content on this website. I’ve never thought I would learn to value writing skills as much as I do right now.