CSS or Cascading Style Sheets — The language that defines how a web page should look, and sometimes behave. It’s often overlooked by web developers as an “easy” language that doesn’t require too much attention. Memorizing some of the most common properties, the general flow and googling more obscure properties is the way to go. But, as more experienced developers know, no language deserves to be overlooked as there are always good and bad ways to do something.
With CSS, it’s very easy to do things the wrong way as there is no compiler that throws errors, there are no warnings or notices for bad implementations. A programming language like JavaScript has a ton of tools that track good code quality, some even throw messages at the developer that a certain function has not been implemented in the right way.
In this relatively short post, we will look at some of the common problems developers face and how to fix them. This is not about specific implementational details for a specific visual component. Instead, the list focuses on thought processes, architecture and approaches.
With CSS3, you can manage media queries, utilize better background images and even handle animations that can be loaded faster on your website – depending on the host you use. These features have made the life of every web designer and developer easier.
That being said, not all web designers and developers follow best practices. If you want to be a good web designer/developer, writing clean, manageable CSS is a healthy habit to make your code easier to maintain as your project development progresses.
Even if you’ve been building web pages for a quite a while, you may still have a few bad CSS writing habits that you’re better off forgetting. This post will also introduce you to some techniques that will help you get the most out of CSS and write beautiful, manageable stylesheets.
The CSS Complexity
Is CSS hard? No! Definitely not, it has a few very basic structural rules. You have selectors (class, id, element), you have properties and you have scope. It’s way less than a language like C, Java or PHP. A developer can get the whole idea in a few minutes of reading and starting styling. Of course, to know the difference between margin and padding, you can take a look at w3schools, but when to use which will take some experience. But it’s not rocket science.
Now, what makes things a bit more complex are all the ways selectors race for which gets to overwrite the next, and how one property affects another (display: block vs display: inline for example). Then we have smaller details like margin collapsing, z-index stacking, GPU vs CPU performance properties, the box model and typography properties.
On top of all that, new CSS features that are not always implemented the same way across browsers, some are even not implemented at all and others require different properties.
If we look at the two paragraphs above, we will see the two main stages front-end developers hit when they work with CSS – One is the “Oh, is that all?” and the second is “Oh ****, that wasn’t all”. This is what means “CSS Complexity”
There are a lot of small things intertwined that are hard to scale up. If you build just one component, it’s easy – you style whatever you see and it works. But put that single component in a larger application and additional styles will be applied. You might end up with the same class as something else (like .button) and your styles will be applied across the whole application, breaking everything. It’s stressful!
How to Write CSS: Fighting Its Complexity
There are a few solutions that smart people have come up, an overview would be:
- CSS Naming conventions like BEM, SMACSS and others, which define rules of class names that would reduce the possibility of clashes.
- CSS in JS is a newer method that practically lets you write CSS components in JS files (sounds bad, looks bad, but it makes some sense at least). Mostly valid with React, Vue and others.
- CSS Modules is a saner approach to CSS in JS for someone who ends up with all styles in one file. CSS Modules encapsulate each component’s styles so that they can’t leak to other components, making reusability way easier. It also reduces stress by a lot!
The first – Naming Conventions is probably the hardest to implement as it has no tools to aid you. It leaves it all to you as a developer to understand and apply the convention. You still have to think of class names, they still shouldn’t clash and then you have to make the whole team follow it. Pretty hard!
CSS in JS and CSS Modules are implementational fixes. They change the code output as a whole, which practically makes it impossible to leak styles. But they require completely different thinking and build setup, something that is not always doable or desired.
The best advice here would be: Study the most common and successful naming conventions, use the one that fits your company or ecosystem’s coding standards. Understand the component separation and start thinking in terms of scope and singular units that build a large app. Not a large app from the start, that would be impossible to manage.
If you are working on a stack that include as library like React or a framework like Vue, then look into additional tools that allow you to manage your styles with them. For any beginner to intermediate level CSS developer, they would be a great aid! And you might never want to ditch them unless something better comes your way.
Keep an open eye to new tools! They stop us from fighting problems that the developer community has already found, fixed and provided a way to deal with them.
Now that we outlined what CSS Complexity means and some starter tips on fighting it, let’s dive into a more detailed overview and provide direct examples.
Nine Signs of a Bad CSS
This list is definitely not complete, not by a long mile. Sadly, you will face countless other issues and hopefully find direct solutions to a specific problem in sites like Stackoverflow.
However, if you want to step up your game, when you fix a problem with a few lines of css, you have to understand what they do. Why did you have to change your markup, and why did you have to change a few selectors, properties?
Margins act strange
A common problem developers face is when two elements don’t add up their margins (top/bottom). For example, we have two <p> elements with margin: 20px 0;. The total space between them is 20px, not 40. This is due to margin collapsing. Except if the elements float or are absolutely positioned. This “Except” is almost everywhere for almost every definition.
Z-index: 9999999 and still not on top of z-index: 1
First of all, there is rarely any need to write such high z-indexes. I’ve even seen values like 99999999999999 or more. First, the z-index max-value is 2147483647, so anything above is useless. Second, it doesn’t work like that. Which elements are on top is defined by the stacking context, not only the z-index value?
Not Animating the Right Properties
It’s a rather simple question, but some developers struggle when it comes to animations and the approach to take. First, what can be animated? Anything that can have a starting value, an end value and anything in between. Opacity is such a property, you can start from 0, end on 1, and add infinite steps between like 0.3, 0.6758, 0.9875 etc. You can’t animate “display” because there are no middle steps between inline and grid.
Inefficient Animations
The most common reason for bad FPS on animations is due to the wrong properties being animated. If you change the “left” property of an absolute element, you will be utilizing the CPU to calculate. However, if you use transform, then it would be the GPU. And the GPU as we all know is better to do graphics.
But how would you animate shadow, when it has no alternative like left/transform? Well, animate opacity! Have two elements, one with start, state, one with end state. Then fade the start out and the end in. This would make it look like the shadow expands, where in fact it only fades.
Do NOT animate elements that affect the layout if possible. Layout calculations are expensive and a CPU can only do a limited number of calculations. Often, less than one every 16ms, which will result in FPS under 60.
Too Deep Selectors
What is a modifier? A class, that you can add to another class and replace some of its properties. Example: .button has color: red. .button-primary, has color: blue. So, when you add .button-primary to .button, you want to end up with a blue color. Easy peasy. Although, not every time it is so easy. And not every time it’s a component like this.
Sometimes, we want to overwrite a component, child of another component on a specific page. So we end up with something like this: .page-name .section-name .component-1 .component-2 { … } Already four levels deep. But what it turns out is that we can simply do .page-name .componen-2 { … }. Shorter is better! Because if it happens that for some reason you have to overwrite that new style, you don’t have to go 5 levels deep, then 6, then more.
Deep selectors are scary and people end up using !important. !important should be used when you want to overwrite inline styles to which you have no control. Otherwise, it’s a red flag that you are not doing something right.
Too large file
Now, of course, for a large application there would be a lot of styles, and that is completely understandable. But is it always that large? Bad way of extending classes in SASS (through mixins) would generate large selector chains, which take a few seconds to scroll to see them all. A proper extension would reduce that file a lot. Could even double down the size.
Included frameworks, of which only a few properties are in use are also a common reason for large files. Bundle together bootstrap/foundation, font-awesome, animate.css and a few other similar frameworks/libraries and you end up with a huge file from which you use about 2%. Clean up, keep it tidy and only use what is really needed. A lot of times there isn’t even a need for a framework.
Framework when it isn’t needed
Bootstrap, Foundation, UIKit and all other frameworks are great! They solve a real problem and are extremely valuable to the web development community. There is no need-to-know HTML or CSS that well to craft dashboards and sites. But walk away from that case, start writing some CSS and you will hit a few problems.
- You have to understand properly how to build them and how to include them
- You have to read their documentation to find the custom settings and mixins they provide.
- You have to follow their markup and teach your team to do so.
- You have to truly understand how to extend and style to create a unique view.
To include them properly means that you use only what you need. To have such a framework help you instead of prohibit you from building anything custom, you have to know it’s inner workings better than average. And to achieve any custom design, you will have to modify settings and properties. And then write some custom CSS. And that custom CSS to overwrite whatever the framework gives.
So, if there is no real need for such a CSS framework, but there is still one and it’s not been utilized properly, this is a red flag that something is wrong. And when you start once with it, you will most likely end up using it till the end of the project’s life cycle. So, it’s an important decision.
Not allowing content to define size
This one is more of a beginner level problem, but it’s a very common one. You should give the content the freedom it deserves.
Example, you don’t want to set the site’s width property to 1200px. You want to set the max-width to 1200px. That way, the viewport will allow your site to remain responsive.
Then, an input field shouldn’t have 40px height, it should have 1em padding. Now, the font-size, the content is what defines the size. And if the content inside grows, the element will not break.
This way of thinking when defining sizes remains important throughout the whole application. And each property has to consider it. Ideally, do not set % for width on content area, because then you have to also write media queries.
If you had max-width, when your browser shrinks under that max width, the content will fill up the browser width and then shrink normally. Else, you would have to write a media query for that size. And then for any other custom size written on the site. This is complexity added due to the bad approach. Almost the same as all other points we’ve already discussed.
JavaScript hacks
JavaScript can do wonders! But it’s not always needed. You can do so much with just CSS that is well supported, doesn’t require functionality (that is best automatically tested afterwards) and doesn’t require your other FE members to know JS too.
Of course, there is the standard toggle functionality, where you can use a checkbox input to change the styles when :checked, there is the background overlay that can be easily triggered and there are properties like counter to create more than simple <ol> lists.
The advice here is to look for CSS solutions first. Let’s say you want to build a popup. It shows up in the middle (position: fixed). Then you want to close it from the [X] icon or when you click around it. In JS you would need to write events that would trigger on anything except the popup content. But still trigger on the [X] which is in the popup content.
Instead, from CSS you can just write another <div> that has 100vw, 100vh size and z-index just under the popup. Then you can simply target that div in JS, which is a one-liner.
Another example – you want a button you can place in content, which will “expand” any content that follows it. Simple with CSS, a bit more complex with JS. With CSS you can do something like this:
Label.button – styles the button visually.
Input:not(:checked) ~ * { display: none} – hide everything after it in the same container.
That bit “in the same container”, you need custom logic in JS. You also need to select all elements, which requires traversing the DOM tree. And then apply CSS styles to them, which would end up in style=”” tag, which in turn would require !important if for some reason you have to address them. IN CSS, thankfully, it’s extremely simple.
Conclusion
As you can see, these are very few common issues. To cover them all, a book would be needed. But hopefully, they will give you a good starting point and remind you to think about any potential problem that could arise due to the wrong approach. Locating such problems comes with a ton of experience and a lot of reading. And if you reached the conclusion here, then you are on the right path with the reading, now it’s time to practice!