selectors – CSS-Tricks https://css-tricks.com Tips, Tricks, and Techniques on using Cascading Style Sheets. Tue, 09 Jul 2024 15:18:17 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.5 https://i0.wp.com/css-tricks.com/wp-content/uploads/2021/07/star.png?fit=32%2C32&ssl=1 selectors – CSS-Tricks https://css-tricks.com 32 32 45537868 “If” CSS Gets Inline Conditionals https://css-tricks.com/if-css-gets-inline-conditionals/ https://css-tricks.com/if-css-gets-inline-conditionals/#comments Tue, 09 Jul 2024 15:18:11 +0000 https://css-tricks.com/?p=379002 A few sirens went off a couple of weeks ago when the CSS Working Group (CSSWG) resolved to add an if() conditional to the CSS Values Module Level 5 specification. It was Lea Verou’s X post that same day that …


“If” CSS Gets Inline Conditionals originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
A few sirens went off a couple of weeks ago when the CSS Working Group (CSSWG) resolved to add an if() conditional to the CSS Values Module Level 5 specification. It was Lea Verou’s X post that same day that caught my attention:

Lea is the one who opened the GitHub issue leading to the discussion and in a stroke of coincidence — or serendipity, perhaps — the resolution came in on her birthday. That had to be quite a whirlwind of a day! What did you get for your birthday? “Oh, you know, just an accepted proposal to the CSS spec.” Wild, just wild.

The accepted proposal is a green light for the CSSWG to work on the idea with the intent of circulating a draft specification for further input and considerations en route to, hopefully, become a recommended CSS feature. So, it’s gonna be a hot minute before any of this is baked, that is, if it gets fully baked.

But the idea of applying styles based on a conditional requirement is super exciting and worth an early look at the idea. I scribbled some notes about it on my blog the same day Lea posted to X and thought I’d distill those here for posterity while rounding up more details that have come up since then.

This isn’t a new idea

Many proposals are born from previously rejected proposals and if() is no different. And, indeed, we have gained several CSS features in recent days that allow for conditional styling — :has() and Container Style Queries being two of the more obvious examples. Lea even cites a 2018 ticket that looks and reads a lot like the accepted proposal.

The difference?

Style queries had already shipped, and we could simply reference the same syntax for conditions (plus media() and supports() from Tab’s @when proposal) whereas in the 2018 proposal how conditions would work was largely undefined.

Lea Verou, “Inline conditionals in CSS?”

I like how Lea points out that CSS goes on to describe how CSS has always been a conditional language:

Folks… CSS had conditionals from the very beginning. Every selector is essentially a conditional!

Lea Verou, “Inline conditionals in CSS?”

True! The Cascade is the vehicle for evaluating selectors and matching them to HTML elements on a page. What if() brings to the table is a way to write inline conditions with selectors.

Syntax

It boils down to this:

<if()> = if( <container-query>, [<declaration-value>]{1, 2} )

…where:

  • Values can be nested to produce multiple branches.
  • If a third argument is not provided, it becomes equivalent to an empty token stream.

All of this is conceptual at the moment and nothing is set in stone. We’re likely to see things change as the CSSWG works on the feature. But as it currently stands, the idea seems to revolve around specifying a condition, and setting one of two declared styles — one as the “default” style, and one as the “updated” style when a match occurs.

.element {
  background-color:
    /* If the style declares the following custom property: */
    if(style(--variant: success),
      var(--color-green-50), /* Matched condition */
      var(--color-blue-50);  /* Default style */
    );
}

In this case, we’re looking for a style() condition where a CSS variable called --variant is declared and is set to a value of success, and:

  • …if --variant is set to success, we set the value of success to --color-green-50 which is a variable mapped to some greenish color value.
  • …if --variant is not set to success, we set the value of the success to --color-blue-50 which is a variable mapped to some bluish color value.

The default style would be optional, so I think it can be omitted in some cases for slightly better legibility:

.element {
  background-color:
    /* If the style declares the following custom property: */
    if(style(--variant: success),
      var(--color-green-50) /* Matched condition */
    );
}

The syntax definition up top mentions that we could support a third argument in addition to the matched condition and default style that allows us to nest conditions within conditions:

background-color: if(
  style(--variant: success), var(--color-success-60), 
    if(style(--variant: warning), var(--color-warning-60), 
      if(style(--variant: danger), var(--color-danger-60), 
        if(style(--variant: primary), var(--color-primary)
      )
    ),
  )
);

Oomph, looks like some wild inception is happening in there! Lea goes on to suggest a syntax that would result in a much flatter structure:

<if()> = if( 
  [ <container-query>, [<declaration-value>]{2}  ]#{0, },
  <container-query>, [<declaration-value>]{1, 2} 
)

In other words, nested conditions are much more flat as they can be declared outside of the initial condition. Same concept as before, but a different syntax:

background-color: if(
  style(--variant: success), var(--color-success-60), 
  style(--variant: warning), var(--color-warning-60),
  style(--variant: danger), var(--color-danger-60), 
  style(--variant: primary), var(--color-primary)
);

So, rather than one if() statement inside another if() statement, we can lump all of the possible matching conditions into a single statement.

We’re attempting to match an if() condition by querying an element’s styles. There is no corresponding size() function for querying dimensions — container queries implicitly assume size:

.element {
  background: var(--color-primary);

  /* Condition */
  @container parent (width >= 60ch) {
    /* Applied styles */
    background: var(--color-success-60);
  }
}

And container queries become style queries when we call the style() function instead:

.element {
  background: orangered;

  /* Condition */
  @container parent style(--variant: success) {
    /* Applied styles */
    background: dodgerblue;
  }
}

Style queries make a lot more sense to me when they’re viewed in the context of if(). Without if(), it’s easy to question the general usefulness of style queries. But in this light, it’s clear that style queries are part of a much bigger picture that goes beyond container queries alone.

There’s still plenty of things to suss out with the if() syntax. For example, Tab Atkins describes a possible scenario that could lead to confusion between what is the matched condition and default style parameters. So, who knows how this all shakes out in the end!

Conditions supporting other conditions

As we’ve already noted, if() is far from the only type of conditional check already provided in CSS. What would it look like to write an inline conditional statement that checks for other conditions, such as @supports and @media?

In code:

background-color: if(
  supports( /* etc. */ ),
  @media( /* etc. */ )
);

The challenge would be container supporting size queries. As mentioned earlier, there is no explicit size() function; instead it’s more like an anonymous function.

@andruud has a succinctly describes the challenge in the GitHub discussion:

I don’t see why we couldn’t do supports() and media(), but size queries would cause cycles with layout that are hard/impossible to even detect. (That’s why we needed the restrictions we currently have for size CQs in the first place.

“Can’t we already do this with [X] approach?”

When we were looking at the syntax earlier, you may have noticed that if() is just as much about custom properties as it is about conditionals. Several workarounds have emerged over the years to mimic what we’d gain if() we could set a custom property value conditionally, including:

  • Using custom properties as a Boolean to apply styles or not depending on whether it is equal to 0 or 1. (Ana has a wonderful article on this.)
  • Using a placeholder custom property with an empty value that’s set when another custom property is set, i.e. “the custom property toggle trick” as Chris describes it.
  • Container Style Queries! The problem (besides lack of implementation) is that containers only apply styles to their descendants, i.e., they cannot apply styles to themselves when they meet a certain condition, only its contents.

Lea gets deep into this in a separate post titled “Inline conditional statements in CSS, now?” that includes a table that outlines and compares approaches, which I’ll simply paste below. The explanations are full of complex CSS nerdery but are extremely helpful for understanding the need for if() and how it compares to the clever “hacks” we’ve used for years.

MethodInput valuesOutput valuesProsCons
Binary Linear InterpolationNumbersQuantitativeCan be used as part of a valueLimited output range
Togglesvar(--alias) (actual values are too weird to expose raw)AnyCan be used in part of a valueWeird values that need to be aliased
Paused animationsNumbersAnyNormal, decoupled declarationsTakes over animation property

Cascade weirdness
Type GrindingKeywordsAny value supported by the syntax descriptorHigh flexibility for exposed APIGood encapsulationMust insert CSS into light DOM

Tedious code (though can be automated with build tools)

No Firefox support (though that’s changing)
Variable animation nameKeywordsAnyNormal, decoupled declarationsImpractical outside of Shadow DOM due to name clashes

Takes over animation property

Cascade weirdness

Happy birthday, Lea!

Belated by two weeks, but thanks for sharing the spoils of your big day with us! 🎂

References

To Shared LinkPermalink on CSS-Tricks


“If” CSS Gets Inline Conditionals originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/if-css-gets-inline-conditionals/feed/ 6 379002
Case-Sensitive Selectors https://css-tricks.com/snippets/css/case-sensitive-selectors/ Wed, 22 May 2024 18:24:49 +0000 https://css-tricks.com/?page_id=378046 /* Case sensitive */ a[href*='css-tricks' s] {} /* Case insensitive */ a[href*='css-tricks' i] {}

Adding an s makes the selector case-sensitive and i makes it case-insensitive.

Source


Case-Sensitive Selectors originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
/* Case sensitive */ a[href*='css-tricks' s] {} /* Case insensitive */ a[href*='css-tricks' i] {}

Adding an s makes the selector case-sensitive and i makes it case-insensitive.


Case-Sensitive Selectors originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
378046
@supports selector() https://css-tricks.com/supports-selector/ https://css-tricks.com/supports-selector/#comments Tue, 19 Oct 2021 21:12:18 +0000 https://css-tricks.com/?p=354115 I didn’t realize the support for @supports determining selector support was so good! I usually think of @supports as a way to test for property: value pair support. But with the selector() function, we can test for selector support …


@supports selector() originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
I didn’t realize the support for @supports determining selector support was so good! I usually think of @supports as a way to test for property: value pair support. But with the selector() function, we can test for selector support as well. It looks like this:

@supports selector(:nth-child(1 of .foo)) {

}

You just drop the selector right between the parens and that’s what it tests for.

That selector above is a pretty good test, actually. It’s a “selector list argument” that works for the :nth-child ‘n’ friends selectors. As I write, it’s only supported in Safari.

So let’s say your ideal situation is that the browser supports this selector. Here’s an example. You know that with <ol> and <ul> the only valid child element is <li>. But also say this list needs separators, so you (and I’m not saying this is a great idea) did this kind of thing:

<ul>
  <li class="list-item">List item</li>
  <li class="list-item">List item</li>
  <li class="separator"></li>
  /* ... */
</ul>

Then you also want to zebra-stripe the list. And, if you want zebra striping, you need to select every other .list-item, ignoring the .separator. So…

li:nth-child(odd of .list-item) {
  background: lightgoldenrodyellow;
}

But only Safari supports that… so you can do:

@supports selector(:nth-child(1 of .foo)) {
  li:nth-child(odd of .list-item) {
    background: lightgoldenrodyellow;
  }
}

If you didn’t care what the fallback was, you wouldn’t even have to bother with the @supports at all. But say you do care about the fallback. Perhaps in the supported situation, the zebra striping does the heavy lifting of the UX you are shooting for, so all you need for the seperator is a bit of space. But for non-supporting browsers, you’ll need something beefier because you don’t have the zebra striping.

So now you can style both situations:

@supports selector(:nth-child(1 of .foo)) {
  li {
    padding: 0.25em;
  }
  li:nth-child(odd of .list-item) {
    background: lightgoldenrodyellow;
  }
  li.separator {
    list-style: none;
    margin: 0.25em 0;
  }
}
@supports not selector(:nth-child(1 of .foo)) {
  li.separator {
    height: 1px;
    list-style: none;
    border-top: 1px dashed purple;
    margin: 0.25em 0;
  }
}

If we get the @when syntax, then we can write it a little cleaner:

/* Maybe? */
@when supports(selector(:nth-child(1 of .foo))) {

} @else {

}

Anyway. The end result is…

Supported
Not Supported

There is a JavaScript API for testing support as well. I wasn’t sure if this would actually work, but it appears to! This fails in Chrome and passes in Safari as I write:

CSS.supports("selector(:nth-child(1 of .foo))")

While I was putting this together, I was thinking… hmmmmmmm — what CSS selectors are out there that have weird cross-browser support? It’s really not that many. And even of those that do have weird cross-browser support, thinking of the number of use-cases where you care to actually wrap it in an @supports (rather than just let it fail) is fairly few.

The ::marker pseudo-element would have been a great one, but it’s pretty well supported now. I was thinking the case-insensitive attribute selector, like [href$="pdf" i], would have been a good one, but nope, also well supported. Same deal with the comma-separated :not(a, .b, [c]). Maybe something like :fullscreen / :-webkit-full-screen would be interesting and useful because it’s uniquely not supported in iOS Safari?


@supports selector() originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/supports-selector/feed/ 2 354115
You want enabling CSS selectors, not disabling ones https://css-tricks.com/you-want-enabling-css-selectors-not-disabling-ones/ https://css-tricks.com/you-want-enabling-css-selectors-not-disabling-ones/#comments Tue, 31 Aug 2021 20:17:25 +0000 https://css-tricks.com/?p=350788 I think this is good advice from Silvestar Bistrović:

An enabling selector is what I call a selector that does a job without disabling the particular rule.

The classic example is applying margin to everything, only to have to remove …


You want enabling CSS selectors, not disabling ones originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
I think this is good advice from Silvestar Bistrović:

An enabling selector is what I call a selector that does a job without disabling the particular rule.

The classic example is applying margin to everything, only to have to remove it from the final element because it adds space in a place you don’t want.

.card {
  margin-bottom: 1rem;
}

/* Wait but not on the last one!! */
.parent-of-cards :last-child {
  margin-bottom: 0;
}

You might also do…

/* "Disabling" rule */
.card:last-child {
  margin-bottom: 0;
}

But that’s maybe not as contextual as selecting from the parent.

Another variation is:

.card:not(:last-child) {
  margin-bottom: 1rem;
}

That’s what Silvestar refers to as “enabling” because you’re only ever applying this rule — not applying it and then removing it with another selector later. I agree that’s harder to understand and error-prone.

Yet another example is a scoped version of Lobotomized Owls:

/* Only space them out if they stack */
.card + .card {
  margin-top: 1rem;
}

I think gap is where this is all headed in the long term. Put the onus on the parent, not the child, and keep it an enabling selector:

.parent-of-cards {
  display: grid;
  gap: 1rem;
}

To Shared LinkPermalink on CSS-Tricks


You want enabling CSS selectors, not disabling ones originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/you-want-enabling-css-selectors-not-disabling-ones/feed/ 4 350788
CSS Vocabulary https://css-tricks.com/css-vocabulary/ Mon, 27 Jul 2020 21:47:42 +0000 https://css-tricks.com/?p=317807 This is a neat interactive page by Ville V. Vanninen to reference the names of things in the CSS syntax. I feel like the easy ones to remember are “selector,” “property,” and “value,” but even as a person who writes …


CSS Vocabulary originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
This is a neat interactive page by Ville V. Vanninen to reference the names of things in the CSS syntax. I feel like the easy ones to remember are “selector,” “property,” and “value,” but even as a person who writes about CSS a lot, I forget some of the others. Like the property and value together (with the colon) is called a declaration. And all the declarations together, including the curly brackets (but not the selector)? That’s a declaration block, which is slightly more specific than a block, because a block might be inside an at-rule and thus contain other complete rule-sets.

To Shared LinkPermalink on CSS-Tricks


CSS Vocabulary originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
317807
A Use Case for a Parent Selector https://css-tricks.com/a-use-case-for-a-parent-selector/ https://css-tricks.com/a-use-case-for-a-parent-selector/#comments Tue, 31 Dec 2019 17:06:54 +0000 https://css-tricks.com/?p=300633 Having a “parent selector” in CSS is mentioned regularly as something CSS could really use. I feel like I’ve had that thought plenty of times myself, but then when I ask my brain for a use case, I find it …


A Use Case for a Parent Selector originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Having a “parent selector” in CSS is mentioned regularly as something CSS could really use. I feel like I’ve had that thought plenty of times myself, but then when I ask my brain for a use case, I find it hard to think of one. Well, I just had one so I thought I’d document it here.

A classic parent/child:

<div class="parent">
  <div class="child"></div>
</div>

Say it makes a lot of sense for this parent to have hidden overflow and also for the child to use absolute positioning.

.parent {
   overflow: hidden;
   position: relative;
}

.child {
   position: absolute; 
}

Now let’s say there’s one special circumstance where the child needs to be positioned outside the parent and still be visible. Hidden overflow is still a good default for the vast majority of situations, so it’s best to leave that rule in place, but in this very specific situation, we need to override that overflow.

.special-child {
   position: absolute; 
   bottom: -20px; /* needs to be slightly outside parent */
}

/* Not real, but just to make a point */
.special-child:parent(.parent) {
   overflow: visible;
}

That selector above is fake but it’s saying, “Select the parent of .special-child,” which would allow that override as needed. Maybe it’s like this:

.parent < .special-child {

}

…which is selecting the element on the left rather than the right. Who knows? Probably both of those are problematic somehow and the final syntax would be something else. Or maybe we’ll never get it. I have no idea. Just documenting a real use case I had.

You might be thinking, “Why not just use another special class on the parent?” I would have, but the parent was being injected by a third-party library through an API that did not offer to add a class of my choosing on it. Ultimately, I did have to add the class to the parent by writing some custom JavaScript that queried the DOM to find the .special-child, find the parent, then add the class there.

Do y’all have some other use-cases for a parent selector?


Here’s one from Uzair Hayat:

My project has an <input> which is wrapped in a<div>. The <div> has a few design elements which need to take effect once the <input> is in :focus. I could have used ::before and ::after pseudo-elements, but inputs do not support those as they are replaced elements. Right now, I juse JavaScript to detect if the input is in focus and apply a class to the parent div. I wish I could do…

input:focus:parent(div):after {
    display: block;
    /* display design changes when focused */
}

A Use Case for a Parent Selector originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/a-use-case-for-a-parent-selector/feed/ 19 300633
Could Grouping HTML Classes Make Them More Readable? https://css-tricks.com/could-grouping-html-classes-make-them-more-readable/ https://css-tricks.com/could-grouping-html-classes-make-them-more-readable/#comments Mon, 22 Apr 2019 19:45:46 +0000 http://css-tricks.com/?p=286565 You can have multiple classes on an HTML element:

<div class="module p-2"></div>

Nothing incorrect or invalid there at all. It has two classes. In CSS, both of these will apply:

.module { }
.p-2 { }
const div = document.querySelector("div");


Could Grouping HTML Classes Make Them More Readable? originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
You can have multiple classes on an HTML element:

<div class="module p-2"></div>

Nothing incorrect or invalid there at all. It has two classes. In CSS, both of these will apply:

.module { }
.p-2 { }
const div = document.querySelector("div");
console.log(div.classList.contains("module")); // true
console.log(div.classList.contains("p-3"));    // false

But what about grouping them? All we have here is a space-separated string. Maybe that’s fine. But maybe we can make things more clear!

Years ago, Harry Roberts talked about grouping them. He wrapped groups of classes in square brackets:

<div class="[ foo  foo--bar ]  [ baz  baz--foo ]">

The example class names above are totally abstract just to demonstrate the grouping. Imagine they are like primary names and variations as one group, then utility classes as another group:

<header class="[ site-header site-header-large ]  [ mb-10 p-15 ]">

Those square brackets? Meaningless. Those are there to visually represent the groups to us developers. Technically, they are also classes, so if some sadist wrote .[ {}, it would do stuff in your CSS. But that’s so unlikely that, hopefully, the clarity from the groups outweighs it and is more helpful.

That example above groups the primary name and a variation in one group and some example utility classes in another group.

I’m not necessarily recommending that approach. They are simply groups of classes that you might have.

Here’s the same style of grouping, with different groups:

<button class="[ link-button ] [ font-base text-xs color-primary ] [ js-trigger ]" type="button" hidden>

That example has a single primary name, utility classes with different naming styles, and a third group for JavaScript specific selectors.

Harry wound up shunning this approach a few years ago, saying that the look of it was just too weird for the variety of people and teams he worked with. It caused enough confusion that the benefits of grouped classes weren’t worth it. He suggested line breaks instead:

<div class="media  media--large
            testimonial  testimonial--main"> 

That seems similarly clear to me. The line breaks in HTML are totally fine. Plus, the browser will have no trouble with that and JSX is generally written with lots of line breaks in HTML anyway because of how much extra stuff is plopped onto elements in there, like event handlers and props.

Perhaps we combine the ideas of line breaks as separators and identified groups… with emojis!

See the Pen
Grouping Classes
by Chris Coyier (@chriscoyier)
on CodePen.

Weird, but fun. Emojis are totally valid there. Like the square brackets, they could also do things if someone wrote a class name for them, but that’s generally unlikely and something for a team to talk about.

Another thing I’ve seen used is data-* attributes for groups instead of classes, like…

<div 
  class="primary-name"
  data-js="js-hook-1 js-hook-2"
  data-utilities="padding-large"
>

You can still select and style based on attributes in both CSS and JavaScript, so it’s functional, though slightly less convenient because of the awkward selectors like [data-js="js-hook-1"] and lack of convenient APIs like classList.

How about you? Do you have any other clever ideas for class name groups?


Could Grouping HTML Classes Make Them More Readable? originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/could-grouping-html-classes-make-them-more-readable/feed/ 16 286565
CSS Selectors are Conditional Statements https://css-tricks.com/css-selectors-are-conditional-statements/ Thu, 06 Dec 2018 15:22:51 +0000 http://css-tricks.com/?p=279857 .foo { }

Programmatically, is:

if (element has a class name of "foo") {

}

Descendent selectors are && logic and commas are ||. It just gets more complicated from there, with things like combinators and pseudo selectors. Just


CSS Selectors are Conditional Statements originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
.foo { }

Programmatically, is:

if (element has a class name of "foo") {

}

Descendent selectors are && logic and commas are ||. It just gets more complicated from there, with things like combinators and pseudo selectors. Just look at all the ways styles can cascade.

Jeremy Keith:

If you find you can’t select something in the CSS, that’s a sign that you probably need to add another class name to the HTML. The complexity is confined to the markup in order to keep the CSS more straightforward, modular, and maintainable.

To Shared LinkPermalink on CSS-Tricks


CSS Selectors are Conditional Statements originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
279857
One Invalid Pseudo Selector Equals an Entire Ignored Selector https://css-tricks.com/one-invalid-pseudo-selector-equals-an-entire-ignored-selector/ https://css-tricks.com/one-invalid-pseudo-selector-equals-an-entire-ignored-selector/#comments Fri, 05 Oct 2018 13:45:01 +0000 http://css-tricks.com/?p=276762 Perhaps you know this one: if any part of a selector is invalid, it invalidates the whole selector. For example:

div, span::butt {
  background: red;
}

Even though div is a perfectly valid selector, span:butt is not, thus the entire …


One Invalid Pseudo Selector Equals an Entire Ignored Selector originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Perhaps you know this one: if any part of a selector is invalid, it invalidates the whole selector. For example:

div, span::butt {
  background: red;
}

Even though div is a perfectly valid selector, span:butt is not, thus the entire selector is invalidated — neither divs nor span::butt elements on the page will have a red background.

Normally that’s not a terribly huge problem. It may even be even useful, depending on the situation. But there are plenty of situations where it has kind of been a pain in the, uh, :butt.

Here’s a classic:

::selection {
  background: lightblue;
}

For a long time, Firefox didn’t understand that selector, and required a vendor prefix (::-moz-selection) to get the same effect. (This is no longer the case in Firefox 62+, but you take the point.)

In other words, this wasn’t possible:

/* would break for everyone */
::selection, ::-moz-selection {
  background: lightblue;
}

That would break for browsers that understood ::selection and break for Firefox that only understood ::-moz-selection. It made it ripe territory for a preprocessor @mixin, that’s for sure.

Here’s another zinger.

/* For navigation with submenus */
ul.submenu {
  display: none;
}

ul.menu li:hover ul.submenu,
ul.menu li:focus ul.submenu,
ul.menu li:focus-within ul.submenu {
  display: block;
}

/* Oh no! We've broken all menu functionality in IE 11, 
   because it doesn't know what `:focus-within` is so it
   throws out the entire selector */

This behavior is annoying enough that browsers have apparently fixed it going forward. In a conversation with Estelle Weyl, I learned that this is being changed. She wrote in the MDN docs:

Generally, if there is an invalid pseudo-element or pseudo-class within in a chain or group of selectors, the whole selector list is invalid. If a pseudo-element (but not pseudo-class) has a -webkit- prefix, As of Firefox 63, Blink, Webkit and Gecko browsers assume it is valid, not invalidating the selector list.

This isn’t for any selector; it’s specifically for pseudo-elements. That is, double colons (::).

Here’s a test:

I’d call that a positive change.


One Invalid Pseudo Selector Equals an Entire Ignored Selector originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/one-invalid-pseudo-selector-equals-an-entire-ignored-selector/feed/ 5 276762
Selectors That Depend on Layout https://css-tricks.com/selectors-that-depend-on-layout/ Tue, 02 Oct 2018 22:44:38 +0000 http://css-tricks.com/?p=277031 “Why the heck don’t we have ::first-column?”

I heard someone ask that the other day and it’s a valid question. I’d even take that question further by asking about ::nth-column() or whatever else relates to CSS columns. We …


Selectors That Depend on Layout originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
“Why the heck don’t we have ::first-column?”

I heard someone ask that the other day and it’s a valid question. I’d even take that question further by asking about ::nth-column() or whatever else relates to CSS columns. We have stuff like ::first-letter and ::first-line. Why not others?

There are many notable things missing from the “nth” crowd. Seven years ago, I wrote “A Call for ::nth-everything” and it included clear use cases like, perhaps, selecting the first two lines of a paragraph.

I don’t know all the technical details of it all, but I know there are some fairly decent reasons why we don’t have all of these in CSS. Part of it is the difficulty of getting it specced (e.g. words and characters get tricky across written languages) and part of it is the difficulty of implementing them. What I just found out is that there is a FAQ document that explains!

So, why don’t we have ::first-column? Because it’s a “selector that depends on layout”:

This falls into a class of problems that unlikely to be solvable in CSS: selectors in general, and pseudo classes in particular, cannot depend on layout, because otherwise they could be used to modify layout in a way that made them no longer match, which would modify the layout back to where it was, so they match again, and we get stuck in an infinite loop of contradictions.

For a simple example:

:stuck { position: static; }

Now what?

Some of the changes web developers might want to apply with a :stuck pseudo class may be safe and not trigger such loops, but selectors are a generic mechanism, and would enable this kind of contradictions.

So even though many of the problem people are trying to address using such pseudo classes are legitimate, selectors are unlikely to be the answer.

What we’ve got are infinite loops that are basically the problem (but read the FAQ — it goes into great detail about the nuance of it). In a related way, the same reason we don’t have element queries in CSS.

It’s a little tricky to think about because even stuff like ::first-line are riddled with paradoxes. Say you use it to increase the font-size. That means fewer characters fit on the line, so the characters that originally matched are now pushed down and don’t match anymore. Seems a bit loopy, but that’s been sorted out. Plus, classics like :hover potentially causing jitter. The document talks about these things in clear terms. It’s not all cut and dry!

The whole FAQ is a fascinating read and covers much more than this situation.

To Shared LinkPermalink on CSS-Tricks


Selectors That Depend on Layout originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
277031
“Stop Using CSS Selectors for Non-CSS” https://css-tricks.com/stop-using-css-selectors-non-css/ https://css-tricks.com/stop-using-css-selectors-non-css/#comments Tue, 16 Jan 2018 22:14:30 +0000 http://css-tricks.com/?p=265461 I saw Nicole Dominguez tweet this the other day:

I wasn’t at this conference, so I have very little context. Normally, I’d consider it …


“Stop Using CSS Selectors for Non-CSS” originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
I saw Nicole Dominguez tweet this the other day:

I wasn’t at this conference, so I have very little context. Normally, I’d consider it a sin to weigh in on a subject brought up by looking at two out-of-context slides, but I’m only weighing in out of interest and to continue the conversation.

The idea seems to be that if you need to select an element in the DOM with JavaScript, don’t use the same selector as you would in CSS.

So if you have…

<article class="article">
</article>

…and you need to apply an event listener to that article for some reason, then don’t use…

$(".article")

(or querySelector or whatever, I assume.)

Instead, apply an attribute intended just for the JavaScript to target, like…

<article class="article" data-hoverable>
</article>

…and target that like…

$("[data-hoverable]")

The idea is that you can separate jobs. The class has the job of styling, and the data attribute has the job of JavaScripting. Both can change without affecting each other.

Seems reasonable to me.

Also seems like there is plenty to talk about here. Performance, I suppose, but that’s probably the least-interesting thing since selectors are generally pretty damn fast these days. We could continue the conversation by talking about:

  • What naming convention?
  • Should you be naming events?
  • What if it needs to be selected for different reasons multiple times?
  • Can you or should you use IDs?
  • Is it worth avoiding DOM selection at all if you can?
  • What other nuances are part of this discussion?

I saw Michael Scharnagl had some thoughts on his own usage of ID’s, classes, and data-attributes that could help frame things a bit.


“Stop Using CSS Selectors for Non-CSS” originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/stop-using-css-selectors-non-css/feed/ 40 265461
Use a Sass Variable for a Selector https://css-tricks.com/snippets/sass/use-sass-variable-selector/ https://css-tricks.com/snippets/sass/use-sass-variable-selector/#comments Mon, 02 Jan 2017 20:51:51 +0000 http://css-tricks.com/?page_id=249595 Say you need to use a certain selector in multiple places in your code. It’s not tremendously common, to be sure, but stuff happens. Repeated code is typically an opportunity for abstraction. Abstracting values in Sass is easy, but selectors …


Use a Sass Variable for a Selector originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Say you need to use a certain selector in multiple places in your code. It’s not tremendously common, to be sure, but stuff happens. Repeated code is typically an opportunity for abstraction. Abstracting values in Sass is easy, but selectors is slightly trickier.

One way to do it is to create your selector as a variable. Here’s an example that is a comma separated list of selectors:

$selectors: "
  .module,
  body.alternate .module
";

Then to use that, you have to interpolate the variable, like this:

#{$selectors} {
  background: red;
}

That works with nesting too:

.nested {
  #{$selectors} {
    background: red;
  }
}

Prefixing

A variable can also be only a part of a selector, like a prefix.

$prefix: css-tricks-;

.#{$prefix}button {
  padding: 0.5rem;
}

You could use nesting to do prefixing as well:

.#{$prefix} {
  &module {
    padding: 1rem;
  } 
  &header {
    font-size: 110%;
  }
  &footer {
    font-size: 90%;
  }
}

Selectors in a Map

Perhaps your abstraction lends itself to a key/value pair situation. That’s a map in Sass.

$selectorMap: (
  selectorsFoo: ".module",
  selectorsBar: ".moodule-2"
);

You can use them individually like:

#{map-get($selectorMap, selectorsFoo)} {
  padding: 1rem;
}

Or loop through them:

@each $selectorName, $actualSelector in $selectorMap {
  #{$actualSelector} {
    padding: 1rem;
  }
}

Examples

See the Pen Sass Variables for Selectors by Chris Coyier (@chriscoyier) on CodePen.


Use a Sass Variable for a Selector originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/snippets/sass/use-sass-variable-selector/feed/ 8 249595
inStyle (Modifying the Current Selector `&` in Sass) https://css-tricks.com/instyle-current-selector-sass/ https://css-tricks.com/instyle-current-selector-sass/#comments Fri, 27 May 2016 13:06:17 +0000 http://css-tricks.com/?p=242174 The following is a guest post by Filip Naumovic from Salsita Software. Filip has built a Sass tool to help with an issue I know I’ve experienced many times. You’re happily nesting in Sass. You’re maybe a level or


inStyle (Modifying the Current Selector `&` in Sass) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The following is a guest post by Filip Naumovic from Salsita Software. Filip has built a Sass tool to help with an issue I know I’ve experienced many times. You’re happily nesting in Sass. You’re maybe a level or two deep, and you need to style a variation based on some parent selector. You need to either break out of the nesting and start a new nesting context, or go nuclear with @at-root. I’ll let Filip tell the story of his new tool that changes that.

Sass has helped a lot of us tremendously with cleaning up our CSS code bases. When it arrived on the scene, some extremely useful patterns emerged and got quickly adopted.

For example, the usage of &amp; and nesting allowed for the cleaning up of otherwise rather gross code blocks.

This:

.my-app { display: block; }
.my-app .widget { border-radius: 5px; }
.my-app .widget.blue { color: blue; }
.isIE6 .my-app .widget { background-image: url('fake-borders.png'); }
@media (max-width: 768px) { .my-app .widget { float: left; } }

Turned into this:

.my-app {
  display: block;
  .widget {
    border-radius: 5px;
    &amp;.blue {
      color: blue;
    }
    .isIE6 &amp; {
      background-image: url("fake-borders.png");
    }
    @media (max-width: 768px) {
      float: left;
    }
  }
}

What a positive change for our sanity!

All style variations of the .widget element are clearly nested below itself, using indentation as both a visual cue for relevancy and a query generator.

The current selector (&amp;), in this case, provides a shortcut for the most common patterns. Styling the variation of an element that is invoked either by a property of the element itself or a prepending parent state.

Nested media queries are a younger addition, but they hint that evolution towards indented syntax for styles comes almost naturally. It’s easy to read and navigate, because it somehow mirrors the familiar DOM structure and keeps all styles for an element in one place, while still producing our precious, yet sometimes complicated, selectors.

Today, nesting and current selector features are present in Less, Sass, and Stylus. With a bit of wine, one could almost call it a standard.

A classic case of “You can’t do that.”

Using the above code block as an example, let’s add styles for .my-app.expanded .widget.

Despite our mighty tools, we quickly find ourselves with limited choices:

Option 1

Using the modern @at-root directive (or / in Stylus), we leave the current scope entirely and repeat the full root query to keep the relevant new styles nested below .widget, because the current selector can’t help us express this relationship.

.my-app {
  display: block;
  .widget {
    border-radius: 5px;
    &amp;.blue {
      color: blue;
    }
    .isIE6 &amp; {
      background-image: url("fake-borders.png");
    }
    // repeating the root selector here
    @at-root .my-app.expanded .widget {
      color: red'
    }
    @media (max-width: 768px) {
      float: left;
    }
  }
}

This creates harder to read code with a lot of duplicity, especially when real world usage extends way over our small example piece. But, it keeps our glorious nesting paradigm intact.

Option 2

We create a new code block below .my-app and use it to change all child elements relevant to the .expanded state. This means that our .widget is now styled in different places, and this separation grows for every added state in each element in the nest.

.my-app {
  display: block;
  .widget {
    border-radius: 5px;
    &amp;.blue {
      color: blue;
    }
    .isIE6 &amp; {
      background-image: url("fake-borders.png");
    }
    @media (max-width: 768px) {
      float: left;
    }
  }
  &amp;.expanded .widget
     color: red;
  }
}

While it is in direct violation of our “nesting all relevant styles” dream, it’s the imperfection we learned to live with. Many of you would probably even defend this pattern, because it has been the way things are done for quite a while.

However, for the sake of choice, wouldn’t it be great to have an Option 3? One that would allow us to express the simple change in .my-app.expanded that influences our .widget without having to escape the context?

This idea has been secretly bothering me for quite a while, if only out of some form of OCD about my own stylesheets. I’ve made it my sidequest to try and find this missing tool in the style shed.

Finding Option 3

While digging around the topic, I’ve found spider webs, eternal discussions, and wildly varying propositions, many of which suggested adding some special syntax to the current selector character &amp;. Doing that would mean months of learning complicated core libraries and fighting the long war, which instantly felt like an unacceptable burden.

Secondly, I think &amp; works well because it’s a clear representation of the whole context, and for that reason it might be problematic adding more features to it. It does one thing and does it well, so creating a good partner to it seemed like a better idea at this time.

For sake of easy integration, I’ve decided to implement the idea on the level of preprocessor language, so you could just @import and use it right away. Preprocessors are powerful frameworks nowadays, so why not?

My first choice was Stylus, because it’s just so awesome. Unfortunately, due to issue 1703, the current selector placeholder cannot be modified inside a mixin function as of right now. Like a good zealot I’ll wait until the end of time for Stylus to fix it, but I had to keep searching for something I could implement now.

You shall not parse the current selector, as I’ve learned, in Less, so that was out.

SassScript on the other hand proved to be a powerhouse. While it is missing many useful abstractions for manipulation with strings and arrays, it is very possible to craft such functions manually. Many of them are already provided by Sass Prince Kitty Giraudel.

After months of controlled string terror…

inStyle for Sass 3.4+ is born!

Cheesy name, I know. But it’s suggestive of the functionality, because you want this thing readable in the actual code. Mixin syntax is already familiar with preprocessor users, so having a suggestive name for describing changes in element parents sounded right to me as an added bumper against unfamiliarity.

Either way, all of it has to stay readable while handling complex cases, otherwise it loses purpose in favor of @at-root selector approaches or just nesting the code elsewhere. I decided to go with two basic mechanisms that I believe address even the most despicable needs, while keeping a logically simple parsing algorithm:

Use 1) Modification

Additions to a compound element present in the current selector proved to handle ~80% of real world code, just like our first example tries to achieve.

.my-app {
  display: block;
  .widget {
    border-radius: 5px;
    &amp;.blue {
      color: blue;
    }
    .isIE6 &amp; {
      background-image: url("fake-borders.png");
    }
    @include in(".my-app.expanded") {
      color: red; // .my-app.expanded .widget { };
    }
    @media (max-width: 768px) {
      float: left;
    }
  }
}

Try to read that like this:

styling .widget in the .my-app.expanded state.

The function searches the nest bottom to top for the first occurrence of .my-app element (skipping current element) and appends the class .expanded to it, returning a new selector.

What about longer queries and combo modifications?

table {
  table-layout: fixed;
  thead {
    font-weight: normal;
    tr {
      height: 30px;
      td {
        background-color: #fafafa;
        &amp;.user {
          font-weight: bold'
        }
        @include in('table.one tr.two:hover') {
          background-image: url(rainbow.png) // table.one thead tr.two:hover td { };
        }
      }
    }
  }
}

The tr parent is found and modified with .two:hover. Going upwards, table is also found and modified with .one, other elements are skipped.

Irrelevant multi-selectors are removed from the new selector:

ul, ol {
  list-style: none;
  li {
    display: inline-block;
    a {
      text-decoration: underline;
      @include in("ol.links") {
        color: orange; // ol.links li a { };
      }
    }
  }
}

Impossible cases and invalid CSS queries produce a blocking Sass error on compilation:

table {
  table-layout: fixed;
  td {
    height: 30px;
    @include in("table^s&amp;()#") {
      what: the; // ERROR, invalid CSS
    }
    @include in ("tr.green:hover") {
      border-color: green; // ERROR, no tr or tr.green to modify in &amp;
    }
  }
}

While crash testing this in production (hah!), I found another very practical need that I couldn’t satisfy with modifications of the parent tree only. In fact, it solves the example above, because you have to be able to do just that with tr.green:hover. You just have to be able to say where.

Use 2) Insertion

Let’s assume the following:

table {
  table-layout: fixed;
  thead {
    font-weight: normal;
  }
  tr {
    height: 30px;
  }
  td {
    background-color: #fafafa;
  }
}

Where would you ideally nest a table thead tr selector? By the dogma, you seemingly have to add it as follows:

table {
  table-layout: fixed;
  thead {
    font-weight: normal;
    tr {
      height: 50px;
    }
  tr {
    height: 30px;
  }
  td {
    background-color: #fafafa;
  }
}

However, the styled element in question is tr and you already have that as a generic style, so theoretically, nesting it below itself as a variant might be closer to how you think about the relationship, filling the gaps that current selector &amp; cannot describe.

In this case, it means there has to be a simple way to insert some selector at a certain position above the current element while also allowing combinations with compound modifications. I couldn’t imagine this without adding a special character, so I went with the visually suggestive ^ caret.

table {
  table-layout: fixed;
  thead {
    font-weight: normal;
  }
  tr {
    height: 30px;
    @include in("^thead") {
      height: 50px; // table thead tr { };
    }
  }
  td {
    background-color: #fafafa;
    @include in("table.blue-skin ^tbody") {
      background-color: blue; // table.blue-skin tbody td { };
    }
  }
}

In this case, the caret is inserting thead one level above current or last modified element. More carets mean higher jumps in the current selector:

main {
  display: flex;
  &gt; div {
    flex-grow: 1;
    span {
      display: inline-block;
      &amp;.highlight {
        outline-style: dashed;
        @include in("^^.section.active") {
          outline-style: solid; // main .section.active &gt; div span.highlight { };
        }
        @include in("^^^.section") {
          some: thing; // ERROR, inserting too high, it would equal to ".section &amp;"
        }
      }
    }
  }
}

Note: &amp;.highlight is the same element as span, so the insertion treats it as one step in the nest

I think inStyle shines in the simplest cases, which are also by far the most common. But things can get more complex if needed.

Use 3) Advanced combinations

The matching algorithm allows you to go even wilder with inserting in or modifying more compounds at once:

.my-app {
  display: flex;
  section {
    flex: 1;    
    header {
      width: 100%;
      @include in("^^.wrapper ^.dialog)") {
        height: 50px; // .my-app .wrapper section .dialog header { };
      }
      @include in("^.overlay ^.sidebar") {
        position: fixed; // .my-app section .overlay .sidebar header { };
      }
      @include in("^.modifier section.next ^.parent") {
        opacity: 0; // .my-app .modifier section.next .parent header { };
      }
    }
  }
}
  1. .dialog is inserted one level above header and .wrapper is inserted two levels.
  2. .sidebar is inserted above header and .overlay directly above it.
  3. Pushes .parent above header, modifies section with .next and then pushes .modifier above it.

This reminds me, perhaps you have some feedback! I’ve been thinking about enabling some simpler syntax when you want to insert more compound elements directly after each other as in the second case, perhaps something like @include in("^(.overlay .sidebar)") or improve the parser and enable more natural @include in("^.overlay .sidebar"). Let me know your opinion!

After using it for a while, I’ve found that most of my inconvenient code patterns are solved rather easily by just changing one element here and there or pushing a new selector at a certain position and keep things in place. Still, I need to be honest, it is potentially quite invasive to your usual code organization by nature of the idea.

I can see how using inStyle might bring on heated arguments. My colleagues seem to be either open minded or don’t care, which is both great.

If you use it, I would hope that the correct handling would be like with any other tool: when it’s fit for the job. Spamming complex nested mixins will unlikely score high on readability than flat out writing the full query, but on the other hand it can simplify most real world problems while keeping a slim footprint.

In the near future, I’d like to get the Stylus port working and perhaps create an Atom editor plugin to display the resulting query as a hint in the code.

It was fun taking a shot at solving the first-world problems of CSS and it is my hope that you consider the subject at least worthy of a discussion. The project is open source, so feel free to get onboard with either code or feedback!

Love it or hate it, here it is on GitHub, here’s a little microsite and here’s a live debugger for good measure.

See the Pen inStyle Crash Test Dummy by Salsita Software (@salsita) on CodePen.

Thanks for reading!


inStyle (Modifying the Current Selector `&` in Sass) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/instyle-current-selector-sass/feed/ 36 242174
Strategies for Keeping CSS Specificity Low https://css-tricks.com/strategies-keeping-css-specificity-low/ https://css-tricks.com/strategies-keeping-css-specificity-low/#comments Mon, 12 Jan 2015 15:45:20 +0000 http://css-tricks.com/?p=192577 Keeping CSS specificity low across all the selectors in your project is a worthy goal. It’s generally a sign that things are in relative harmony. You aren’t fighting against yourself and you have plenty of room to override styles when …


Strategies for Keeping CSS Specificity Low originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Keeping CSS specificity low across all the selectors in your project is a worthy goal. It’s generally a sign that things are in relative harmony. You aren’t fighting against yourself and you have plenty of room to override styles when you need to. Specificity on selectors tends to creep up over time, and there is a hard ceiling to that. I’m sure we’ve all felt the pain of !important tags and inline styles.

So how do we keep that specificity low over time?

Give yourself the class you need

Perhaps you’re writing a high-specificity selector because you’re overriding an already-existing selector. Maybe the element you’re trying to select is included on multiple pages, so you select it from a higher-up class:

.section-header {
  /* normal styles */
}

body.about-page .section-header {
  /* override with higher specificity */
}

However you feel about this, recognize that that specificity is creeping up here. To prevent this, is there a way you can alter the class name on that element you’re trying to style directly instead? Sometimes creating some server side helper functions or variables for emitting classes can be helpful, to avoid logic in views.

<header class="<%= header_class %>">
  Which could output one class, or both, as desired.
</header>
.section-header {
  /* normal styles */
}

.about-section-header {
  /* override with same specificity */
  /* possibly extend the standard class */
}

Consider the source-order of the stylesheets

Along that same trying-to-override-something vein, perhaps you are applying an additional class name to handle a style override for you.

<header class="section-header section-header-about">
  
</header>

But where you are writing your override styles with .section-header-about are actually getting overridden by the existing class. That can happen because of selector order in the stylesheet. Both selectors have the exact same specificity, so the rules from whichever one is declared last win.

Fixing that means just ensuring that where ever you write your override class comes later. Perhaps organize your stylesheets something like:

@import "third-party-and-libs-and-stuff";

@import "global-stuff";

@import "section-stuff";

@import "specific-things-and-potential-overrides";

Reduce the specificity of the thing you’re overriding

You know what they say about leaving things better than you found them. You should do that.

If there is an ID on an element and that’s what it’s being styled by, can you remove it? Definitely do a project-wide search for #that before you do it. It might be being used as a JS hook (perfectly fine) in which case you should leave it alone and either add a class or use a class already on it for the CSS.

Don’t avoid classes to begin with

I’ve been known to use a selector like:

.module > h2 {

}

That’ll work fine, until they day you want a special style for that h2 element. You might go in your your markup and be like:

<div class="module">
  <h2 class="unique">
    Special Header
  </h2>
</div>

But sadly:

.module > h2 {
  /* normal styles */
}
.unique {
  /* I'm going to lose this specificity battle */
}
.module .unique {
  /* This will work, but specificity creep! */
}

The specificity creep is happening because the original selector is biting us. That’s why almost all CSS methologies recommend flat structures in the vein of:

<div class="module">
  <h2 class="module-header">
  </h2>
  <div class="module-content">
  </div>
</div>

It can feel like more tedious work up-front because you might not need those classes right away. But by not avoiding that work (don’t be lazy!), the hooks you’ll have later can save your grief.

Use the cascade

As a project ages, it becomes more and more dangerous to alter selectors with low specificity, because they potentially can affect more things and have unintended consequences.

#im #just .gonna[do] .this .to .be #safe {
  /* cries (ಥ_ʖಥ) */
}

But affecting more things is the power of CSS. Having a solid base you’re building from hopefully means less overrides are ever necessary. The strategies for this can be things like…

Use a pattern library and/or style guide and/or atomic design

A pattern library (something like this) can mean you have what you are looking for when you need it. Need some tabs? Here you go, this is the established way for doing that. And it’s likely built in such a way the specificity is already light, so overriding won’t be overly difficult.

Atomic design (book) can guide how your site (or the pattern library) is built, so even if you don’t have a full pattern for what you need, you have the building blocks below it.

A style guide might save you, because it might enforce specific rules about specificity, in an attempt to save you from yourself.

Consider opt-in typography

At the same time you’re trying to use the cascade and have smart defaults for lots of elements, you might want to scope some of those smart defaults sometimes. For instance, a list element is often used for navigation and within content. The styles for them will be drastically different. So why not start with a clean slate and apply text styling only when needed.

/* reset styles globally */
ul, ol, li {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* scope text styles to text content */
.text-content {
  h1, h2, h3 {
  }
  p {
  }
  ul {
  }
  ol {
  }
  /* etc */
}

That does increase the specificity of those text selectors, but it means that rules specifically for text aren’t affecting more than they need to be and there is less need for overrides.

Outside of your control issues

Perhaps some third party code expects or injects certain HTML onto your page. You have to work with that. You can still try and use as low specificity selectors as you can. You can leave comments in the code indicating why the selectors are like this. You can use low specificity selectors, but use !important overrides. You can ask the people responsible for the non-ideal code if they can fix it.

Only up the specificity lightly, and note it

If you need to get into a specificity fight, rather than reaching for a sledgehammer like an ID or !important, trying something lighter.

A tag-qualifier is the minimum possible specificity upgrade.

ul.override {
  /* I win */  
}
.normal {
}

But limiting a selector to a specific tag is a weird limiting factor. Might be smart to just add an additional class instead. If another name doesn’t make sense, you can even use the same class if needed.

.nav.override {
}
.override.override {
}
.nav {
}

Just because nesting is nice, it doesn’t mean specificity has to go up

Nesting in preprocessors is sometimes discouraged because it makes it so easy to write overly selectors. But nesting is sometimes just visually nice in the stylesheet, because it groups things together making it easier to read and digest.

Bohdan Kokotko reminded me recently the ampersand in Sass can be used to essentially do namespacing rather than compound selectors.

.what {
    color: red;
   &-so {
      color: green;
      &-ever {
        color: blue;
      }
   }
}
.what {
  color: red;
}
.what-so {
  color: green;
}
.what-so-ever {
  color: blue;
}

The single-class wrapper

A rather forceful way to handle an override is use an existing wrapping element over the area you need to apply style overrides to, or add a new one.

<div class="override">

   ... existing stuff ...

</div>

You now have the ability to override any existing style in there by only adding a single class to the selector. It’s specificity creep, but an attempt at keeping it low.

.override .existing {
}

Only once

If you’re ever overriding an override, that’s a good place to stop and re-consider.

Can you give yourself just a single class that you can use instead? A single override can actually be efficient, like a variation on a pattern instead of creating a new pattern. An override of an override is a recipe for confusion and further fighting.

Just do better next time

If you’ve been bitten by specificity problems, even if fixing them is impractical right now, at least you know now that it’s problematic and can approach things differently next time.


Strategies for Keeping CSS Specificity Low originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/strategies-keeping-css-specificity-low/feed/ 40 192577
Meet the Pseudo Class Selectors https://css-tricks.com/pseudo-class-selectors/ https://css-tricks.com/pseudo-class-selectors/#comments Wed, 17 Mar 2010 11:55:23 +0000 http://css-tricks.com/?p=5762 Pseudo class selectors are CSS selectors with a colon preceding them. You are probably very familiar with a few of them. Like hover:

a:hover {
  /* Yep, hover is a pseudo class */
}

They are immensely useful in a …


Meet the Pseudo Class Selectors originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Pseudo class selectors are CSS selectors with a colon preceding them. You are probably very familiar with a few of them. Like hover:

a:hover {
  /* Yep, hover is a pseudo class */
}

They are immensely useful in a variety of situations. Some of them are CSS3, some CSS2… it depends on each particular one. Outside of IE, they have great browser support. In IE land, even IE8, support is pretty barren. However, the IE9 preview has full support for them. The link-related ones work but not much else. Let’s take a brief look at each one of them. Don’t worry, there aren’t that many.

:link – Perhaps the most confusion-causing link-related pseudo-selector. Aren’t all <a> links? Well not if they don’t have an href attribute. This selects only those that do, thus is essentially the same as a[href]. This selector will become a lot more useful should any-element linking become reality.

:visited – Selects links that have already been visited by the current browser.

:hover – When the mouse cursor rolls over a link, that link is in its hover state and this will select it.

:active – Selects the link while it is being activated (being clicked on or otherwise activated). For example, for the “pressed” state of a button-style link or to make all links feel more button-like.

There is a fun technique to remember all the link pseudo-class selectors. Look at the first letter of each: LVHA … LOVE HATE.

:focus – Defining hover styles for links is great, but it doesn’t help out those who used keyboard navigation to get to the link. :focus will select links that are the current focus of the keyboard. This is not limited to links, but can be used (and really should be used) on inputs and textareas as well. Some would tell you to define a :focus style for anything that has a :hover style.

Form with a text input in focus. Yellow background is a focus style.
Form with a text input in focus. Yellow background is a focus style.

:target – The target pseudo class is used in conjunction with IDs, and match when the hash tag in the current URL matches that ID. So if you are at URL www.yoursite.com/#home then the selector #home:target will match. That can be extremely powerful. For example, you can create a tabbed area where the tabs link to hash tags and then the panels “activate” by matching :target selectors and (for example) using z-index to move to the top.

:enabled – Selects inputs that are in the default state of enabled and ready to be used.

:disabled – Selects inputs that have the disabled attribute. A lot of browsers will make the input a faded out gray, you can control that with this selector.

Form using the :disabled attribute.

:checked – Selects checkboxes that are, wait for it, checked.

:indeterminate – Selects radio buttons that are in the purgatory state of neither chosen or unchosen (like when a page loads with radio button choices but no default is set).

Set of radio buttons in purgatory. Or more accurately, in their :indeterminate status.

:required – Selects inputs with the required attribute.
:optional – Selects inputs that do not have the required attribute.

:read-only / :read-write – Selects elements based on a combination of readonly and disabled attriutes.

Position/Number-based pseudo class selectors

:root – Selects the element that is at the root of the document. Almost certainly will select the element, unless you are specifically working in some weird environment that somehow also allows CSS. Perhaps XML.

:first-child – Selects the first element within a parent.

:last-child – Selects the last element within a parent.

:nth-child() – Selects elements based on a simple provided algebraic expression (e.g. “2n” or “4n-1”). Has the ability to do things like select even/odd elements, “every third”, “the first five”, and things like that. Covered in more detail here with a tester tool.

:nth-of-type() – Works like :nth-child, but used in places where the elements at the same level are of different types. Like if inside a div you had a number of paragraphs and a number of images. You wanted to select all the odd images. :nth-child won’t work there, you’d use div img:nth-of-type(odd). Particularly useful when working with definition lists and their alternating <dt> and <dd> elements.

:first-of-type – Selects the first element of this type within any parent. So if you have two divs, each had within it a paragraph, image, paragraph, image. Then div img:first-of-type would select the first image inside the first div and the first image inside the second div.

:last-of-type – Same as above, only would select the last image inside the first div and the last image inside the second div.

:nth-last-of-type() – Works like :nth-of-type, but it counts up from the bottom instead of the top.

:nth-last-child() – Works like :nth-child, but it counts up from the bottom instead of the top.

:only-of-type – Selects only if the element is the only one of its kind within the current parent.

Position/Number-based pseudo class selectors

Relational pseudo class selectors

:not() – Removes elements from an existing matched set that match the selector inside the parameter of :not(). So for example, all divs except those with a class of “music” = div:not(.music). The spec says that :not selectors cannot be nested, but they can be chained. Some browsers (Firefox) also support comma-separated selectors as the selector parameter, although chaining them would be a far safter bet. Also useful in conjunction with attribute selectors, e.g. input:not([disabled]).

:empty – Selects elements which contain no text and no child elements. Like:

::first-letter – Selects the first letter of the text in the element. Typical use: drop caps.

::first-line – Selects the first line of text in the element. Typical use: setting the first sentence in small caps as a typographical eye-catcher/lead-in.

:lang – This pseudo selector is in the CSS3 spec but is only implemented in IE 8+. Will match anything that either has or is a descendant of an element with a matching lang attribute. For example, :lang(fr) will match any paragraph, even without a lang attribute, if the parent body had lang="fr" as an attribute.

Quick note

You can chain pseudo selectors just like you can chain class and ID selectors. This is particularly useful here while we are looking at :first-letter and :first-line. You probably wouldn’t want to drop cap every single paragraph on the page, but just the first one, so, p:first-child:first-letter { }

Dropcap using :first-letter, which enlarges the font size and floats to the left.

::before – Is able to add content before a certain element. For example, adding an opening quote before a blockquote or perhaps an preceding image to set apart a particular paragraph.

::after – Is able to add content after a certain element. For example, a closing quote to a blockquote. Also used commonly for the clearfix, where an empty space is added after the element which clears the float without any need for extra HTML markup.

Pseudo Elements vs Pseudo Classes

The above two selectors are appropriately called pseudo “elements” (not selectors) because they don’t select any “real” element that exists on the page. This goes for these two, as well as the previous sections ::first-letter and ::first-line. Make sense? Like the first letter that ::first-letter selects isn’t an element all to itself, it’s just a part of an existing element, hence, pseudo element.

Selector StyleNameDoesSpecificity
::pseudo elementselects/creates some actual content0 0 0 1
:pseudo classselects elements in certain conditions0 0 1 0

Tag Qualification

These selectors can be tag-qualified, meaning they will only apply if both the element (tag) and selector match. For instance:

p:first-child {
  color: red;
}

That will only match if the first child of another element is a <p>. If it’s not, it won’t match.

Deprecated

:contains() – As far as I know, this is gone. The current CSS3 spec has removed it. I don’t know the story, let me know if you do. At a glance, it looks ridiculously useful (being able to select objects based on the textual content they contain). It may be because of problems, or having content in selectors being undesirable. My preference would be to have it select by elements rather than text, like p:contains(img), but alas, no such luck.

Update: There is a :has() selector now!

::selection – Allows the changing of style of selected text. It was drafted for CSS Selectors Level 3 but removed before it reached the Recommendation status. Despite this, it’s implemented in some browsers, which will probably retain experimental support for it. For Firefox, you can use ::-moz-selection. More information here.

Update: ::selection is totally standard now, go ahead and use it!

jQuery Usage

jQuery can use all of these in its selectors, which is awesome. Even awesomer, jQuery has additional pseudo class selectors available.

:first – Matches the first instance of the already matched set. This is different than :nth-child(1) which will only select if the selector matches and it’s the first child. With :first, the selector matches, then it takes the first one it finds regardless of child position.

:eq()jQuery doesn’t support :nth-of-type as a part of it’s selector engine, but this is very similar.It does now. This selects the Xth element from the already-matched set. It’s also zero-indexed (0 is the first element) unlike :nth-child in which the first element is 1.

:contains('text') – This is removed from CSS, but it works in jQuery.

:lt() – The same as :nth-child(-n+X), as in it selects the “first X elements”

:gt() – The same as :nth-child(n+X), as in it selects everything except the “first (X-1) elements”

:even – The same as :nth-child(even) or :nth-child(2n)

:odd – The same as :nth-child(odd) or :nth-child(2n+1)

:has() – Tests if the element has a descendant of a certain selector before matching, like :has("div.intro").

There are actually a whole bunch more, and all of them are clever and useful (or at least an improvement on readability) See the selector documentation for more.

jQuery can’t really help you with pseudo elements like ::before and ::after, but you can access their values in some browsers. E.g if a div had some ::before generated content, you could get the value like:

var div = document.querySelector("div");

var content = window
  .getComputedStyle(div, '::before')
  .getPropertyValue('content');

Specificity

Class selectors and pseudo class selectors have the same specificity weight. Pseudo elements have the specificity of an element selector.

li             {} /* specificity = 0,0,0,1 */
li:first-child {} /* specificity = 0,0,1,1 */
li:first-line  {} /* specificity = 0,0,0,2 */
li.red         {} /* specificity = 0,0,1,1 */

Typically they are used in conjunction or listed afterwards in CSS anyway, so hopefully it won’t cause too many problems…

ul li.friend { margin: 0 0 5px 0; }
ul li:last-child { margin: 0; }

In that case the zeroing out of the margin would work (assuming it matched the same element), but only because the zeroing out is listed second (they have the same specificity). So… watch for that.


Meet the Pseudo Class Selectors originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/pseudo-class-selectors/feed/ 69 5762