Friday, April 26, 2024
HomeCSSConditional CSS with :has and :nth-last-child

Conditional CSS with :has and :nth-last-child


We are able to test with CSS to know if the variety of a bunch factor is lower than or equal to a quantity. For instance, having a grid with three or extra objects. You is likely to be questioning, why is that even wanted. In some instances, a part or a structure would possibly change based mostly on the variety of youngster components.

This has been in CSS for years, nevertheless it turning into extra highly effective now with CSS :has. We are able to mix each the nth-last-child selector together with :has to do magic, sure! You heard that proper.

Earlier this 12 months, I revealed a submit titled Conditional CSS the place I confirmed how some CSS options assist us to create conditional UIs. On this article, I’ll spotlight a number of examples of the place we will mix a CSS selector with :has to have a conditional part/structure states.

Introduction to :nth-last-child

One of many principal elements on this article is the :nth-last-child pseudo-class. We are able to use that selector to mock counting youngster components.

Let’s discover the way it works. I’ll strive my finest to elucidate the way it works with plain phrases.

Contemplate the next determine:

Now we have an inventory of 5 playing cards. I’ll use that for example to show what we will do with :nth-last-child.

Within the following CSS, we’ve n + 3 which suggests:

li:nth-last-child(n + 3) {
    /* kinds */
}

Choose the primary three objects from the tip, counting from the third merchandise.

Let’s take a more in-depth look. First, we have to depend 3 objects from the tip. With that, the third merchandise is definitely the primary merchandise that we’ll depend until the tip of the listing.

After we depend from the third merchandise until the tip, listed here are the chosen objects:

Amount queries limitations in CSS

As defined in this nice article by Heydon Pickering, we will use the :nth-last-child as a amount question.

Contemplate the next determine:

Now we have an inventory of data that’s displayed otherwise when we’ve 5 or extra objects.

<ul>
   <li></li>
   <li></li>
   <li></li>
   <!-- extra objects -->
</ul>
li {
    /* default kinds */
}

/* If the listing has 5 or extra objects */
li:nth-last-child(n + 5),
li:nth-last-child(n + 5) ~ li {
  width: 50%;
  show: inline-block;
  border-bottom: 0;
}

Whereas that works, it’s nonetheless a bit limiting in some methods.

It’s not attainable to fashion the mum or dad based mostly on the variety of components.

Think about that we have to add show: flex to every <li> when there are 5 or extra objects. We are able to’t do this with the :nth-last-child pseudo-class selector.

The reason being that including show: flex will pressure every merchandise to remain in its personal row, which doesn’t align with the design to realize.

li:nth-last-child(n + 5),
li:nth-last-child(n + 5) ~ li {
  width: 50%;
  show: flex;
  flex-direciton: column;
}

We are able to repair that with show: inline-flex as a substitute, nevertheless it’s nonetheless not the optimum answer for me. The reason being that the browser will account for the spacing between the HTML components, they need to be like that:

<ul>
   <li></li><li></li><li></li>
   <!-- extra objects -->
</ul>

If we don’t do this, show: inline-flex may have the identical impact as show: flex. One hack to repair that’s to cut back the width by 1%.

li:nth-last-child(n + 5),
li:nth-last-child(n + 5) ~ li {
  width: 49%;
  show: flex;
  flex-direciton: column;
}

Making them work on totally different viewport sizes

With out the power to have management over the mum or dad, it’s not that easy to fashion the structure of the itemizing. For instance, when the container or viewport width is smaller, we have to present 1 merchandise per row.

Extra work to handle the spacing

When there are 3 objects or fewer, the spacing is horizontal, and when it’s 5 or extra, the spacing is vertical. We are able to handle that manually by flipping the margin from horizontal to vertical, or by utilizing CSS hole with Flexbox. However once more, we’re compelled to make use of inline-flex for that case.

The CSS :nth-last-child pseudo-class is the important thing to constructing conditional layouts. By combining it with the CSS :has selector, we will test if a mum or dad factor has a minimum of a particular variety of objects and elegance it accordingly. The probabilities are infinite!

Use instances and examples

Grid that modifications based mostly on the variety of youngster objects

When we have to change a grid based mostly on the variety of objects, this isn’t attainable with the present CSS. In CSS grid, we will use the minmax() operate to have a dynamic grid that modifications based mostly on the accessible area.

Right here is my tackle CSS grid minmax():

.listing {
    show: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    hole: 1rem;
}

The consequence would possibly seem like this:

This isn’t excellent. We don’t have a lot management, as we have to tweak the worth of 150px within the minmax(). It could work nice when having 4 objects or much less, and break for five objects or extra.

The answer? We are able to test with CSS :has if there are greater than 5 objects or extra, and alter the minmax() worth based mostly on that.

/* default grid */
.listing {
    --item-size: 200px;
    show: grid;
    grid-template-columns: repeat(auto-fit, minmax(var(--item-size), 1fr));
    hole: 1rem;
}

/* If the grid has 5+ objects, change the --item-size width to 150px */
.listing:has(li:nth-last-child(n + 5)) {
    --item-size: 150px;
}

I solely modified the --item-size variable to make the code simpler to learn and to keep away from duplication.

See the next video and spot how the grid columns change as I add or take away objects.

Isn’t that highly effective?

Within the following determine, we’ve a header that ought to change its structure when the navigation objects are 4 or extra. With CSS :has and :nth-last-child, we will detect that and alter the structure.

.site-header:has(li:nth-last-child(n + 4)) {
    .site-header__wrapper > * {
        flex: preliminary;
    }

    .site-header__start {
        order: 2;
    }

    .site-header__middle {
        order: -1;
        text-align: begin;
    }

    .site-header__end {
        margin-left: auto;
    }
}

The above is the Sass code. It’d look a bit an excessive amount of when written in vanilla CSS.

.site-header:has(li:nth-last-child(n + 4)) .site-header__wrapper > * {
    flex: preliminary;
}

.site-header:has(li:nth-last-child(n + 4)) .site-header__start {
    order: 2;
}

.site-header:has(li:nth-last-child(n + 4)) .site-header__middle {
    order: -1;
    text-align: begin;
}

.site-header:has(li:nth-last-child(n + 4)) .site-header__end {
    margin-left: auto;
}

Can we do higher? Sure! However this isn’t supported nicely (but!). We are able to add a boolean CSS variable that will likely be toggled when the header has 4 objects or extra, after which use a fashion question to alter the header.

.site-header:has(li:nth-last-child(n + 4)) {
    --layout-2: true;
}

With that, we set the variable --layout-2 when the navigation objects are 4 or extra.

/* This can solely works if the --layout-2 CSS variable is ready */
@container fashion(--layout-2: true) {
  .site-header__wrapper {
    > * {
      flex: preliminary;
    }
  }

  .site-header__start {
    order: 2;
  }

  .site-header__middle {
    order: -1;
    text-align: begin;
  }

  .site-header__end {
    margin-left: auto;
  }
}

For me, this seems to be clear and a lot better than nesting all CSS kinds throughout the :has selector.

Demo

Dynamic information part

The next is a information part design that ought to change its structure when the variety of objects is 3 or extra.

By combining CSS :has and :nth-last-child, we will create a toggle CSS variable that will likely be checked by a mode question.

First, I’ll assume that the default card fashion is the horizontal one.

<div class="structure">
    <article class="card"></article>
    <article class="card"></article>
    <article class="card"></article>
</div>
.structure {
  show: grid;
  grid-gap: 1rem;
}

.card {
    show: flex;
    hole: 1rem;
    align-items: heart;
}

After that, I have to test the variety of .card components.

.structure:has(.card:nth-last-child(n + 4)) {
  --layout-4: true;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}

Now, we’ve the CSS variable --layout-4 that will likely be toggled solely when we’ve 4 objects or extra. We are able to test that with a mode question and replace the .card fashion accordingly.

@container fashion(--layout-4: true) {
    .card {
        flex-direction: column;
    }

    .card__thumb {
        flex: 1;
        width: 100%;
        aspect-ratio: 4 / 3;
    }
}

Demo

In a design system, we would have to dynamically management the alignment of the modal actions based mostly on what number of actions we’ve.

Contemplate the next determine:

For instance, if we’ve one motion, it ought to be centered. In any other case, right-align them.

Right here is the CSS:

.modal__footer {
    show: flex;
    justify-content: heart;
    hole: 0.5rem;
}

/* If there are 2 buttons or extra */
.modal__footer:has(a:nth-last-child(n + 2)) {
    justify-content: flex-end;
}

Easy, isn’t it? Here’s a demo in motion.

Demo

Person avatars

On editorial web sites, an article is likely to be written by a number of authors. A typical sample is to stack the creator photographs with damaging spacing when we’ve a number of authors.

Through the use of amount queries alone, we will obtain the minimal, which is to:

  • Add damaging spacing (stack the avatars on high of one another).
  • Shrink the avatar measurement when there are a number of ones.
img:nth-last-child(n+2) ~ img {
    border: 2px stable #fff;
    margin-left: -0.25rem;
    width: 30px;
    peak: 30px;
}

The above works, nevertheless it’s limiting. What if we wish to fashion the container itself? Nicely, that’s the place CSS :has turns into highly effective.

First, we have to test and toggle a CSS variable:

.post-author:has(img:nth-last-child(n + 2)) {
    --multiple-avatars: true;
}

If that CSS variable is true, we then apply the kinds for a number of avatars:

@container fashion(--multiple-avatars: true) {
    .avatars-list {
        show: flex;
        background-color: #efefef;
        padding: 8px 12px;
        border-radius: 50px;
    }

    img:not(:first-child) {
        border: stable 2px #fff;
        margin-left: -0.25rem;
    }
}

Take a look at the next video:

Demo

Timeline

One other attention-grabbing instance the place conditional CSS works nicely is a timeline part.

On this instance, I would like the timeline to modify from a vertical itemizing to an alternating fashion when it has 4 or extra objects.

First, I used the :nth-last-child with CSS :has:

.timeline-wrapper:has(.timeline__item:nth-last-child(n + 4)) {
    --alternating: true;
}

If the above is met, the next CSS will likely be utilized:

@container fashion(--alternating: true) {
    /* Alternating timeline kinds. */
}

What’s helpful about utilizing fashion queries right here is that we will reuse that styling on one other web page. It doesn’t must be a conditional CSS.

I’d do one thing like this:

.timeline-wrapper--page-10 {
    --alternating: true;
}

Please don’t thoughts .timeline-wrapper--page-10, that is an intentional random class title. The CSS variable will be assigned wherever we wish, and the CSS will work out of the field.

Write it as soon as, and it really works for a lot of instances.

Notice: this demo breaks in Chrome Canary and I suppose the reason being that I’m utilizing pseudo-elements inside fashion queries. I’m investigating that in additional element and can replace the article as I received extra info.

Demo

Grid of logos

One of many tough issues to cope with in CSS is aligning a number of logos and ensuring all of them look good. With conditional CSS, we will detect the variety of logos and shrink their measurement a bit.

ul:has(li:nth-last-child(n + 8)) img {
    max-width: 160px;
    peak: 35px;
}

Demo

Outro

This was one of many attention-grabbing articles that I labored on. Combining trendy CSS options can let to thrilling new methods to construct layouts, and this text’s examples had been no exception.

Altering a mode based mostly on the variety of objects may not be a one-off utilization; it may be extracted to totally different use instances. Through the use of fashion queries, we will write as soon as and reuse them in all places.

Additional sources

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments