2
\$\begingroup\$

I have this menu in jQuery that works perfectly, but I think it would be better if, instead of using class="btn-accordion" for each li, I just had a single class="accordion-menu".

Is that possible or is the code OK?

my html:

<div class="accordion-menu">
    <ul class="" role="tabpanel">
      <li class="btn-accordion is-active">
        <a class="menu-toggle" data-toggle="tooltip" data-placement="bottom" title="home">
          <i class="fa fa-bars fa-2x" aria-hidden="true"></i>
        </a>
      </li>
      <li class="btn-accordion"><a href=""><i class="fa fa-search fa-2x" aria-hidden="true"></i></a></li>
      <li class="btn-accordion"><a href=""><i class="fa fa-info-circle fa-2x" aria-hidden="true"></i></a></li>
      <li class="btn-accordion"><a class="filter-toggle"><i class="fa fa-filter fa-2x" aria-hidden="true"></i></a></li>
    </ul>
  </div>

jquery

  $('.btn-accordion').on('click', function() { 
        $(this).closest('li').toggleClass('is-active');
  });

scss:

.btn-accordion {
  color: red;
  background-color: blue;
  .fa {
    color: white;
  }
  &.is-active {
    color: blue;
    background-color: red;
    .fa {
      color: yellow;
    }
  }
}
\$\endgroup\$
2
\$\begingroup\$

This is ok since it's valid CSS anyways. However...

The problem with both approaches (.btn-accordion.is-active and .accordion-menu .is-active) is that they're least 0020 in specificity. Should you ever want to override, the override must at least have a specificity of 0020.

.btn-accordion {
  color: red;
}
.btn-accordion.is-active {
  color: blue;
}

/* overriding selector needs to be at least the same specificity */
.btn-accordion.is-active {
  color: green;
}
<div class="accordion-menu">
  <ul class="" role="tabpanel">
    <li class="btn-accordion is-active">
      <a class="menu-toggle" data-toggle="tooltip" data-placement="bottom" title="home">
        <i class="fa fa-bars fa-2x" aria-hidden="true"></i>
      </a>
    </li>
    <li class="btn-accordion"><a href=""><i class="fa fa-search fa-2x" aria-hidden="true"></i></a>
    </li>
    <li class="btn-accordion"><a href=""><i class="fa fa-info-circle fa-2x" aria-hidden="true"></i></a>
    </li>
    <li class="btn-accordion"><a class="filter-toggle"><i class="fa fa-filter fa-2x" aria-hidden="true"></i></a>
    </li>
  </ul>
</div>

In the case where you can't control order of the styles, you can raise the specificity. However, this would equate to longer selectors and/or harder-to-override selectors.

.btn-accordion {
  color: red;
}

/* An override appearing earlier in the styles */
/* Specificity: 0021 */
li.btn-accordion.is-active {
  color: blue;
}

/* Specificity: 0020 */
.btn-accordion.is-active {
  color: green;
}
<div class="accordion-menu">
  <ul class="" role="tabpanel">
    <li class="btn-accordion is-active">
      <a class="menu-toggle" data-toggle="tooltip" data-placement="bottom" title="home">
        <i class="fa fa-bars fa-2x" aria-hidden="true"></i>
      </a>
    </li>
    <li class="btn-accordion"><a href=""><i class="fa fa-search fa-2x" aria-hidden="true"></i></a>
    </li>
    <li class="btn-accordion"><a href=""><i class="fa fa-info-circle fa-2x" aria-hidden="true"></i></a>
    </li>
    <li class="btn-accordion"><a class="filter-toggle"><i class="fa fa-filter fa-2x" aria-hidden="true"></i></a>
    </li>
  </ul>
</div>

CSS tightly coupled to the HTML structure are fragile. Accidentally removing/renaming the class name, the selector becomes useless. If the HTML structure changes, the selectors also become useless.

Long chains of selectors are also hard to maintain. You do not know how deep a selector is relative to its parent/descendant selector. It's also hard to figure out if a combination of selectors is even used on the page, leading to excess fat on the CSS.

One way to avoid specificity climb is to use BEM naming scheme. This allows you to write selectors with just one class while being able to specify the containing block, block contents and modifiers.

With the following code, it allows me to know the accordion-menu__button belongs to accordion-menu set of elements and that it has a accordion-menu__button--is-active state. Since the selectors are just 1 class name, modifier selectors can predictably be placed after the selectors they modify. It also allows me to easily deduce that a style is applied since it doesn't depend on the HTML nesting structure.

/* Default state of the button */
.accordion-menu__button{
  color: red;
}

/* Override the color when the button is active */
.accordion-menu__button--is-active{
  color: green;
}
<div class="accordion-menu">
  <ul class="" role="tabpanel">
    <li class="accordion-menu__button accordion-menu__button--is-active">
      <a class="menu-toggle" data-toggle="tooltip" data-placement="bottom" title="home">
        <i class="fa fa-bars fa-2x" aria-hidden="true"></i>
      </a>
    </li>
    <li class="accordion-menu__button"><a href=""><i class="fa fa-search fa-2x" aria-hidden="true"></i></a>
    </li>
    <li class="accordion-menu__button"><a href=""><i class="fa fa-info-circle fa-2x" aria-hidden="true"></i></a>
    </li>
    <li class="accordion-menu__button"><a class="filter-toggle"><i class="fa fa-filter fa-2x" aria-hidden="true"></i></a>
    </li>
  </ul>
</div>

However, this would mean that every target element will have a class. But in most cases, especially in large apps, you will have templating mechanisms that will do all this repetitiveness for you and the tradeoff of writing this way vs manageability of the code is justifiable.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.