Mountains

Code is Sisyphean

Simple Link Decoration using Fuzzy Attribute Selectors

Over on my project journal I wrote a post a while back about doing dynamically-styled buttons. They look like this, for example:

Listen on Spotify

The animation is neat and all (go dive into that link if you’re curious) but what I wanted to talk about here was writing selectors that focus on the intent of the document and decouple the content layer from the presentation layer.

On one side of the spectrum would be this very simplistic example:

<a
  href="https://open.spotify.com/user/armahillo"
  class="spotify m-1 pt-1 pb-1 pl-2 pr-2">
  @armahillo on Spotify
</a>
<a
  href="https://github.com/armahillo"
  class="github m-1 pt-1 pb-1 pl-2 pr-2">
  @armahillo on Github
</a>
<a
  href="https://instagram.com/armahillo"
  class="instagram m-1 pt-1 pb-1 pl-2 pr-2">
  @armahillo on Instagram
</a>

This isn’t specifically wrong HTML. It’s valid HTML, well-formatted, and all things considered is fine.

But I think we can do better.

Two issues I see here are:

  1. Having a class “spotify” for a Spotify link is redundant.
  2. Class-stuffing adds noise and conflates content/presentation layers.

What if it looked like this, but displayed identically, instead?

<a
  href="https://open.spotify.com/user/armahillo">
  @armahillo on Spotify
</a>
<a
  href="https://github.com/armahillo">
  @armahillo on Github
</a>
<a
  href="https://instagram.com/armahillo">
  @armahillo on Instagram
</a>

This would be the simplest possible HTML we could write that is still functional. I like this approach because it lets the document remain as unmuddied as possible, letting it stay lithe and adaptable.

The first thing we can do can be done in native CSS, using fuzzy attribute selectors. To add icons for each of these links without using classes, we could use this CSS:

a[href*="spotify.com"]::before {
  background: url(/assets/img/post_images/spotify-icon.png) no-repeat;
  /* remainder of styles */
}
a[href*="github.com"]::before {
  background: url(/assets/img/post_images/github-icon.png) no-repeat;
  /* remainder of styles */
}
a[href*="instagram.com"]::before {
  background: url(/assets/img/post_images/instagram-icon.png) no-repeat;
  /* remainder of styles */
}

The selector a[href*="spotify.com"] means “find any a tag that has the href property containing the substring spotify.com”. You can be as specific as you like, though note that you will likely need to escape your slashes (e.g. a[href*="docs.google.com\/document] for Google Docs).

The next step is most easily done using SCSS. You can accomplish something similar using @apply in Tailwind, as well. Using the styles from the example in the beginning:

@mixin icon-prepend {
  content: '';
  display: inline-block;
  width: 15px;
  height: 15px;
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  vertical-align: middle;
}

This defines a reusable block of styles. We can than incorporate those with the fuzzy-matched selectors:

a[href*="spotify.com"]::before {
  background: url(/assets/img/post_images/spotify-icon.png) no-repeat;
  @include icon-prepend;
}
a[href*="github.com"]::before {
  background: url(/assets/img/post_images/github-icon.png) no-repeat;
  @include icon-prepend;
}
a[href*="instagram.com"]::before {
  background: url(/assets/img/post_images/instagram-icon.png) no-repeat;
  @include icon-prepend;
}

Which would render a link that looks more like this:

@armahillo @armahillo @armahillo