Tuesday, April 7, 2026

Alternate options to the !essential Key phrase


Now and again, I stumble onto an outdated challenge of mine, or worse, another person’s, and I’m reminded simply how chaotic CSS can recover from time. In most of those circumstances, the !essential key phrase appears to be concerned in a method or one other. And it’s straightforward to know why builders depend on it. It offers an instantaneous repair and forces a rule to take priority within the cascade.

That’s to not say !essential doesn’t have its place. The issue is that after you begin utilizing it, you’re now not working with the cascade; you’re bypassing it. This will shortly get out of hand in bigger tasks with a number of folks engaged on them, the place every new override makes the following one more durable.

Cascade layers, specificity tips, smarter ordering, and even some intelligent selector hacks can usually substitute !essential with one thing cleaner, extra predictable, and much much less embarrassing to elucidate to your future self.

Let’s speak about these options.

Specificity and !essential

Selector specificity is a deep rabbit gap, and never the objective of this dialogue. That stated, to know why !essential exists, we have to have a look at how CSS decides which guidelines apply within the first place. I wrote a transient overview on specificity that serves as place to begin. Chris additionally has a concise piece on it. And when you actually wish to go deep into all the sting circumstances, Frontend Masters has a thorough breakdown.

In brief, CSS offers every selector a sort of “weight.” When two guidelines goal the identical factor, the rule with larger specificity wins. If the specificity is equal, the rule declared later within the stylesheet takes priority.

  • Inline kinds (fashion="...") are the heaviest.
  • ID selectors (#header) are stronger than courses or kind selectors.
  • Class, attribute, and pseudo-class selectors (.btn, [type="text"], :hover) carry medium weight.
  • Sort selectors and pseudo-elements (div, p, ::earlier than) have the bottom weight. Though, the * selector is even decrease with a specificity of 0-0-0 in comparison with kind selectors which have a specificity of 0-0-1.
/* Low specificity (0,0,1) */
p {
  colour: grey;
}

/* Medium specificity (0,1,0) */
.button {
  colour: blue;
}

/* Excessive specificity (1,1,0) */
#header .button {
  colour: purple;
}

Hi there

Inline kinds being the heaviest additionally explains why they’re usually frowned upon and never thought of “clear” CSS since they bypass a lot of the regular construction we attempt to keep.

!essential adjustments this habits. It skips regular specificity and supply order, pushing that declaration to the highest inside its origin and cascade layer:

p {
  colour: purple !essential;
}

#essential p {
  colour: blue;
}

Though #essential p is extra particular, the paragraph will seem purple as a result of the !essential declaration overrides it.

Why !essential could be problematic

Right here’s the standard lifecycle of !essential in a challenge involving a number of builders:

“Why isn’t this working? Add !essential. Okay, fastened.”

Then another person comes alongside and tries to alter that very same part. Their rule doesn’t apply, and after some digging, they discover the !essential. Now they’ve a selection:

  • take away it and threat breaking one thing else,
  • or add one other !essential to override it.

And since nobody is totally positive why the primary one was added, the safer transfer usually seems like including one other one. This will shortly spiral uncontrolled in bigger tasks.

On a extra technical notice, the basic downside with !essential is that it breaks the supposed order of the cascade. CSS is designed to resolve conflicts predictably by means of specificity and supply order. Later guidelines override earlier ones, and extra particular selectors override much less particular ones.

A typical place the place this turns into apparent is theme switching. Think about the instance beneath:

.button {
  colour: purple !essential;
}

.darkish .button {
  colour: white;
}

Even inside a darkish theme, the button stays purple. This ends in the stylesheet turning into more durable to cause about, as a result of the cascade is now not predictable.

In massive groups, particularly, this ends in upkeep and debugging turning into more durable. None of this implies !essential ought to by no means be used. There are reliable circumstances for it, particularly in utility courses, accessibility overrides, or person stylesheets. However when you’re utilizing it as your go-to methodology to resolve a selector/styling battle, it’s normally an indication that one thing else within the cascade wants consideration.

Let’s have a look at options.

Cascade layers

Cascade layers are a extra superior function of CSS, and there’s a lot of principle on them. For the needs of this dialogue, we’ll deal with how they assist you keep away from !essential. If you wish to be taught extra, Miriam Suzanne wrote a full information on CSS Cascade Layers on it that goes into appreciable element.

In brief, cascade layers allow you to outline specific precedence teams in your CSS. As an alternative of counting on selector specificity, you determine up entrance which class of kinds ought to take priority. You possibly can outline your layer order up entrance:

@layer reset, defaults, elements, utilities;

This establishes precedence from lowest to highest. Now you’ll be able to add kinds into these layers:

@layer defaults {
  a:any-link {
    colour: maroon;
  }
}

@layer utilities {
  [data-color="brand"] {
    colour: inexperienced;
  }
}

Though [data-color="brand"] has decrease specificity than a:any-link, the utilities layer takes priority as a result of it was outlined later within the layer stack.

It’s price noting that specificity nonetheless works inside a layer. However between layers, layer order is given precedence.

With cascade layers, you’ll be able to prioritize complete classes of kinds as an alternative of particular person guidelines. For instance, your “overrides” layer at all times takes priority over your “base” layer. This form of architectural considering, as an alternative of reactive fixing saves a variety of complications down the road.

One quite common instance is integrating third-party CSS. If a framework ships with extremely particular selectors, you are able to do this:

@layer framework, elements;

@import url('framework.css') layer(framework);

@layer elements {
  .card {
    padding: 2rem;
  }
}

Now your part kinds robotically override the framework kinds, no matter their selector specificity, so long as the framework isn’t utilizing !essential.

And whereas we’re speaking about it, it’s good to notice that utilizing !essential with cascade layers is definitely counterintuitive. That’s as a result of !essential really reverses the layer order. It’s now not a fast method to bounce to the highest of the priorities — however an built-in a part of our cascade layering; a manner for decrease layers to insist that a few of their kinds are important.

So, if we have been to order a set of layers like this:

  1. utilities (strongest)
  2. elements
  3. defaults (least highly effective)

Utilizing !essential flips issues on their head:

  1. !essential defaults (strongest)
  2. !essential elements
  3. !essential utilities
  4. regular utilities
  5. regular elements
  6. regular defaults (least highly effective)

Discover what occurs there: it generates three new, reversed essential layers that supersede the unique three layers whereas reversing all the order.

The :is() pseudo

The :is() pseudo-class is attention-grabbing as a result of it takes the specificity of its most particular argument. Say you’ve a part that should match the load of a extra particular selector elsewhere within the codebase:

/* someplace in your kinds */
#sidebar a {
  colour: grey;
}

/* your part */
.nav-link {
  colour: blue;
}

Quite than utilizing !essential, you’ll be able to bump .nav-link up by wrapping it in :is() with a extra particular argument:

:is(#some_id, .nav-link) {
  colour: blue;
}

Now this has id-level specificity whereas matching solely .nav-link. It’s price noting that the selector inside :is() doesn’t need to match an precise factor. We’re utilizing #some_id purely to extend specificity on this case.

Word: If #some_id really exists in your markup, this selector would additionally match that factor. So it might be finest to make use of an id not getting used to keep away from unintended effects.

On the flip aspect, :the place() does the other. It at all times resolves to a specificity of (0,0,0), it doesn’t matter what’s inside it. That is helpful for reset or base kinds the place you need something downstream to override simply.

Doubling up a selector

A fairly simple manner of accelerating a selectors specificity is repeating the selector. That is normally accomplished with courses. For instance:

.button {
  colour: blue;
}

.button.button {
  colour: purple;  /* larger specificity */
}

You’d usually not wish to do that too usually as it will possibly develop into a readability nightmare.

Reordering

CSS resolves ties in specificity by supply order, so a rule that comes later is prioritized. That is straightforward to miss, particularly in bigger stylesheets the place kinds are unfold throughout a number of information.

If a extra generic rule retains overriding a extra focused one and the specificity is similar, test whether or not the generic rule is being loaded after yours. Flipping the order can repair the battle without having to extend specificity.

That is additionally why it’s price desirous about stylesheet group from the beginning. A typical sample is to go from generic to particular (resets and base kinds first, then structure, then elements, then utilities).

When utilizing !essential does make sense

In any case that, it’s price being clear: !essential does have reliable use circumstances. Chris mentioned this some time again too, and the feedback are price a learn too.

The most typical case is utility courses. For instance, the entire level of courses like .visually-hidden is that they do one factor, in all places. On this circumstances, you don’t desire a extra particular selector quietly undoing it elsewhere. The identical is true for state courses like .disabled or generic part kinds like .button.

.visually-hidden {
  place: absolute !essential;
  width: 1px !essential;
  top: 1px !essential;
  overflow: hidden !essential;
  clip-path: inset(50%) !essential;
}

Third-party overrides are one other frequent situation. !essential can be utilized right here to both override inline kinds being set in JavaScript or regular kinds in a stylesheet that you would be able to’t edit.

From an accessibility viewpoint, !essential is irreplaceable for person stylesheets. Since these are utilized on all webpages and there’s just about no method to assure if the stylesheets’ selectors will at all times have the very best specificity, !essential is mainly the one dependable manner to ensure your kinds at all times get priority.

One other good instance is in terms of respecting a person’s browser preferences, comparable to lowering movement:

@media display screen and (prefers-reduced-motion: cut back) {
  * {
    animation-duration: 0.001ms !essential;
    animation-iteration-count: 1 !essential;
    transition-duration: 0.001ms !essential;
  }
}

Wrapping up

The distinction between good and unhealthy use of !essential actually comes all the way down to intent. Are you utilizing it since you perceive the CSS Cascade and have made a name that this declaration ought to at all times apply? Or are you utilizing it as a band-aid? The latter will inevitably trigger points down the road.

Additional studying

Related Articles

Latest Articles