← jordanlittle.com
The web sure moves fast and, since this was written in 2016, a lot of this information may be outdated. Consider this when reading.

9 Underutilized Features in CSS

CSS has been around for a long time — since 1994 depending on who you ask — and has become the de facto standard for styling websites. New specs (like the new CSS4 modules) keep adding cool functionality with animations, transforms, box-shadows, and a smattering of new units and selectors. Awesome stuff, but those aren’t the properties we’ll be discussing today.

With all the hotness in the world of CSS, sometimes it’s tempting to dive into the newer specs for ideas on how to implement a design, but there are many older, often-overlooked features in former CSS specs which offer some very handy functionality. Let’s dig in.

1. The CSS calc() function


Calc() is probably the most commonly used on this list, but it is so worth mentioning. If you’ve never used it, just think of it as saying, “I want this element to be this wide, minus this many pixels.”

.box {
  width: calc( 100% - 20px );

This may look like something that should be in a preprocesser like SASS, but this is completely native and uses the browser’s rendered size of the element before doing the math.

The .box element above will be “100% of the width it would’ve been, minus 20px.” This can be used for many purposes such as putting fixed-width elements alongside those of variable widths.

2. The pointer media query


While not supported as well as I’d like (~70%), the pointer media query can be extremely helpful. Most of the time, media queries are based on a simple metric: the width of the browser window. But there are many instances when we’d rather ask the browser, “is this person using their finger or a mouse?” so we can adjust our button sizes accordingly.

@media( pointer: coarse) { }

none The primary input mechanism of the device does not include a pointing device.

coarse The primary input mechanism of the device includes a pointing device of limited accuracy. Examples include touchscreens and motion-detection sensors (like the Kinect peripheral for the Xbox.)

fine The primary input mechanism of the device includes an accurate pointing device. Examples include mice, touchpads, and drawing styluses.

Support isn't great, but is getting better. Webkit, Blink, and Edge are a sure-thing, but Gecko/Firefox is a no-go.

3. The currentColor variable


Introduced with CSS3, the currentColor variable is native to CSS and represents, easily enough, the element’s current “color” value.

.card {
    color: #333333;
.card--error {
    color: #ff0000;
    .card__guts {
        border-top-color: currentColor; // see note below [1]

[1] Michael Wong makes a great point to make sure you’re using this variable in places where the color doesn’t already cascade. While trying to be brief, I neglected to notice my example uses border-top-color, which would already have the color value from its parent. Using it on properties like background would work, though.

4. The :valid, :invalid, and :empty pseudoclasses

W3C(2) • CanIuse

The :valid and :invalid pseudo classes are used to style validated form inputs. Most new input types have the ability to be “valid” and “invalid” based on their type. For example, an input of type “email” with an invalid email address will trigger the :invalid pseudo class — all natively in the browser.

input:valid { color: green; }
input:invalid { color: red; }

The :empty pseudo class is applied to elements with no content. So instead of wrapping <h1>{name}</h1> with conditional logic in the template, you could simply leave it as is and hide it if there’s no value.

h1:empty { display: none; }

5. Counters using only CSS


Need an ordered list, but don’t (or can’t) use an <ol> element? Don’t want to use javascript either? No worries, plain old CSS has you covered with counters.

.shelf { counter-reset: books; }
.shelf__book { counter-increment: books; }
    .shelf__book::before {
        content: "Book " counter(books) " of 10.";

Okay, so sometimes CSS looks ridiculous. This is one of those times. No symbol needed for concatenation — just throw a space in there.

6. Predictable tables with fixed layout


Tables, by default, pay a lot of attention to the contents of their table cells in deciding how wide to be displayed. Adding table-layout:fixed forces tables to behave in a more manageable way.

.grid {
    table-layout: fixed;

7. Easy form states using adjacent sibling selectors


The adjacent sibling selector is very basic CSS. It’s a part of every beginner tutorial out there, but it’s not used nearly enough. By leveraging this selector, it’s simple to make stateful buttons or form validation messages that appear when needed without javascript.

[type="checkbox"] + label {
    [type="checkbox"]:checked + label {
[required]:valid + span { color: green; }
[required]:invalid + span { color: red; }

8. Algebraic nth-child() selection


Nth-child selectors are great for removing the border from the last element in a list or striping rows in a table, but that is only the tip of the iceberg for this very useful selector function.

Using some basic algebra, you can grab “every fourth element, starting with the first.”

.book:nth-child(4n+1) { color: red; }

or “every third element, starting with the second.”

.book:nth-child(3n-1) { color: blue; }

See nth-test.com or How nth-child works on CSS-Tricks for more info.

9. Animating elements with animation-fill-mode


Often, when working with animations, an element will need to “remember” its styles at the end of an animation and not reset to its default state, pre-animation. When you want your animation to run and leave its completed state styles attached to the target element, then use animation-fill-mode.

@keyframes slideIn {
    0% { transform: translateX(-100%); }
    100% { transform: translateX(0); }

.slideIn {
    animation-name: slideIn;
    animation-duration: .25s;
    animation-fill-mode: forwards;

As browser teams keep marching towards an “application-like web,” our tools keep getting more native-feeling, complex, and interesting. There are new features being announced every day and some amazing functionality always on the horizon.

But even with all the newness, consider taking a look at what’s already available — you might find exactly what you need.