Bad CSS in JS

I tried to understand the argument made for styled components vs. ‘traditional’ CSS. But when reviewing these arguments I found out that they typically use bad code as proof for their point.

See e.g. the following SCSS code:

$blue = #5DBCD2;

@mixin button-styles($bg-color, $color) {
  background: $bg-color;
  color: $color;
  border: none;
  border-radius: 0.20em;
  &:hover{
      background: darken($bg-color, 6%);
      cursor: pointer;
  }
}

.button {
  @include button-styles(#ddd, black)
}

.button--primary {
  @include button-styles($blue, white)
}

To pair with a simple component along the lines of:

const Buttons = props => (
  <>
    <button className="button">Default</button>
    <button className="button--primary">Primary</button>
  </>
)
export default Buttons

The suggested alternative is:

import themeVars from "myTheme"
import styled from "styled-components"
import { darken } from "polished"

const Button = styled.button`
  background: ${({ background }) => background || "#ddd"};
  color: ${({ color }) => color || "black"};
  border: none;
  border-radius: 0.25em;
  :hover {
    background: ${({ background }) => darken(0.05, background || "#ddd")};
  }
`

const Buttons = props => {
  const { blue } = props.theme.colors;
  return (
    <>
      <Button>Default</Button>
      <Button background={blue} color={"white"}>Primary</Button>
    </>
  )
}
export default withTheme(Buttons)

You might think this is better when you’re used to writing the earlier mentioned SCSS/SASS.

Learn CSS again.

With good ol’ CSS you’d write:

.button {
  background: #ddd;
  color: #000;
  border: none;
  border-radius: 0.25em;
  &:hover{
      background: #ccc; /*estimation*/
      /* cursor: pointer; <- also don't: another discussion */
  }
}

.button--primary {
  background: #5DBCD2;
  color: #fff;
  &:hover{
      background: #4DACC2; /*estimation*/
  }
}

The HTML would be:

<button class="button"></button>
<button class="button button--primary”></button>

In BEM, what the original example seemed to be using, modifiers modify, they are not the style itself as well. Please, front-end devs: Let’s not (programatically) over duplicate & complicate things.

Note, in contrast to some of the pure CSS folks, I’m not anti-SCSS. I actually do use SCSS; to store the typical theme variables for margins, colours etc. I even use an occasional function to darken something or calculate some ratio’s (typically at settings level). Even an occasional include to replicate an almost similar border style for totally unrelated objects. But never to overcomplicate things.

Lazy loading the lazy way

Large pages with many images make pages heavy to load.

Below is a simple, lazy, technique I applied in a collection management tool where its users wanted to browse over 10.000 images without scroll-hijacking or pagination. Sure, only the HTML weight several MB’s at once, but for this particular application used by professionals it is worth the weight. But performance was too heavily affected by downloading all these separate images.

So let’s have a look how I solved this.

What does the code below do?

<figure class="image">         
  <noscript data-lazy="lazy-load">
    <img class="nonlinked_show" decoding="async" src="/uploads/images/1540215129.jpg" alt="tree in field" />         
  </noscript>
</figure>

You rightly guessed that it won’t display any image to say ~95% of the users. The 5% who have disabled Javascript however, will see it.

The 95% who do have Javascript can enjoy the image simply by moving the img tag out of the noscript tag using a bit of JavaScript:

ELEMENTS_QUERY = "noscript[data-lazy=\"lazy-load\"]"

decode_entities = (encodedString)->
   # required for IE and Safari
   textArea = document.createElement('textarea')
   textArea.innerHTML = encodedString
   textArea.value

lazy_load_image_in_noscript_wrapper = (noscript_tag)->
   fragment = noscript_tag.innerHTML
   parent = noscript_tag.parentElement
   parent.innerHTML = decode_entities(fragment)

lazy_load_images = ->
  noscript_wrapped_images = document.querySelectorAll(ELEMENTS_QUERY)
  noscript_wrapped_images.forEach(lazy_load_image_in_noscript_wrapper)

document.addEventListener "ready", ->
   lazy_load_images()

Yeah, I don’t enjoy typing ;’s, so it’s Coffeescript here. It also reads more like pseudocode 🙂 You can convert it here.

But this ain’t lazy loading yet. It brings back the images though. And next up is actually making the images load lazily.

I promised to keep this lazy. So I’m only going to implement lazy loading (which I consider an progressive improvement over traditional loading) for newer browsers that actually support the IntersectionObserver. Currently supported by almost 70% of the browsers (including Firefox, Chrome, Edge), soon close to 80% when a new version of Safari is being released.

Here is the rewritten lazy_load_images()-method:

lazy_load_images = ->   
  noscript_wrapped_images = document.querySelectorAll(ELEMENTS_QUERY) 
  supportsIntersectionObserver = typeof IntersectionObserver == "function"   
  if supportsIntersectionObserver
     intersectionObsOptions =
       root_margin: "100px"
     intersectionObserver = new IntersectionObserver((entries)->
       entries.forEach(process_intersection_observer_entry)
     , intersectionObsOptions)
     noscript_wrapped_images.forEach((e)->intersectionObserver.observe(e.parentElement))
   else
     noscript_wrapped_images.forEach(lazy_load_image_in_noscript_wrapper

And there you have it. Lazy lazy loading. Make sure that you prevent reflows though. Without proper styling your page may have to be re-rendered all the way down when a single image is triggered for loading. So make sure there is an relatively positioned container for your images which removes the position of the images from the document flow.

Bonus points: how to make it work for print:

show_all_images = ->
   noscript_wrapped_images = document.querySelectorAll(ELEMENTS_QUERY)
   noscript_wrapped_images.forEach(lazy_load_image_in_noscript_wrapper)

# handle pritning of images: https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeprint

window.addEventListener "beforeprint", ->
   show_all_images()

mediaQueryList = window.matchMedia('print');
mediaQueryList.addListener (mql)->
   if mql.matches
     show_all_images()

I hope you enjoyed this. If so, sign-up for my mailinglist to get the latest in your inbox!


Creating a link in Vue

Any HTML is legal, so you can just write a regular link, but if you want to have a fast response, it is recommended that you include Vue Router. This brings two important tags: <router-link>, which this post is mainly about and <router-view>.

Creating a link in Vue is as simple as:

<router-link to="home">Home</router-link>

This renders a perfectly correct link, using the earlier mentioned a-tag. It even adds a nice bonus, adding an ‘active’ class when that route is active. If you prefer this class on the containing element, do this:

<router-link to="home" tag="li"><a>Home</a></router-link>

This will render something along the lines of:

<li><a href="home">Home</a></li>

Be aware: this is the only valid use-case for the tag-property in vue’s router-link component. Hence be wary if you come across any use of the tag-property, but Vue’s default is good.

You pick up on this link by defining the route ‘home’, make sure your app contains a <router-view> and attach a component to this route. In the most basic form:

<div id="app">
  <nav role="menu">
    <router-link to="/">Home</router-link>
    <router-link to="/about">About</router-link>
  </nav>
  <router-view></router-view>
</div>

The JS:

const Home = {template: '<div><h1>Home</h1></div>'}
const About = {template: '<div>h1>About</h1></div>'}

const router = new VueRouter({
  routes: [
    { path: '/', component: Home },
    { path: '/about', component: About }
  ]
});

const app = new Vue({
  router
}).$mount('#app')

See here for a minimal vue Router Example on CodePen.

That’s it. If you enjoy my sensible approach to front-end web development, don’t forget to subscribe below!


How do I navigate to another page in React or Vue?

Congratulations, you started working with one of the popular front-end frameworks. Both Vue and React are excellent choices to create rich and reactive applications. But sometimes, you ‘just’ want to show the user another page. Whether it is a simple about page, or another ‘section’ in the app. In this post I will cover both Vue en React and, little bonus, “React on steroids”, NextJS.

It is important to know that you need a router to make in-app pagination work. If you look up router and your favourite framework you’re probably find a way to do it, accompanied with some ways to actually not do it. Hence, here I’ll focus on the right way.

The basics of every link

While you can make everything clickable (or touch-able), it is important that enabling navigation between pages was already a core concept when the web was invented. Even when writing webpages using React, Vue, or most other JavaScript frameworks, it ends up as HTML to the browser (with JavaScript & CSS). HTML stands for Hyper Text Markup  Language and HyperText is text with hyperlinks. Hyperlinks in HTML are written as follows:

<a href="https://example.com">Example link</a>

So why is this important? On parsing code presented as above, the browser does the following:

  • It colours the links (typically) blue & underlines them (styles them as links).
  • On mouse-over you get a little hand-pointer
  • When using the keyboard you can ‘tab’-to the link.
  • Text-to-speech engines can announce it as being a link
  • Search engines can follow the link (may, but not always, require server-side rendering)

While the code above looks easy, it is easy with modern frameworks to create something like this:

<div onclick="open('https://example.com')">Example link</div>

This is clickable, and yes, it might navigate you to example.com, but shares none of the desirable characteristics of the well known hyperlink.

The boring fact

The boring fact is that the above code, yes with the <a href=..., works in every framework. In the next few posts I’ll dive into the ways to make it  work more natively in the frameworks mentioned earlier, often resulting in much faster page loads when you stay within your React/Vue or whatever application. Also, I’ll give a bit of a review of the good parts of these and make sure you don’t use the bad parts (if any). To stay up to date, make sure you subscribe to my mailinglist!




Should I use styled components?

You may have asked yourself if you shouldn’t invest more time getting started with this thing called styled components (or CSS-in-JS). They get heavily promoted in modern front-end application frameworks. And sure, they look interesting.

While it depends may be the only right answer, sometimes you might be simply making things more difficult than really needed. You’re fixing the wrong problem.

Take for example the SCSS code.

$blue = #5DBCD2;

@mixin button-styles($bg-color, $color) {
  background: $bg-color;
  color: $color;
  border: none;
  border-radius: 0.20em;
  &:hover{
      background: darken($bg-color, 6%);
      cursor: pointer;
  }
}

.button {
  @include button-styles(#ddd, black)
}

.button--primary {
  @include button-styles($blue, white)
}

To pair with a simple component along the lines of:

const Buttons = props => (
  <>
    <button className="button">Default</button>
    <button className="button--primary">Primary</button>
  </>
)
export default Buttons

It looks like smart reuse of a button-style mixin, saving you to type things twice. It also looks smart, because it seems to follow a well established naming convention called BEM (although is it really BEM?).

The code above was inspired by an online discussion. The author suggested something along the lines of:

import themeVars from "myTheme"
import styled from "styled-components"
import { darken } from "polished"

const Button = styled.button`
  background: ${({ background }) => background || "#ddd"};
  color: ${({ color }) => color || "black"};
  border: none;
  border-radius: 0.25em;
  :hover {
    background: ${({ background }) => darken(0.05, background || "#ddd")};
  }
`

const Buttons = () => {
  const { blue } = themeVars.colors;
  return (
    <>
      <Button>Default</Button>
      <Button background={blue} color={"white"}>Primary</Button>
    </>
  )
}
export default Buttons

You might think this is better when you’re used to writing the earlier mentioned SCSS/SASS, because it skips the ‘unnecessary’ translation to primary/default button classes. It may not be smaller in line length, but everything is nicely packed together as a single component. Ready for re-use. By including a theme object, it is easy to reuse elements and colours, useful to keep things consistent. You could even use it to pull out default margin’s and radia.

Well. Learn CSS again. There is a C in CSS, the cascade. Inheritance is built-in.

With good ol’ CSS you’d write:

.button {
  background: #ddd;
  color: #000;
  border: none;
  border-radius: 0.25em;
  &:hover{
      background: #ccc; /*estimation*/
      /* cursor: pointer; <- also don't: another discussion */
  }
}

.button--primary {
  background: #5DBCD2;
  color: #fff;
  &:hover{
      background: #4DACC2; /*estimation*/
  }
}

The HTML would be:

<button class="button"></button>
<button class="button button--primary”></button>

I also fixed classes here to make it proper BEM. In BEM, what the original author seems to be using, learns that modifiers modify, they are not the full styled element itself. Let’s not over duplicate & complicate things.