You know the feeling when youβre trying to achieve some styling or layout with CSS and it just wonβt work? I know I do and if youβve done any kind of work with CSS you probably had that feeling many times too, especially when you were starting out.
CSS looks (and is) simple on the surface, and as a result people tend to not spend too much time learning much about it. Certainly not as much as they would another, perhaps more traditional, language.
However, the sheer number of properties, values, and ways you can combine them makes it a complicated language after all, and one where itβs hard to know just how to achieve a given result. On top of this, CSS is a very resilient programming language. It will apply to your page, at least partially. Even if you get it wrong, if you still follow the general syntax, your code will load, be parsed, and (partially) run.
JavaScript, being an imperative language, lets you describe how to do something. If the code you wrote doesnβt lead to the expected result, then you investigate why. This is often done by adding console logs or using a debugger.
On the other hand, CSS is a declarative language. With it, you tell the computer what to do, not how to do it. This means the browser engine is the one that figures out the βhowβ part and itβs all hidden from you. You donβt have access to that part of the code and you have no way of adding console messages to it or connecting a debugger to it.
You might get some error messages if your syntax is invalid, which is good, but itβs not enough to debug cases where your code parses and executes fine but just doesnβt do the thing you wanted it to do.
There may be a whole lot of CSS rules and properties you added that were accepted by the browser as totally valid but that didnβt actually produce the effect you were looking for, and that you canβt debug.
Browsers do show certain CSS warnings when things are invalid. Below is a screenshot of a few different CSS warnings that get displayed in the web console in Firefox:
As shown above, Firefox warns about dropped declarations (when a property name is unknown), values that couldnβt be parsed, bad selectors, or at-rules.
These warnings are very useful, but there are times when you donβt get any of them when your code is valid but just doesnβt yield the expected result. What do you do then? You try stuff out until it works. You ask people. You learn CSS.
Weβve all internalized this as the normal way to go about working with the language, but letβs face it, itβs very ineffective and not a great developer experience.
Would it be useful if we could debug what the browser did as a result of applying some CSS code to a page? Iβm not sure. It might actually be awfully complex given how complicated layout engines are.
There is something else that sets CSS apart from languages such as JavaScript that might come as an advantage here. CSS is a domain-specific language, it is in that sense, more constrained than JavaScript. Its only purpose is to style elements on a page.
Letβs take an example. If youβre using the width
property then itβs probably safe to assume you are trying to describe how wide an element should be, and if whether you add or remove this property does not visually impact the styling of the page, then that property didnβt do the thing you want to do. So perhaps what we need isnβt a debugger in the traditional sense of the term, but instead, a contextual helper that tells you when things donβt actually do what you think they do.
But before jumping to conclusions, letβs look at a few examples.
Let me list a few common examples for when CSS code applies fine but just doesnβt do anything.
Say you have a line of text and you want it to always fit in some container element, whatever its width. A common design pattern to solve this is cutting the text off before it overflows and displaying an ellipsis.
text-overflow
is the CSS property that can make that happen for you. It has a pretty self-explanatory name and a reasonable number of values that seem to make sense. On the surface, itβs a really simple property to use, right?
If youβve already used this successfully, then please assume you havenβt for a second. Go back to when you didnβt know about it. Or go on Stack Overflow and look at how many people ask about it, and try to put yourself in their shoes.
It turns out this property only works on block elements, it also only works in the direction the line of text is progressing in (i.e. not at the bottom of a paragraph of text) and it also only works if the element also has overflow:hidden
applied to it.
Not that simple anymore, is it? Itβs actually even more complicated than this as it does work with some inline elements that result in a βblock frameβ under the hood in the layout engine. For instance, a fieldset's legend
element is an inline element, but it still supports text-overflow
because the layout engine βblockifiesβ this element at run-time.
So, the point is, applying the text-overflow
property to the element is only one part of the puzzle. It will apply fine, but in many cases wonβt lead to the result people expect. On the other hand, it will lead to a lot of frustration and time lost.
width
and height
donβt apply to inline elements, only to blocks. That might seem very obvious to you, but again, assume that youβre starting out with CSS and donβt know it yet.
I certainly remember having to look it up a few times when starting out in my career and that it wasnβt obvious to me at the time why these, seemingly super useful properties, didnβt have any effect on my elements. Again, because CSS seems really simple at first, I think we just overlook the traditional learning phase that normally comes with most programming languages. If you wanted to use a JavaScript function, chances are, youβd probably look it up on MDN and checked what it applied to.
But with CSS, because things like color:blue
seems so simple, we assume the rest of it is simple too, and we just try to use it.
In the case of width
and height
, they only apply to block elements, which means you have to know what is a block element vs. an inline element first. But even more than that, you kind of also need to know what a replaced element is and thatβs a lot more involved.
Replaced elements are elements that donβt get their content rendered by CSS. Examples include <img>
and <audio>
. Now, those can be (and are by default) inline elements, but width
and height
do work on them anyway!
This is probably one of the most common sources of frustration for people. CSS has all of these useful-sounding properties like vertical-align
or align-items
that seems like they would do what you want them to.
However, it turns out you need to know a whole lot of complicated concepts such as what a block or an inline formatting context is or what a grid or flex layout is before you can start using these properties confidently (instead of, you know, trying them out until they work).
For example, align-*
properties only apply in the block/cross/column direction of a layout whereas justify-*
properties apply in the other direction.
So, as you can see, there are cases where CSS seems very simple on the surface when it actually requires expert knowledge for you to achieve a particular effect.
There are dozens of examples like the ones above. If you have examples of your own, Iβd love to hear them! Tweet them at me @patrickbrosset.
What if there was a way for developer tools to help you with the above? We have JavaScript debuggers and consoles to investigate when JS code doesnβt do what we want it to do. But we donβt have much for CSS.
Debugging CSS code in the traditional sense of the term might not be the right idea, but tooling can still help here. CSS is very expressive and it has a visual impact on the page. Based on this, it should be possible to inform DevTools users proactively when CSS doesnβt have any effect.
This is where Firefoxβs Inactive CSS feature comes in. This is a feature my team and I worked on and shipped in Firefox 70 at the end of 2019.
Hereβs a screenshot showing what this feature looks like:
At its core, it is very simple and intuitive. Any CSS declaration that is detected as being inactive will be shown greyed-out and will be followed by an information icon that provides an explanation about the problem.
An inactive declaration is one that is valid in terms of syntax, and which applies fine to the element, but just does not produce any effect on the element.
Here is another example screenshot:
Back in 2018, we asked a lot of questions to web authors out there related to what challenges they were having when writing CSS. Certain things came back over and over again, like why is this element not fitting in this space? Why doesnβt that property do anything? How did this element get to be that size? It was clear that people needed help with understanding what properties had an effect on an element.
The other thing that really made it click for us was Sarah Limβs research paper and Ply Web Inspector tool. In the paper, Sarah and her colleagues describe how βvisually ineffective propertiesβ are a main source of frustration and time lost when working with CSS. Those are properties that, whether they are added or removed, donβt cause any difference on the page.
Sarah Limβs Ply tool uses visual diffing to detect inactive CSS properties. This essentially means taking a screenshot with the property applied, then another one without the property applied and diffing those two images.
This approach is very generic and will apply to all properties in the same way which is great, and it works great for Ply. It can also be a little costly in terms of processing required to do this for all properties applied to a specific element in DevTools. We knew this wasnβt an approach we could use.
In Firefox DevTools, the user experience we designed for Inactive CSS is such that you get a warning about properties as soon as you select an element in the Inspector panel and its applied CSS rules get displayed in the rules sidebar. This is such a common thing users do all the time that we couldnβt make this any slower. So diffing images wasnβt a viable approach for DevTools.
Instead, we added a simple rules engine to DevTools that would do this. In this engine, each rule is responsible for checking if a property (or a set of properties) is active or not, depending on the current conditions.
For example, here is the (simplified) code for one of these rules:
// Flex container property used on non-flex container { invalidProperties: ["flex-direction", "flex-flow", "flex-wrap"], when: () => !this.flexContainer, }
What that means is the engine will return a warning when the current element is not a flexbox container and one of flex-direction
, flex-flow
or flex-wrap
is used on this element.
Here are all of the rules currently implemented in DevTools.
I think itβs important to realize that this feature comes with a couple of constraints, so let me tell you what it is not.
This tool wonβt tell you about all CSS properties. If you did look at the list of rules currently implemented, you probably realized that weβve made it so it would tell you about the most common pitfalls only, to start with. The list of things Inactive CSS warns you about will grow over time, but starting with a small number of common cases first was important to make sure the feature was rock solid.
Secondly, it is not an audit tool. Such a tool would be awesome, but this is not what Inactive CSS is about. Many people ask if it can scan their entire CSS codebase and tell them about things that can be removed. This is not what Inactive CSS does! Instead, it can tell you things like on this given page, this specific element, under the current circumstances (window size, state, etc.) has a given property that does not have any effect at all.
In that sense, Inactive CSS is a debugging tool, not an audit tool. The way to approach it is if you are trying to create some layout, or center something, or align something and things are not working, then select this element in the Inspector panel, and let the tool tell you why some properties arenβt taking effect.
As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If youβre interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.
Modernize how you debug web and mobile apps β start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocketβs Content Advisory Board. Youβll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.