The Dangers of Logic in CSS

We can do a lot of logic in CSS. I’m thinking specifically of scenarios which take the form “if this, then that”.

There are media queries which will let us control the appearance of elements based on a variety of factors. Probably the most common is screen width, so we get things like “if the width is 1024px or higher, then show an extra element”.

There are pseudo selectors like :empty, :checked, :valid, :disabled which let us control appearance based on state, giving us, for example, “if the parent element has no children (:empty), then change its opacity”.

We can also do logic based on combinations of elements or selectors, like li + li, for example, “if the list item follows another list item, then give it a top border.”

I’m sure there are plenty more ways of applying logic but you get the idea. Each of these is equivalent to writing a conditional statement in a programming language – if this, then that.

The big question is, should we be doing this in CSS, and is there a point at which it becomes too difficult to manage in CSS and we need to defer to another technology, like JavaScript?

My Experience

I started thinking about this when I was designing a UI with a lot of logic going on. We had a busy table where we show some additional columns at wider screen resolutions. At smaller widths the user would be able to toggle between different groups of columns and save a preference. But then these additional columns are only offered if they contain data. So, we had logic around the screen width, user preference and presence of data, which, multiplied out, meant 8 permutations.

I initially tried to do it all with CSS. I set up media queries for the narrower and wider screen widths. Within each of these I then read a class for the user preference and then I used :empty to see if table cells contained any data. I managed it and it worked. Just.

However, I realised that it was really not scalable. Every time I added a new condition it doubled the amount of CSS I was having to write. If we wanted to add more logic in future it would create more and more work and be harder and harder to maintain.

By moving all the logic into JavaScript we can set simpler classes, one for each type of display we need rather than every permutation of logic. So now we just have 3 classes: 1) show the first set of columns, 2) show the second set or 3) show all columns.

We still have lots of branches in our JavaScript but the big difference is that it’s easy to read and follow. It’s also easy to edit or extend if we ever need to.

I think doing some logic with CSS is fine but as soon as you’re starting to layer it on and find yourself multiplying out the selectors you’re writing it’s probably time to move it over to JavaScript.

Clicking Off Things to Close Them

There’s a common design pattern these days where a bit of content is shown over the main screen. It might be a modal window or maybe a fly-in menu or sidebar. It’s also common, especially in native apps, that we can just click or tap in the space not used by these elements to close them. So, how can we do this?

I think there are 2 approaches, one which is pure JavaScript and another which involves an overlay element. Let’s look at both.

JavaScript Document Click Event Method

We can detect that a click is not on the content element using JavaScript. As all click events bubble up or propagate through the DOM tree they eventually reach the top document level. We can listen for a click on the document and then use the event target to check what was clicked. For this example let’s assume our content element has a class of ‘modal’, like <div class=”modal”></div>.

// JavaScript

function handleClick(event) {
  if (!event.target.closest('.modal')) {
    console.log('close');
  }
}

document.addEventListener('click', handleClick);

event.target gets the element that was clicked. This could be the modal element itself, another element inside it or another element outside of it. event.target.closest(‘.modal’) checks if the element clicked is the modal or is an antecedent of the modal element, an element within it. If the element clicked is not (!) in the modal we can close it.

It’s maybe worth noting that .closest() doesn’t work in IE11 if you need to support this but there is a polyfill available.

Using an Overlay Element

The other approach, and the one I tend to use, is to use an overlay element. This means adding an element that covers the whole screen area, slipped in between the modal and the main screen content. The idea is that this overlay will appear and disappear along with the modal and will pick up any click events which are not on the modal. It’s a bit like a safety net that will catch any stray clicks.

The CSS for the overlay would typically look something like this:

/* CSS */

.overlay {
  background-color:#000;
  bottom:0;
  left:0;
  opacity:.5;
  position:fixed;
  right:0;
  top:0;
  z-index:1;
}

.modal {
  /* styling */
  z-index:2;
}

The overlay has fixed display and goes to each edge so it fills the screen.

The overlay has z-index set to 1 so it’s over the main screen content, and the modal has it set to 2 so it’s over the overlay. These can increase as needed but the modal always needs to be higher.

I’ve given the overlay a background colour and 50% (.5) opacity so that it veils the main screen content underneath giving the modal a “lightbox” effect but these are not needed and our overlay can be effectively invisible.

You then just add a click event to the overlay which will close the modal and hide the overlay.

The reason I like this approach is that by covering the main screen we prevent any interactions with it. If the user tries to click on a button under the overlay their first click will just close the modal. We don’t need to worry about any other interactions – while the modal is showing it becomes the sole focus.

Extra tip. If you use a visible overlay it’s nicer not to show and hide it immediately but fade it in and out with a transition on the opacity, taking it between 0 and .5. This feels much smoother and less jerky.

Switching APIs

It’s completely normal now for web apps to work with external third party JavaScript APIs. The good ones give you lots of documentation making them pretty straightforward to implement and use. You load a script from a URL or local resource, use their methods and properties in your code and away you go.

But what if you then want to switch to another API provider? Maybe the one you were using isn’t accessible, doesn’t support the older browsers you need? Maybe they put their prices up? Whatever the reason you may need to switch at some point.

This change means rewriting your app’s JavaScript to work with the new API. You’ll have to find every point it touches and update it. What if it works differently? What if this one takes an object parameter rather than an array? You’ll have to completely rewrite the data parts too.

There is a better way.

Rather than working directly with an API and using the properties and methods in your code, you create an intermediary, your own internal service, which handles this functionality. The internal service maps the properties and methods you need to those of the third party API. It handles any data transformation needed. Your app works with this service, your own API, so that when a change of service provider is needed the app itself does not need to be rewritten, just the internal service.

An internal service can also be set up to work with multiple APIs at once so it maps everything it needs for each. This allows you to push choice to your users. So, for example, if it’s a news, sport or weather feed built into a site, you can let your user choice their preferred information source. You could use different services for different regions, e.g. local news.

To use a slightly inappropriate analogy, it’s better to be dating these APIs than to move in with them. 😉

Edging Out of IE Support

Having to support a minority of users on IE11 is painful. It’s a browser that is still officially supported by Microsoft, until 2025, but active development has stopped, or at least slowed right down, so the gap between it and other browsers is growing wider with every update.

The growing feature gap means either not using newer features if we’re working to the lowest common denominator or else having to provide polyfills or fallbacks so IE11 can still be used.

But there could be a light at the end of the tunnel, and before 2025. Microsoft Edge is moving from using the EdgeHTML engine to open source Chromium, which will bring it in line with Chrome, Opera and Safari. That’s nice for compatibility but the big deal is that it will be available on older Windows operating systems. Up until now Edge was only available on Windows 10 but it will soon be available on 7, 8 and 8.1 too so more users can drop IE and use Edge as their official Microsoft browser.

The Edge team are going one better than that though. One of the big reasons for clinging on to IE is the use of legacy software which only works on that browser. Edge is introducing an IE11 mode, so users can seamlessly (hmm – I can already hear the massive crash) switch over to IE for specific sites. Very clever.

It looks like we might have a way out of IE11 support in the next 12 months or so. Just need to convince the large corporations to make the switch now but I’m sure Microsoft will be pushing hard on this too. Good news.