惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Full Disclosure
WordPress大学
WordPress大学
小众软件
小众软件
Cloudbric
Cloudbric
AWS News Blog
AWS News Blog
腾讯CDC
量子位
人人都是产品经理
人人都是产品经理
大猫的无限游戏
大猫的无限游戏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V
Vulnerabilities – Threatpost
Scott Helme
Scott Helme
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
C
CXSECURITY Database RSS Feed - CXSecurity.com
The Hacker News
The Hacker News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
IT之家
IT之家
Jina AI
Jina AI
Attack and Defense Labs
Attack and Defense Labs
S
SegmentFault 最新的问题
Simon Willison's Weblog
Simon Willison's Weblog
The Cloudflare Blog
阮一峰的网络日志
阮一峰的网络日志
T
Tailwind CSS Blog
Last Week in AI
Last Week in AI
博客园 - 【当耐特】
Google Online Security Blog
Google Online Security Blog
美团技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
Visual Studio Blog
罗磊的独立博客
L
LINUX DO - 最新话题
博客园 - Franky
博客园 - 叶小钗
Apple Machine Learning Research
Apple Machine Learning Research
The Last Watchdog
The Last Watchdog
J
Java Code Geeks
AI
AI
C
Cisco Blogs
酷 壳 – CoolShell
酷 壳 – CoolShell
C
Cyber Attacks, Cyber Crime and Cyber Security
Cisco Talos Blog
Cisco Talos Blog
博客园 - 三生石上(FineUI控件)
雷峰网
雷峰网
Help Net Security
Help Net Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
云风的 BLOG
云风的 BLOG
I
Intezer
S
Securelist

Home on Alex Plescan

Just for fun: animating a mosaic of 90s GIFs Two computers, one monitor, zero fiddling Placeholder names should be bad and unique Okay, I really like WezTerm GNU Parallel, where have you been all my life? Timeseries with PostgreSQL Easy SVG sparklines Using Declarative Shadow DOM to embed HTML emails on a web page Selling SaaS on Gumroad PDF: The Conjoined Triangles of Success Deploying Metabase to Fly.io The ".x" Files Xcode 8 managed signing: adding new device UUIDs to a provisioning profile Emojify your Wi-Fi (Netgear R6300 edition) How to use the San Francisco Mono typeface before macOS Sierra is released Disabling App Transport Security in your development environment Swift: A nicer way to tell if your app is running in Debug mode Development environment config overrides in Jekyll Setting up SwiftLint on Travis CI
Rebuilding this site
2024-09-21 · via Home on Alex Plescan

I first made this site back in 2016, and since then it hasn’t had any substantial updates to its design or overall structure. Here we are now in 2024, and I find myself to be a developer with a blog that doesn’t have a dark theme.

What set out as an effort to rectify this embarrassing fact with some simple CSS tweaks ended up being a rewrite of the entire site. Oh well - guess I love to shave yaks!

This post runs through some of the new things I’m doing with this site from a technical point of view, as well as some things that I’ve decided not to change from the old one.

For posterity (and my own nostalgia), the old website has been archived at v1.alexplescan.com. This site’s source code is available at alexpls/alexplescan.com, so if you want to dig in to anything I talk about here in more detail - help yourself!

Using Hugo

The biggest change I made was migrating from Jekyll to Hugo. This was a tough choice to make, I still have a soft spot for Jekyll, have built a number of sites in it, and still find it generally more intuitive, but there were a few things about Hugo that swayed me over…

Hugo seems easier to maintain

Hugo cuts down on a lot of dependency management work by shipping as a single binary. Jekyll on the other hand is distributed as a Ruby gem… this means that in order to build my site in Jekyll I’ve gotta manage Ruby versions, gems, and system dependencies. Often I’ll take long breaks between updates to this site, and with Jekyll when I’d jump in to make a change for the first time in a while I’d find myself spending a bit of time just getting the build working again. I am hoping that with Hugo I’ll have a bit more stability in this respect.

There’s also a bunch of stuff built-in to Hugo that I previously had to use Jekyll plug-ins for (RSS feed, sitemap generation, pagination, responsive images). With the new site my only dependency is Hugo - which makes me very happy.

Of course, at this stage my hope that Hugo will be easier to maintain is just conjecture… let’s check in in another eight years and see if I was right :)

Hugo builds are faster

Without any caching, my Jekyll site would build in about eleven seconds, the Hugo one builds in about six. With caching, Jekyll rebuilds in 700ms, Hugo in 100ms. Neither are slow enough to be a real blocker while I’m working on the site… but faster is better.

Hugo’s templating is more flexible (albeit it’s also uglier)

Jekyll uses the Liquid templating language, whereas Hugo uses Go templates. As a quick comparison between the two, let’s see what it looks like to render out a comma separated list of pages:

In Liquid:

{% for page in pages %}
  <a href="{{ page.url }}">{{ page.title }}</a>
  {% if forloop.last %}
	.
  {% else %}
	,
  {% endif %}
{% endfor %}

And in Go templates:

{{ $length := (len .Pages) }}
{{ range $index, $page := .Pages }}
  <a href="{{ .RelPermalink }}">{{ .Title }}</a>
  {{ if eq (add $index 1) $length }}
	.
  {{ else }}
	,
  {{ end }}
{{ end }}

It took me a while to get used to the Go templates’ functional pipeline syntax and lack of native operators. From the example above, I would’ve preferred to write if $index + 1 == $length instead of if eq (add $index 1) $length.

There’re also more conveniences in Liquid that make it a generally friendlier templating language (take the forloop.last helper for example).

On the flipside, Go templates are more powerful and flexible, so suit complex scenarios better. I’ll cover an example of this in more detail under the Responsive images heading below. All things considered, I think the tradeoff has been worth it.

Using Tailwind CSS

This rewrite was also a good opportunity to use Tailwind CSS (the old site used SCSS stylesheets). I experimented with Tailwind when I first built Mailgrip and I was immediately hooked - I don’t see myself starting any new web project without it.

It strikes the perfect balance between being flexible enough to feel like you can write custom CSS for everything, while still giving defaults that look good in any environment (colour palettes, spacing, typography rules, responsive layouts, etc). Plus its approach of styling with utility classes right in HTML, as weird as it first was to get used to, is extremely productive.

A downside to using Tailwind with Hugo is that my build now has two steps. One to generate Tailwind’s CSS, and one to build the Hugo site. This should hopefully get stamped down into one step once Tailwind 4 is released and Hugo’s built-in css.TailwindCSS processor is ready for general usage.

Adding a dark theme

Step 0 of creating a developer blog in 2024 is adding a dark theme, so this site has one now. Tailwind CSS made this really easy with its built-in dark mode selectors and colour palettes. I went for “Slate” for the dark theme, and “Zinc” for the light theme.

Visitors can choose between three theme options: System, Dark, and Light. I like to use native HTML controls as much as possible, but after experimenting with a few I couldn’t find any that really looked nice in this case. As such, I made a custom control for toggling between the themes. A sprinkling of ARIA attributes helped make it accessible by keyboard and screen reader.

Responsive images

Images are available at variable quality levels depending on the screen the website is being viewed on and how modern a visitor’s browser is. On a mobile display, a lower resolution image will be loaded - whereas on a high-DPI desktop visitors get a higher res image. Modern browsers get more efficient WebP images. Older ones get JPEGs.

With Jekyll I was using jekyll_picture_tag for this. Hugo’s got image processing functionality built in, so for the new site I’ve used that.

I took inspiration from Bryce Wray’s post and created a shortcode to handle responsive image generation. This ends up looking like this in a template:

{{< image src="08_status_bar.png" alt="screenshot of WezTerm with a right status bar showing the system's hostname" class="ap-post-img" >}}

and produces HTML that looks like this:

<picture>
  <source type="image/webp" srcset="/posts/2024/08/10/wezterm/08_status_bar_hu4260704181743966047.webp, /posts/2024/08/10/wezterm/08_status_bar_hu13694828537407067836.webp 2x" />
  <source type="image/jpeg" srcset="/posts/2024/08/10/wezterm/08_status_bar_hu9862889464567697310.jpg, /posts/2024/08/10/wezterm/08_status_bar_hu3429514080672963904.jpg 2x" />
  <img src="/posts/2024/08/10/wezterm/08_status_bar_hu3699262374842627327.jpg" alt="screenshot of WezTerm with a right status bar showing the system&#39;s hostname" class="ap-post-img" />
</picture>

So, that single image ends up being converted into five:

  • 2 x WebP (low-res and high-res)
  • 2 x JPEG (low-res and high-res)
  • Fallback JPEG, in case the browser is too old to support responsive images

The shortcode’s implementation looks like:

{{- /* layouts/shortcodes/image.html */ -}}

{{- $src := .Get "src" -}}
{{- $alt := .Get "alt" | default "" -}}
{{- $quality := .Get "quality" | default "60" -}}
{{- $class := .Get "class" -}}
{{- $desiredWidth := .Get "width" -}}
{{- $width := $desiredWidth | default "760" -}}
{{- $width = int $width -}}
{{- $img := .Page.Resources.Get $src -}}
{{- $fallbackImg := $img.Process (print "resize " $width "x jpg") -}}

{{- $variants := slice
  (dict "type" "webp" "contentType" "image/webp")
  (dict "type" "jpg" "contentType" "image/jpeg")
-}}

{{- $dpiMultipliers := slice 1 -}}
{{- range slice 2 3 -}}
  {{- $multipliedWidth := mul $width . -}}
  {{- if lt $multipliedWidth $img.Width -}}
    {{- $dpiMultipliers = $dpiMultipliers | append . -}}
  {{- end -}}
{{- end -}}

<picture>
  {{- range $variant := $variants -}}
    <source type="{{ $variant.contentType }}" srcset="
      {{- range $i, $multiplier := $dpiMultipliers -}}
        {{- $multipliedWidth := mul $width $multiplier -}}
        {{- if $i }}, {{ end -}}
        {{- ($img.Process (print "resize " $multipliedWidth "x " $variant.type " q" $quality)).RelPermalink -}}
        {{- if ne $multiplier 1 -}}
          {{- print " " $multiplier "x" -}}
        {{- end -}}
      {{- end -}}
    " />
  {{- end -}}
  <img src="{{ $fallbackImg.RelPermalink }}" alt="{{ $alt }}"{{ if $class }} class="{{ $class }}"{{ end }} />
</picture>
{{- /* swallow trailing newline */ -}}

A downside of this approach is that I couldn’t find an easy way to

Deploying to Cloudflare pages

Like the old one, this new site is still deployed to Cloudflare Pages. I’ve got romantic ideas about one day hosting it on my own home server, but so far Cloudflare’s been making deployments too easy, cheap, and reliable for me to bother.

When I push to the master branch of the site, Cloudflare checks out the latest version of the repo, builds it, and deploys it to their network. I already host my DNS with Cloudflare, so this ends up being a near zero configuration setup that just works.

Analytics with Plausible

I’m still using Plausible Analytics to track views on the site. I like that they’re a small bootstrapped company delivering a focused and high quality product, and I like their strong stance on privacy. Their tracking Javascript respects a visitors’ do not track settings, doesn’t use cookies, and doesn’t gather any personally identifying information. This means that I don’t need to have a dreaded “Please accept cookies” banner on the site to be GDPR compliant.

Lato font

Lato remains as the font for this site. Along with other visual changes made, I thought it’d be interesting to find a new font as well… but I didn’t end up finding a (free) one that I liked as much as Lato… what I did find however is Font Squirrel - which is a great site for finding free fonts that you can self-host.

Conclusion

Did I really need to rebuild this site? Nah - the 2016 one still had plenty of life in it. I attribute this to sticking with standard web technologies and keeping things simple. Was it fun to rebuild this site though? Yeah.

What’s next? Writing more posts, ideally. And I think my custom Hugo theme is a bit dry at the moment. I’ll probably look into doing something to make it more colourful.