Custom CSS Selectors

on
  • CSS

With cssnext it is possible to use a maybe future standard feature of CSS called "Custom CSS Selector".

Using custom CSS selectors, repeated selectors can be aliased to a shorter pseudo-selector.

Example:

/* Define custom selector, note that they must be prefixed with :-- */
@custom-selector :--headings h1, h2, h3, h4, h5, h6;

/* Use custom selector */
:--headings {
  color: blue;
}

The custom selectors basically transform into :matches() selectors, most modern browser support some kind of :matches() (:-moz-any() and :-webkit-any()). :--heading would be equivalent to the :matches(h1, h2, h3, h4, h5, h6) pseudo-selector. But cssnext compiles them into simple selectors, supported by all browsers.

Styling checkboxes using custom CSS selectors

As a more complicated example, I'm going to show you how one might implement custom checkboxes or radios.

Implement custom checkboxes and radios for browsers which support :checked can be much more readable with custom selectors.

/*
Select radio and checkbox as :--checkbox, select both types as we will
customize both but style the radio a bit different later.
*/
@custom-selector :--checkbox input[type=checkbox], input[type=radio];

/* Only select radios which are customizable */
@custom-selector :--customizableRadio input[type=radio]:not(:checked), input[type=radio]:checked;

/* Checkboxes and radios are only customizable if `:checked` is supported */
@custom-selector :--customizable input[type=checkbox]:not(:checked), input[type=checkbox]:checked,
                                 input[type=radio]:not(:checked),    input[type=radio]:checked;

/*
Hide the radios and checkboxes by moving them out of the viewport,
some browsers behave weird if they are set to `display: none`
*/
:--customizable {
  position: absolute;
  left: -99999px;
}

:--customizable + label {
  position: relative;
  padding-left: 1.5em;
  cursor: pointer;
}

:--customizable + label::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  width: 16px;
  height: 16px;
  border: 1px solid rgb(200, 200, 200);
  background-color: rgb(255, 255, 255);
  border-radius: 4px;
  transition: border-color 0.15s, box-shadow 0.15s;
}

/* Make radios round */
:--customizableRadio + label::before {
  border-radius: 50%;
  top: -2px;
  left: 6px;
}

:--customizable + label::after {
  content: '\2713';
  position: absolute;
  top: -1px;
  left: 4px;
  font-size: 16px;
  font-weight: 700;
  line-height: 1.4;
  color: green;
  transition: opacity 0.2s;
}

/* Use bullets for radios instead of the check mark */
:--customizableRadio + label::after {
  content: '\2022';
}

:--checkbox:not(:checked) + label::after {
  opacity: 0;
}

:--checkbox:checked + label::after {
  opacity: 1;
}

:--customizable:disabled + label::before {
  border-color: #bbb;
  background-color: #ddd;
}

:--checkbox:disabled:checked + label::after {
  color: #999;
}

:--checkbox:disabled + label {
  color: #aaa;
}

:--customizable:focus + label::before {
  border-color: color(green tint(20%));
  outline: 0;
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 8px color(green a(60%));
}

:--customizable + label:hover::before {
  border-color: green;
}

Which can now be used with the following HTML markup:

<div>
  <input  id="checkbox1" type="checkbox">
  <label for="checkbox1">Checkme</label>
</div>

<div>
  <input  id="radio1" type="radio" name="radio" checked>
  <label for="radio1">Checkme</label>

  <input  id="radio2" type="radio" name="radio">
  <label for="radio2">Checkme</label>
</div>

Checkout the generated CSS file to see the difference to the CSS above.

Happy styling :)