← bits

update #wilhelm.codes, #homelab, #css#design

I’ve been rebuilding the public landing page for the homelab this weekend and somewhere in the middle of it I laid a scanline over the whole thing. It’s a single hairline that repeats itself every three pixels. It’s faint enough that the page stops looking quite so flat without you ever working out why. Then, obviously, I wanted it here too.

The lazy version is a white line at about 1.5% opacity, which is what the other page uses, because the other page is forty words of neon over black; legibility isn’t really the goal. However, this site is mostly prose, so a white line lightens the words as well as the background. I didn’t spend all that time on the reading column just to lay a haze over it.

So the grain mixes off --text instead of white:

CSS
--grain: color-mix(in srgb, var(--text) 1.5%, transparent);

This can be applied like so:

CSS
.grain {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 10;
  background: repeating-linear-gradient(
    to bottom,
    var(--grain) 0 1px,
    transparent 1px 3px
  );
}

And finally referened like so just above the closing </body> tag:

HTML
<div class="grain" aria-hidden="true"></div>

This works out great, because a colour mixed with itself gives that colour straight back. The grain physically cannot touch the body copy. I’ve learned quite a few new things today!

As a bonus the lightbox is a <dialog> and the browser promotes a modal dialog into the top layer. This sits above every z-index on the page; including the one holding the grain. So a zoomed photograph comes up perfectly clean and I didn’t have to write a rule to make that happen.

Whether you can see any of this depends entirely on your monitor, your colour settings and even your resolution. If you can’t see it, it’s working.

Comments