I recently added a CSS background color to my cover images, using the dominant color from the cover image itself. It is a nice effect, and gives the appearance of faster load time — should the image take some time to complete.
For my transparent cover images; I made the background very opaque, providing a soft touch of color. I think it looks nice 🙂
Here is how I did it. 👇
Table of contents
Getting the colors
First I needed to get the dominant color for each image. And it turns out that isn’t as clear-cut as I thought. What is the definition of dominant color? Is it the most vibrant? Most eye-catching? Most used?
This post on Super User got me started.
I first tried with an Imagemagick histogram:
convert image.jpg -format %c -depth 8 histogram:info:histogram_image.txt
sort -n histogram_image.txt | tail -1
This worked well for some images, not for others. Using only the frequency of pixel colors returned lot of black and dark colors. As it is stated in the Super User answer:
[…] Frequency of specific colour pixels might not correspond to a human perception of predominant color of course. […]
Also mentioned in that answer was dcolor, which is what I decided to use.
I made a simple Python script — get the dominant color of all my cover images, and store it in a json file in my data
folder:
import os, glob, json, subprocess
def get_color(filename):
result = subprocess.run(["dcolors", "-k1", "-r100x100", filename],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
return result.stdout.decode("utf-8").rstrip()
colors = {}
with open('colors.json') as json_file:
colors = json.load(json_file)
files_grabbed = [sorted(glob.glob(e, recursive=True)) for e in [
'/home/hebron/dev/cavelab-blog/content/**/cover.jpg',
'/home/hebron/dev/cavelab-blog/content/**/cover.png'
]]
for files in files_grabbed:
for img in files:
imgKey = img.replace('/home/hebron/dev/cavelab-blog/content/', '')
if imgKey in colors:
continue
colors[imgKey] = get_color(img)
print('{}: {}'.format(colors[imgKey], imgKey))
with open('colors.json', 'w') as outfile:
json.dump(colors, outfile, indent=4)
For each image, it stores a single color in rgb — but only if the path is not already defined. The resulting json file looks like this:
{
"posts/2021/zfs-ssd-pool-problems/cover.jpg": "32,30,31",
"posts/2021/zfs-on-arch-linux-no-luck/cover.png": "2,23,44"
}
Implement in Hugo
Next I just needed to add a bit of code to my post-cover.html
partial:
{{- $page := . -}}
{{- $src := $page.Resources.GetMatch "cover.*" -}}
{{/* Image processing happens here */}}
{{- $bgColors := "" -}}
{{- with $page.Site.Data.colors -}}
{{- $imgPath := path.Join (path.Dir $page.Page.File.Path) $src.Name -}}
{{- with (index . $imgPath) -}}
{{- if eq $src.MediaType.SubType "png" -}}
{{- $bgColors = printf "background-color:rgba(%s,0.1);" . -}}
{{- else -}}
{{- $bgColors = printf "background-color:rgb(%s);" . -}}
{{- end -}}
{{- end -}}
{{- end -}}
<figure class="post-cover" {{ if gt (len $bgColors) 0 -}} style="{{ $bgColors | safeCSS }}" {{- end }}>
{{/* Picture element */}}
</figure>
This the logic (in simple terms):
- If a key with the relative path to the image exists in
colors.json
- If cover image is
png
: add background color with 0.1 opacity - If cover image is not
png
: add solid background color - If the
$bgColors
variable is not empty, insert it as an element style on thepost-cover
figure
The result
The individual background colors make for a more pleasant browsing experience when on a slow connection — or, at least I think so 🙂
This video shows browsing on a landscape phone, on a “regular” 3G internet connection (according to Firefox):
And here is a screenshot showing the cover image for this page, with an opaque background (with light theme):
Having the color codes; it is easy to extend this for figures, gallery images, etc. I might do that in the future 🥳 🖖
Last commit 2024-04-05, with message: Tag cleanup.